summaryrefslogtreecommitdiff
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/Kconfig75
-rw-r--r--drivers/iio/Makefile24
-rw-r--r--drivers/iio/accel/Kconfig55
-rw-r--r--drivers/iio/accel/Makefile14
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c404
-rw-r--r--drivers/iio/accel/kxsd9.c288
-rw-r--r--drivers/iio/accel/st_accel.h47
-rw-r--r--drivers/iio/accel/st_accel_buffer.c114
-rw-r--r--drivers/iio/accel/st_accel_core.c500
-rw-r--r--drivers/iio/accel/st_accel_i2c.c86
-rw-r--r--drivers/iio/accel/st_accel_spi.c85
-rw-r--r--drivers/iio/adc/Kconfig160
-rw-r--r--drivers/iio/adc/Makefile19
-rw-r--r--drivers/iio/adc/ad7266.c539
-rw-r--r--drivers/iio/adc/ad7298.c408
-rw-r--r--drivers/iio/adc/ad7476.c332
-rw-r--r--drivers/iio/adc/ad7791.c460
-rw-r--r--drivers/iio/adc/ad7793.c876
-rw-r--r--drivers/iio/adc/ad7887.c390
-rw-r--r--drivers/iio/adc/ad7923.c383
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c558
-rw-r--r--drivers/iio/adc/at91_adc.c796
-rw-r--r--drivers/iio/adc/exynos_adc.c452
-rw-r--r--drivers/iio/adc/lp8788_adc.c258
-rw-r--r--drivers/iio/adc/max1363.c1667
-rw-r--r--drivers/iio/adc/ti-adc081c.c161
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c260
-rw-r--r--drivers/iio/adc/viperboard_adc.c181
-rw-r--r--drivers/iio/amplifiers/Kconfig17
-rw-r--r--drivers/iio/amplifiers/Makefile5
-rw-r--r--drivers/iio/amplifiers/ad8366.c222
-rw-r--r--drivers/iio/buffer_cb.c116
-rw-r--r--drivers/iio/common/Kconfig6
-rw-r--r--drivers/iio/common/Makefile10
-rw-r--r--drivers/iio/common/hid-sensors/Kconfig37
-rw-r--r--drivers/iio/common/hid-sensors/Makefile7
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c249
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c100
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.h26
-rw-r--r--drivers/iio/common/st_sensors/Kconfig14
-rw-r--r--drivers/iio/common/st_sensors/Makefile10
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_buffer.c116
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c447
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_i2c.c81
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_spi.c121
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_trigger.c77
-rw-r--r--drivers/iio/dac/Kconfig154
-rw-r--r--drivers/iio/dac/Makefile18
-rw-r--r--drivers/iio/dac/ad5064.c688
-rw-r--r--drivers/iio/dac/ad5360.c565
-rw-r--r--drivers/iio/dac/ad5380.c659
-rw-r--r--drivers/iio/dac/ad5421.c539
-rw-r--r--drivers/iio/dac/ad5446.c623
-rw-r--r--drivers/iio/dac/ad5449.c376
-rw-r--r--drivers/iio/dac/ad5504.c393
-rw-r--r--drivers/iio/dac/ad5624r.h79
-rw-r--r--drivers/iio/dac/ad5624r_spi.c328
-rw-r--r--drivers/iio/dac/ad5686.c417
-rw-r--r--drivers/iio/dac/ad5755.c645
-rw-r--r--drivers/iio/dac/ad5764.c377
-rw-r--r--drivers/iio/dac/ad5791.c490
-rw-r--r--drivers/iio/dac/max517.c242
-rw-r--r--drivers/iio/dac/mcp4725.c227
-rw-r--r--drivers/iio/frequency/Kconfig41
-rw-r--r--drivers/iio/frequency/Makefile6
-rw-r--r--drivers/iio/frequency/ad9523.c1049
-rw-r--r--drivers/iio/frequency/adf4350.c486
-rw-r--r--drivers/iio/gyro/Kconfig81
-rw-r--r--drivers/iio/gyro/Makefile20
-rw-r--r--drivers/iio/gyro/adis16080.c259
-rw-r--r--drivers/iio/gyro/adis16136.c581
-rw-r--r--drivers/iio/gyro/adxrs450.c494
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c404
-rw-r--r--drivers/iio/gyro/itg3200_buffer.c156
-rw-r--r--drivers/iio/gyro/itg3200_core.c398
-rw-r--r--drivers/iio/gyro/st_gyro.h44
-rw-r--r--drivers/iio/gyro/st_gyro_buffer.c114
-rw-r--r--drivers/iio/gyro/st_gyro_core.c367
-rw-r--r--drivers/iio/gyro/st_gyro_i2c.c83
-rw-r--r--drivers/iio/gyro/st_gyro_spi.c82
-rw-r--r--drivers/iio/iio_core.h63
-rw-r--r--drivers/iio/iio_core_trigger.h46
-rw-r--r--drivers/iio/imu/Kconfig5
-rw-r--r--drivers/iio/imu/Makefile6
-rw-r--r--drivers/iio/imu/adis16400.h213
-rw-r--r--drivers/iio/imu/adis16400_core.c971
-rw-r--r--drivers/iio/imu/inv_mpu/Kconfig24
-rw-r--r--drivers/iio/imu/inv_mpu/Makefile27
-rw-r--r--drivers/iio/imu/inv_mpu/README569
-rw-r--r--drivers/iio/imu/inv_mpu/dmpDefaultMPU6050.c323
-rw-r--r--drivers/iio/imu/inv_mpu/dmpKey.h462
-rw-r--r--drivers/iio/imu/inv_mpu/dmpmap.h283
-rw-r--r--drivers/iio/imu/inv_mpu/inv_counters.h72
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu3050_iio.c292
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_core.c2078
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_iio.h861
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_misc.c1874
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_ring.c1048
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_trigger.c92
-rw-r--r--drivers/iio/imu/inv_mpu/inv_slave_bma250.c330
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_core.c798
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c155
-rw-r--r--drivers/iio/industrialio-buffer.c938
-rw-r--r--drivers/iio/industrialio-core.c1046
-rw-r--r--drivers/iio/industrialio-event.c456
-rw-r--r--drivers/iio/industrialio-trigger.c496
-rw-r--r--drivers/iio/industrialio-triggered-buffer.c110
-rw-r--r--drivers/iio/inkern.c580
-rw-r--r--drivers/iio/inv_test/Kconfig11
-rw-r--r--drivers/iio/inv_test/Makefile6
-rw-r--r--drivers/iio/inv_test/inv_counters.c153
-rw-r--r--drivers/iio/inv_test/inv_counters.h72
-rw-r--r--drivers/iio/kfifo_buf.c164
-rw-r--r--drivers/iio/light/Kconfig66
-rw-r--r--drivers/iio/light/Makefile9
-rw-r--r--drivers/iio/light/adjd_s311.c364
-rw-r--r--drivers/iio/light/hid-sensor-als.c370
-rw-r--r--drivers/iio/light/lm3533-als.c931
-rw-r--r--drivers/iio/light/tsl2563.c887
-rw-r--r--drivers/iio/light/vcnl4000.c217
-rw-r--r--drivers/iio/magnetometer/Kconfig5
-rw-r--r--drivers/iio/magnetometer/Makefile6
-rw-r--r--drivers/iio/magnetometer/ak8975.c478
-rw-r--r--drivers/iio/magnetometer/inv_compass/Kconfig25
-rw-r--r--drivers/iio/magnetometer/inv_compass/Makefile25
-rw-r--r--drivers/iio/magnetometer/inv_compass/README176
-rw-r--r--drivers/iio/magnetometer/inv_compass/inv_ami306_core.c570
-rw-r--r--drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h159
-rw-r--r--drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c163
-rw-r--r--drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c90
-rw-r--r--drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c969
-rw-r--r--drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h172
-rw-r--r--drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c165
-rw-r--r--drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c91
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c408
135 files changed, 43708 insertions, 0 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
new file mode 100644
index 00000000..d6c68b5c
--- /dev/null
+++ b/drivers/iio/Kconfig
@@ -0,0 +1,75 @@
+#
+# Industrial I/O subsystem configuration
+#
+
+menuconfig IIO
+ tristate "Industrial I/O support"
+ depends on GENERIC_HARDIRQS
+ help
+ The industrial I/O subsystem provides a unified framework for
+ drivers for many different types of embedded sensors using a
+ number of different physical interfaces (i2c, spi, etc).
+
+if IIO
+
+config IIO_BUFFER
+ bool "Enable buffer support within IIO"
+ help
+ Provide core support for various buffer based data
+ acquisition methods.
+
+if IIO_BUFFER
+
+config IIO_BUFFER_CB
+boolean "IIO callback buffer used for push in-kernel interfaces"
+ help
+ Should be selected by any drivers that do-inkernel push
+ usage. That is, those where the data is pushed to the consumer.
+
+config IIO_KFIFO_BUF
+ select IIO_TRIGGER
+ tristate "Industrial I/O buffering based on kfifo"
+ help
+ A simple fifo based on kfifo. Use this if you want a fifo
+ rather than a ring buffer. Note that this currently provides
+ no buffer events so it is up to userspace to work out how
+ often to read from the buffer.
+
+config IIO_TRIGGERED_BUFFER
+ tristate
+ select IIO_TRIGGER
+ select IIO_KFIFO_BUF
+ help
+ Provides helper functions for setting up triggered buffers.
+
+endif # IIO_BUFFER
+
+config IIO_TRIGGER
+ boolean "Enable triggered sampling support"
+ help
+ Provides IIO core support for triggers. Currently these
+ are used to initialize capture of samples to push into
+ ring buffers. The triggers are effectively a 'capture
+ data now' interrupt.
+
+config IIO_CONSUMERS_PER_TRIGGER
+ int "Maximum number of consumers per trigger"
+ depends on IIO_TRIGGER
+ default "2"
+ help
+ This value controls the maximum number of consumers that a
+ given trigger may handle. Default is 2.
+
+source "drivers/iio/accel/Kconfig"
+source "drivers/iio/adc/Kconfig"
+source "drivers/iio/amplifiers/Kconfig"
+source "drivers/iio/common/Kconfig"
+source "drivers/iio/dac/Kconfig"
+source "drivers/iio/frequency/Kconfig"
+source "drivers/iio/gyro/Kconfig"
+source "drivers/iio/imu/inv_mpu/Kconfig"
+source "drivers/iio/inv_test/Kconfig"
+source "drivers/iio/light/Kconfig"
+source "drivers/iio/magnetometer/inv_compass/Kconfig"
+
+endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
new file mode 100644
index 00000000..e5ad4e3e
--- /dev/null
+++ b/drivers/iio/Makefile
@@ -0,0 +1,24 @@
+#
+# Makefile for the industrial I/O core.
+#
+
+obj-$(CONFIG_IIO) += industrialio.o
+industrialio-y := industrialio-core.o industrialio-event.o inkern.o
+industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
+industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
+industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
+
+obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
+obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
+
+obj-y += accel/
+obj-y += adc/
+obj-y += amplifiers/
+obj-y += common/
+obj-y += dac/
+obj-y += gyro/
+obj-y += frequency/
+obj-y += imu/
+obj-y += light/
+obj-y += magnetometer/
+obj-y += inv_test/
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
new file mode 100644
index 00000000..bb594963
--- /dev/null
+++ b/drivers/iio/accel/Kconfig
@@ -0,0 +1,55 @@
+#
+# Accelerometer drivers
+#
+menu "Accelerometers"
+
+config HID_SENSOR_ACCEL_3D
+ depends on HID_SENSOR_HUB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select HID_SENSOR_IIO_COMMON
+ select HID_SENSOR_IIO_TRIGGER
+ tristate "HID Accelerometers 3D"
+ help
+ Say yes here to build support for the HID SENSOR
+ accelerometers 3D.
+
+config KXSD9
+ tristate "Kionix KXSD9 Accelerometer Driver"
+ depends on SPI
+ help
+ 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
new file mode 100644
index 00000000..87d8fa26
--- /dev/null
+++ b/drivers/iio/accel/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+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/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
new file mode 100644
index 00000000..bbcbd710
--- /dev/null
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -0,0 +1,404 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Accelerometer-3D: 0x200073*/
+#define DRIVER_NAME "HID-SENSOR-200073"
+
+enum accel_3d_channel {
+ CHANNEL_SCAN_INDEX_X,
+ CHANNEL_SCAN_INDEX_Y,
+ CHANNEL_SCAN_INDEX_Z,
+ ACCEL_3D_CHANNEL_MAX,
+};
+
+struct accel_3d_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_common common_attributes;
+ struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
+ u32 accel_val[ACCEL_3D_CHANNEL_MAX];
+};
+
+static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
+ HID_USAGE_SENSOR_ACCEL_X_AXIS,
+ HID_USAGE_SENSOR_ACCEL_Y_AXIS,
+ HID_USAGE_SENSOR_ACCEL_Z_AXIS
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec accel_3d_channels[] = {
+ {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_HYSTERESIS),
+ .scan_index = CHANNEL_SCAN_INDEX_X,
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_HYSTERESIS),
+ .scan_index = CHANNEL_SCAN_INDEX_Y,
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_HYSTERESIS),
+ .scan_index = CHANNEL_SCAN_INDEX_Z,
+ }
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+ int channel, int size)
+{
+ channels[channel].scan_type.sign = 's';
+ /* Real storage bits will change based on the report desc. */
+ channels[channel].scan_type.realbits = size * 8;
+ /* Maximum size of a sample to capture is u32 */
+ channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int accel_3d_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct accel_3d_state *accel_state = iio_priv(indio_dev);
+ int report_id = -1;
+ u32 address;
+ int ret;
+ int ret_type;
+
+ *val = 0;
+ *val2 = 0;
+ switch (mask) {
+ case 0:
+ report_id = accel_state->accel[chan->scan_index].report_id;
+ address = accel_3d_addresses[chan->scan_index];
+ if (report_id >= 0)
+ *val = sensor_hub_input_attr_get_raw_value(
+ accel_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_ACCEL_3D, address,
+ report_id);
+ else {
+ *val = 0;
+ return -EINVAL;
+ }
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = accel_state->accel[CHANNEL_SCAN_INDEX_X].units;
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = hid_sensor_convert_exponent(
+ accel_state->accel[CHANNEL_SCAN_INDEX_X].unit_expo);
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_read_samp_freq_value(
+ &accel_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_read_raw_hyst_value(
+ &accel_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret_type = -EINVAL;
+ break;
+ }
+
+ return ret_type;
+}
+
+/* Channel write_raw handler */
+static int accel_3d_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct accel_3d_state *accel_state = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_write_samp_freq_value(
+ &accel_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_write_raw_hyst_value(
+ &accel_state->common_attributes, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int accel_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info accel_3d_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &accel_3d_read_raw,
+ .write_raw = &accel_3d_write_raw,
+ .write_raw_get_fmt = &accel_3d_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ iio_push_to_buffers(indio_dev, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct accel_3d_state *accel_state = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "accel_3d_proc_event [%d]\n",
+ accel_state->common_attributes.data_ready);
+ if (accel_state->common_attributes.data_ready)
+ hid_sensor_push_data(indio_dev,
+ (u8 *)accel_state->accel_val,
+ sizeof(accel_state->accel_val));
+
+ return 0;
+}
+
+/* Capture samples in local storage */
+static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct accel_3d_state *accel_state = iio_priv(indio_dev);
+ int offset;
+ int ret = -EINVAL;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_ACCEL_X_AXIS:
+ case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
+ case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
+ offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
+ accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] =
+ *(u32 *)raw_data;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int accel_3d_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ unsigned usage_id,
+ struct accel_3d_state *st)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
+ ret = sensor_hub_input_get_attribute_info(hsdev,
+ HID_INPUT_REPORT,
+ usage_id,
+ HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
+ &st->accel[CHANNEL_SCAN_INDEX_X + i]);
+ if (ret < 0)
+ break;
+ accel_3d_adjust_channel_bit_mask(channels,
+ CHANNEL_SCAN_INDEX_X + i,
+ st->accel[CHANNEL_SCAN_INDEX_X + i].size);
+ }
+ dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
+ st->accel[0].index,
+ st->accel[0].report_id,
+ st->accel[1].index, st->accel[1].report_id,
+ st->accel[2].index, st->accel[2].report_id);
+
+ return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int hid_accel_3d_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ static const char *name = "accel_3d";
+ struct iio_dev *indio_dev;
+ struct accel_3d_state *accel_state;
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_chan_spec *channels;
+
+ indio_dev = iio_device_alloc(sizeof(struct accel_3d_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+
+ accel_state = iio_priv(indio_dev);
+ accel_state->common_attributes.hsdev = hsdev;
+ accel_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev,
+ HID_USAGE_SENSOR_ACCEL_3D,
+ &accel_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes\n");
+ goto error_free_dev;
+ }
+
+ channels = kmemdup(accel_3d_channels, sizeof(accel_3d_channels),
+ GFP_KERNEL);
+ if (!channels) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "failed to duplicate channels\n");
+ goto error_free_dev;
+ }
+
+ ret = accel_3d_parse_report(pdev, hsdev, channels,
+ HID_USAGE_SENSOR_ACCEL_3D, accel_state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup attributes\n");
+ goto error_free_dev_mem;
+ }
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &accel_3d_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ NULL, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+ goto error_free_dev_mem;
+ }
+ accel_state->common_attributes.data_ready = false;
+ ret = hid_sensor_setup_trigger(indio_dev, name,
+ &accel_state->common_attributes);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "trigger setup failed\n");
+ goto error_unreg_buffer_funcs;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_trigger;
+ }
+
+ accel_state->callbacks.send_event = accel_3d_proc_event;
+ accel_state->callbacks.capture_sample = accel_3d_capture_sample;
+ accel_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D,
+ &accel_state->callbacks);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "callback reg failed\n");
+ goto error_iio_unreg;
+ }
+
+ return ret;
+
+error_iio_unreg:
+ iio_device_unregister(indio_dev);
+error_remove_trigger:
+ hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+ kfree(indio_dev->channels);
+error_free_dev:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int hid_accel_3d_remove(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);
+ iio_device_unregister(indio_dev);
+ hid_sensor_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ kfree(indio_dev->channels);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver hid_accel_3d_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = hid_accel_3d_probe,
+ .remove = hid_accel_3d_remove,
+};
+module_platform_driver(hid_accel_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Accel 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
new file mode 100644
index 00000000..7c9a1d97
--- /dev/null
+++ b/drivers/iio/accel/kxsd9.c
@@ -0,0 +1,288 @@
+/*
+ * kxsd9.c simple support for the Kionix KXSD9 3D
+ * accelerometer.
+ *
+ * Copyright (c) 2008-2009 Jonathan Cameron <jic23@kernel.org>
+ *
+ * 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.
+ *
+ * The i2c interface is very similar, so shouldn't be a problem once
+ * I have a suitable wire made up.
+ *
+ * TODO: Support the motion detector
+ * Uses register address incrementing so could have a
+ * heavily optimized ring buffer access function.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define KXSD9_REG_X 0x00
+#define KXSD9_REG_Y 0x02
+#define KXSD9_REG_Z 0x04
+#define KXSD9_REG_AUX 0x06
+#define KXSD9_REG_RESET 0x0a
+#define KXSD9_REG_CTRL_C 0x0c
+
+#define KXSD9_FS_MASK 0x03
+
+#define KXSD9_REG_CTRL_B 0x0d
+#define KXSD9_REG_CTRL_A 0x0e
+
+#define KXSD9_READ(a) (0x80 | (a))
+#define KXSD9_WRITE(a) (a)
+
+#define KXSD9_STATE_RX_SIZE 2
+#define KXSD9_STATE_TX_SIZE 2
+/**
+ * struct kxsd9_state - device related storage
+ * @buf_lock: protect the rx and tx buffers.
+ * @us: spi device
+ * @rx: single rx buffer storage
+ * @tx: single tx buffer storage
+ **/
+struct kxsd9_state {
+ struct mutex buf_lock;
+ struct spi_device *us;
+ u8 rx[KXSD9_STATE_RX_SIZE] ____cacheline_aligned;
+ u8 tx[KXSD9_STATE_TX_SIZE];
+};
+
+#define KXSD9_SCALE_2G "0.011978"
+#define KXSD9_SCALE_4G "0.023927"
+#define KXSD9_SCALE_6G "0.035934"
+#define KXSD9_SCALE_8G "0.047853"
+
+/* reverse order */
+static const int kxsd9_micro_scales[4] = { 47853, 35934, 23927, 11978 };
+
+static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
+{
+ int ret, i;
+ struct kxsd9_state *st = iio_priv(indio_dev);
+ bool foundit = false;
+
+ for (i = 0; i < 4; i++)
+ if (micro == kxsd9_micro_scales[i]) {
+ foundit = true;
+ break;
+ }
+ if (!foundit)
+ return -EINVAL;
+
+ mutex_lock(&st->buf_lock);
+ ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
+ if (ret)
+ goto error_ret;
+ st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C);
+ st->tx[1] = (ret & ~KXSD9_FS_MASK) | i;
+
+ ret = spi_write(st->us, st->tx, 2);
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int kxsd9_read(struct iio_dev *indio_dev, u8 address)
+{
+ int ret;
+ struct kxsd9_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = {
+ {
+ .bits_per_word = 8,
+ .len = 1,
+ .delay_usecs = 200,
+ .tx_buf = st->tx,
+ }, {
+ .bits_per_word = 8,
+ .len = 2,
+ .rx_buf = st->rx,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = KXSD9_READ(address);
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ if (!ret)
+ ret = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static IIO_CONST_ATTR(accel_scale_available,
+ KXSD9_SCALE_2G " "
+ KXSD9_SCALE_4G " "
+ KXSD9_SCALE_6G " "
+ KXSD9_SCALE_8G);
+
+static struct attribute *kxsd9_attributes[] = {
+ &iio_const_attr_accel_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static int kxsd9_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ int ret = -EINVAL;
+
+ if (mask == IIO_CHAN_INFO_SCALE) {
+ /* Check no integer component */
+ if (val)
+ return -EINVAL;
+ ret = kxsd9_write_scale(indio_dev, val2);
+ }
+
+ return ret;
+}
+
+static int kxsd9_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret = -EINVAL;
+ struct kxsd9_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = kxsd9_read(indio_dev, chan->address);
+ if (ret < 0)
+ goto error_ret;
+ *val = ret;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
+ if (ret)
+ goto error_ret;
+ *val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ }
+
+error_ret:
+ return ret;
+};
+#define KXSD9_ACCEL_CHAN(axis) \
+ { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = KXSD9_REG_##axis, \
+ }
+
+static const struct iio_chan_spec kxsd9_channels[] = {
+ KXSD9_ACCEL_CHAN(X), KXSD9_ACCEL_CHAN(Y), KXSD9_ACCEL_CHAN(Z),
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .indexed = 1,
+ .address = KXSD9_REG_AUX,
+ }
+};
+
+static const struct attribute_group kxsd9_attribute_group = {
+ .attrs = kxsd9_attributes,
+};
+
+static int kxsd9_power_up(struct kxsd9_state *st)
+{
+ int ret;
+
+ st->tx[0] = 0x0d;
+ st->tx[1] = 0x40;
+ ret = spi_write(st->us, st->tx, 2);
+ if (ret)
+ return ret;
+
+ st->tx[0] = 0x0c;
+ st->tx[1] = 0x9b;
+ return spi_write(st->us, st->tx, 2);
+};
+
+static const struct iio_info kxsd9_info = {
+ .read_raw = &kxsd9_read_raw,
+ .write_raw = &kxsd9_write_raw,
+ .attrs = &kxsd9_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int kxsd9_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct kxsd9_state *st;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->us = spi;
+ mutex_init(&st->buf_lock);
+ indio_dev->channels = kxsd9_channels;
+ indio_dev->num_channels = ARRAY_SIZE(kxsd9_channels);
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &kxsd9_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ spi->mode = SPI_MODE_0;
+ spi_setup(spi);
+ kxsd9_power_up(st);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_dev;
+
+ return 0;
+
+error_free_dev:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+static int kxsd9_remove(struct spi_device *spi)
+{
+ iio_device_unregister(spi_get_drvdata(spi));
+ iio_device_free(spi_get_drvdata(spi));
+
+ return 0;
+}
+
+static const struct spi_device_id kxsd9_id[] = {
+ {"kxsd9", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(spi, kxsd9_id);
+
+static struct spi_driver kxsd9_driver = {
+ .driver = {
+ .name = "kxsd9",
+ .owner = THIS_MODULE,
+ },
+ .probe = kxsd9_probe,
+ .remove = kxsd9_remove,
+ .id_table = kxsd9_id,
+};
+module_spi_driver(kxsd9_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
new file mode 100644
index 00000000..37949b94
--- /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 00000000..d9b35075
--- /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 = iio_trigger_get_drvdata(trig);
+
+ 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 00000000..e0f5a3ce
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,500 @@
+/*
+ * 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.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,
+};
+
+#ifdef CONFIG_IIO_TRIGGER
+static const struct iio_trigger_ops st_accel_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = ST_ACCEL_TRIGGER_SET_STATE,
+};
+#define ST_ACCEL_TRIGGER_OPS (&st_accel_trigger_ops)
+#else
+#define ST_ACCEL_TRIGGER_OPS NULL
+#endif
+
+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 00000000..ffc9d097
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.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/i2c.h>
+#include <linux/iio/iio.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 00000000..22b35bfe
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,85 @@
+/*
+ * 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/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/Kconfig b/drivers/iio/adc/Kconfig
new file mode 100644
index 00000000..ab0767e6
--- /dev/null
+++ b/drivers/iio/adc/Kconfig
@@ -0,0 +1,160 @@
+#
+# ADC drivers
+#
+menu "Analog to digital converters"
+
+config AD_SIGMA_DELTA
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config AD7266
+ tristate "Analog Devices AD7265/AD7266 ADC driver"
+ depends on SPI_MASTER
+ select IIO_BUFFER
+ select IIO_TRIGGER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7265 and AD7266
+ ADCs.
+
+config AD7298
+ tristate "Analog Devices AD7298 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7298
+ 8 Channel ADC with temperature sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7298.
+
+config AD7923
+ tristate "Analog Devices AD7923 and similar ADCs driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices
+ AD7904, AD7914, AD7923, AD7924 4 Channel ADCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7923.
+
+config AD7791
+ tristate "Analog Devices AD7791 ADC driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7787, AD7788, AD7789,
+ AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say
+ N (but it is safe to say "Y").
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7791.
+
+config AD7793
+ tristate "Analog Devices AD7793 and similar ADCs driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7785, AD7792, AD7793,
+ AD7794 and AD7795 SPI analog to digital converters (ADC).
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called AD7793.
+
+config AD7476
+ tristate "Analog Devices AD7476 and similar 1-channel ADCs driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
+ AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
+ AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7476.
+
+config AD7887
+ tristate "Analog Devices AD7887 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices
+ AD7887 SPI analog to digital converter (ADC).
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7887.
+
+config AT91_ADC
+ tristate "Atmel AT91 ADC"
+ depends on ARCH_AT91
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select SYSFS
+ help
+ Say yes here to build support for Atmel AT91 ADC.
+
+config EXYNOS_ADC
+ bool "Exynos ADC driver support"
+ depends on OF
+ help
+ Core support for the ADC block found in the Samsung EXYNOS series
+ of SoCs for drivers such as the touchscreen and hwmon to use to share
+ this resource.
+
+config LP8788_ADC
+ bool "LP8788 ADC driver"
+ depends on MFD_LP8788
+ help
+ Say yes here to build support for TI LP8788 ADC.
+
+config MAX1363
+ tristate "Maxim max1363 ADC driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for many Maxim i2c analog to digital
+ converters (ADC). (max1361, max1362, max1363, max1364, max1036,
+ max1037, max1038, max1039, max1136, max1136, max1137, max1138,
+ max1139, max1236, max1237, max11238, max1239, max11600, max11601,
+ max11602, max11603, max11604, max11605, max11606, max11607,
+ max11608, max11609, max11610, max11611, max11612, max11613,
+ max11614, max11615, max11616, max11617, max11644, max11645,
+ max11646, max11647) Provides direct access via sysfs and buffered
+ data via the iio dev interface.
+
+config TI_ADC081C
+ tristate "Texas Instruments ADC081C021/027"
+ depends on I2C
+ help
+ If you say yes here you get support for Texas Instruments ADC081C021
+ and ADC081C027 ADC chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-adc081c.
+
+config TI_AM335X_ADC
+ tristate "TI's ADC driver"
+ depends on MFD_TI_AM335X_TSCADC
+ help
+ Say yes here to build support for Texas Instruments ADC
+ driver which is also a MFD client.
+
+config VIPERBOARD_ADC
+ tristate "Viperboard ADC support"
+ depends on MFD_VIPERBOARD && USB
+ help
+ Say yes here to access the ADC part of the Nano River
+ Technologies Viperboard.
+
+endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
new file mode 100644
index 00000000..0a825bed
--- /dev/null
+++ b/drivers/iio/adc/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for IIO ADC drivers
+#
+
+obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
+obj-$(CONFIG_AD7266) += ad7266.o
+obj-$(CONFIG_AD7298) += ad7298.o
+obj-$(CONFIG_AD7923) += ad7923.o
+obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD7791) += ad7791.o
+obj-$(CONFIG_AD7793) += ad7793.o
+obj-$(CONFIG_AD7887) += ad7887.o
+obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
+obj-$(CONFIG_MAX1363) += max1363.o
+obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
+obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
new file mode 100644
index 00000000..c2744a75
--- /dev/null
+++ b/drivers/iio/adc/ad7266.c
@@ -0,0 +1,539 @@
+/*
+ * AD7266/65 SPI ADC driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+
+#include <linux/interrupt.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/platform_data/ad7266.h>
+
+struct ad7266_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned long vref_uv;
+
+ struct spi_transfer single_xfer[3];
+ struct spi_message single_msg;
+
+ enum ad7266_range range;
+ enum ad7266_mode mode;
+ bool fixed_addr;
+ struct gpio gpios[3];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ * The buffer needs to be large enough to hold two samples (4 bytes) and
+ * the naturally aligned timestamp (8 bytes).
+ */
+ uint8_t data[ALIGN(4, sizeof(s64)) + sizeof(s64)] ____cacheline_aligned;
+};
+
+static int ad7266_wakeup(struct ad7266_state *st)
+{
+ /* Any read with >= 2 bytes will wake the device */
+ return spi_read(st->spi, st->data, 2);
+}
+
+static int ad7266_powerdown(struct ad7266_state *st)
+{
+ /* Any read with < 2 bytes will powerdown the device */
+ return spi_read(st->spi, st->data, 1);
+}
+
+static int ad7266_preenable(struct iio_dev *indio_dev)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad7266_wakeup(st);
+ if (ret)
+ return ret;
+
+ ret = iio_sw_buffer_preenable(indio_dev);
+ if (ret)
+ ad7266_powerdown(st);
+
+ return ret;
+}
+
+static int ad7266_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ return ad7266_powerdown(st);
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .preenable = &ad7266_preenable,
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &ad7266_postdisable,
+};
+
+static irqreturn_t ad7266_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7266_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_read(st->spi, st->data, 4);
+ if (ret == 0) {
+ if (indio_dev->scan_timestamp)
+ ((s64 *)st->data)[1] = pf->timestamp;
+ iio_push_to_buffers(indio_dev, (u8 *)st->data);
+ }
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void ad7266_select_input(struct ad7266_state *st, unsigned int nr)
+{
+ unsigned int i;
+
+ if (st->fixed_addr)
+ return;
+
+ switch (st->mode) {
+ case AD7266_MODE_SINGLE_ENDED:
+ nr >>= 1;
+ break;
+ case AD7266_MODE_PSEUDO_DIFF:
+ nr |= 1;
+ break;
+ case AD7266_MODE_DIFF:
+ nr &= ~1;
+ break;
+ }
+
+ for (i = 0; i < 3; ++i)
+ gpio_set_value(st->gpios[i].gpio, (bool)(nr & BIT(i)));
+}
+
+static int ad7266_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength);
+
+ ad7266_select_input(st, nr);
+
+ return 0;
+}
+
+static int ad7266_read_single(struct ad7266_state *st, int *val,
+ unsigned int address)
+{
+ int ret;
+
+ ad7266_select_input(st, address);
+
+ ret = spi_sync(st->spi, &st->single_msg);
+ *val = be16_to_cpu(st->data[address % 2]);
+
+ return ret;
+}
+
+static int ad7266_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long m)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ unsigned long scale_uv;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ ret = ad7266_read_single(st, val, chan->address);
+ if (ret)
+ return ret;
+
+ *val = (*val >> 2) & 0xfff;
+ if (chan->scan_type.sign == 's')
+ *val = sign_extend32(*val, 11);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = (st->vref_uv * 100);
+ if (st->mode == AD7266_MODE_DIFF)
+ scale_uv *= 2;
+ if (st->range == AD7266_RANGE_2VREF)
+ scale_uv *= 2;
+
+ scale_uv >>= chan->scan_type.realbits;
+ *val = scale_uv / 100000;
+ *val2 = (scale_uv % 100000) * 10;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ if (st->range == AD7266_RANGE_2VREF &&
+ st->mode != AD7266_MODE_DIFF)
+ *val = 2048;
+ else
+ *val = 0;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+#define AD7266_CHAN(_chan, _sign) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_chan), \
+ .address = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_index = (_chan), \
+ .scan_type = { \
+ .sign = (_sign), \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 2, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS(_name, _sign) \
+const struct iio_chan_spec ad7266_channels_##_name[] = { \
+ AD7266_CHAN(0, (_sign)), \
+ AD7266_CHAN(1, (_sign)), \
+ AD7266_CHAN(2, (_sign)), \
+ AD7266_CHAN(3, (_sign)), \
+ AD7266_CHAN(4, (_sign)), \
+ AD7266_CHAN(5, (_sign)), \
+ AD7266_CHAN(6, (_sign)), \
+ AD7266_CHAN(7, (_sign)), \
+ AD7266_CHAN(8, (_sign)), \
+ AD7266_CHAN(9, (_sign)), \
+ AD7266_CHAN(10, (_sign)), \
+ AD7266_CHAN(11, (_sign)), \
+ IIO_CHAN_SOFT_TIMESTAMP(13), \
+}
+
+#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(_name, _sign) \
+const struct iio_chan_spec ad7266_channels_##_name##_fixed[] = { \
+ AD7266_CHAN(0, (_sign)), \
+ AD7266_CHAN(1, (_sign)), \
+ IIO_CHAN_SOFT_TIMESTAMP(2), \
+}
+
+static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(u, 'u');
+static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(s, 's');
+static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(u, 'u');
+static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(s, 's');
+
+#define AD7266_CHAN_DIFF(_chan, _sign) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_chan) * 2, \
+ .channel2 = (_chan) * 2 + 1, \
+ .address = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_index = (_chan), \
+ .scan_type = { \
+ .sign = _sign, \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 2, \
+ .endianness = IIO_BE, \
+ }, \
+ .differential = 1, \
+}
+
+#define AD7266_DECLARE_DIFF_CHANNELS(_name, _sign) \
+const struct iio_chan_spec ad7266_channels_diff_##_name[] = { \
+ AD7266_CHAN_DIFF(0, (_sign)), \
+ AD7266_CHAN_DIFF(1, (_sign)), \
+ AD7266_CHAN_DIFF(2, (_sign)), \
+ AD7266_CHAN_DIFF(3, (_sign)), \
+ AD7266_CHAN_DIFF(4, (_sign)), \
+ AD7266_CHAN_DIFF(5, (_sign)), \
+ IIO_CHAN_SOFT_TIMESTAMP(6), \
+}
+
+static AD7266_DECLARE_DIFF_CHANNELS(s, 's');
+static AD7266_DECLARE_DIFF_CHANNELS(u, 'u');
+
+#define AD7266_DECLARE_DIFF_CHANNELS_FIXED(_name, _sign) \
+const struct iio_chan_spec ad7266_channels_diff_fixed_##_name[] = { \
+ AD7266_CHAN_DIFF(0, (_sign)), \
+ AD7266_CHAN_DIFF(1, (_sign)), \
+ IIO_CHAN_SOFT_TIMESTAMP(2), \
+}
+
+static AD7266_DECLARE_DIFF_CHANNELS_FIXED(s, 's');
+static AD7266_DECLARE_DIFF_CHANNELS_FIXED(u, 'u');
+
+static const struct iio_info ad7266_info = {
+ .read_raw = &ad7266_read_raw,
+ .update_scan_mode = &ad7266_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static unsigned long ad7266_available_scan_masks[] = {
+ 0x003,
+ 0x00c,
+ 0x030,
+ 0x0c0,
+ 0x300,
+ 0xc00,
+ 0x000,
+};
+
+static unsigned long ad7266_available_scan_masks_diff[] = {
+ 0x003,
+ 0x00c,
+ 0x030,
+ 0x000,
+};
+
+static unsigned long ad7266_available_scan_masks_fixed[] = {
+ 0x003,
+ 0x000,
+};
+
+struct ad7266_chan_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ unsigned long *scan_masks;
+};
+
+#define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \
+ (((_differential) << 2) | ((_signed) << 1) | ((_fixed) << 0))
+
+static const struct ad7266_chan_info ad7266_chan_infos[] = {
+ [AD7266_CHAN_INFO_INDEX(0, 0, 0)] = {
+ .channels = ad7266_channels_u,
+ .num_channels = ARRAY_SIZE(ad7266_channels_u),
+ .scan_masks = ad7266_available_scan_masks,
+ },
+ [AD7266_CHAN_INFO_INDEX(0, 0, 1)] = {
+ .channels = ad7266_channels_u_fixed,
+ .num_channels = ARRAY_SIZE(ad7266_channels_u_fixed),
+ .scan_masks = ad7266_available_scan_masks_fixed,
+ },
+ [AD7266_CHAN_INFO_INDEX(0, 1, 0)] = {
+ .channels = ad7266_channels_s,
+ .num_channels = ARRAY_SIZE(ad7266_channels_s),
+ .scan_masks = ad7266_available_scan_masks,
+ },
+ [AD7266_CHAN_INFO_INDEX(0, 1, 1)] = {
+ .channels = ad7266_channels_s_fixed,
+ .num_channels = ARRAY_SIZE(ad7266_channels_s_fixed),
+ .scan_masks = ad7266_available_scan_masks_fixed,
+ },
+ [AD7266_CHAN_INFO_INDEX(1, 0, 0)] = {
+ .channels = ad7266_channels_diff_u,
+ .num_channels = ARRAY_SIZE(ad7266_channels_diff_u),
+ .scan_masks = ad7266_available_scan_masks_diff,
+ },
+ [AD7266_CHAN_INFO_INDEX(1, 0, 1)] = {
+ .channels = ad7266_channels_diff_fixed_u,
+ .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_u),
+ .scan_masks = ad7266_available_scan_masks_fixed,
+ },
+ [AD7266_CHAN_INFO_INDEX(1, 1, 0)] = {
+ .channels = ad7266_channels_diff_s,
+ .num_channels = ARRAY_SIZE(ad7266_channels_diff_s),
+ .scan_masks = ad7266_available_scan_masks_diff,
+ },
+ [AD7266_CHAN_INFO_INDEX(1, 1, 1)] = {
+ .channels = ad7266_channels_diff_fixed_s,
+ .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_s),
+ .scan_masks = ad7266_available_scan_masks_fixed,
+ },
+};
+
+static void ad7266_init_channels(struct iio_dev *indio_dev)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ bool is_differential, is_signed;
+ const struct ad7266_chan_info *chan_info;
+ int i;
+
+ is_differential = st->mode != AD7266_MODE_SINGLE_ENDED;
+ is_signed = (st->range == AD7266_RANGE_2VREF) |
+ (st->mode == AD7266_MODE_DIFF);
+
+ i = AD7266_CHAN_INFO_INDEX(is_differential, is_signed, st->fixed_addr);
+ chan_info = &ad7266_chan_infos[i];
+
+ indio_dev->channels = chan_info->channels;
+ indio_dev->num_channels = chan_info->num_channels;
+ indio_dev->available_scan_masks = chan_info->scan_masks;
+ indio_dev->masklength = chan_info->num_channels - 1;
+}
+
+static const char * const ad7266_gpio_labels[] = {
+ "AD0", "AD1", "AD2",
+};
+
+static int ad7266_probe(struct spi_device *spi)
+{
+ struct ad7266_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad7266_state *st;
+ unsigned int i;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = regulator_get(&spi->dev, "vref");
+ if (!IS_ERR_OR_NULL(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ st->vref_uv = ret;
+ } else {
+ /* Use internal reference */
+ st->vref_uv = 2500000;
+ }
+
+ if (pdata) {
+ st->fixed_addr = pdata->fixed_addr;
+ st->mode = pdata->mode;
+ st->range = pdata->range;
+
+ if (!st->fixed_addr) {
+ for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) {
+ st->gpios[i].gpio = pdata->addr_gpios[i];
+ st->gpios[i].flags = GPIOF_OUT_INIT_LOW;
+ st->gpios[i].label = ad7266_gpio_labels[i];
+ }
+ ret = gpio_request_array(st->gpios,
+ ARRAY_SIZE(st->gpios));
+ if (ret)
+ goto error_disable_reg;
+ }
+ } else {
+ st->fixed_addr = true;
+ st->range = AD7266_RANGE_VREF;
+ st->mode = AD7266_MODE_DIFF;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7266_info;
+
+ ad7266_init_channels(indio_dev);
+
+ /* wakeup */
+ st->single_xfer[0].rx_buf = &st->data;
+ st->single_xfer[0].len = 2;
+ st->single_xfer[0].cs_change = 1;
+ /* conversion */
+ st->single_xfer[1].rx_buf = &st->data;
+ st->single_xfer[1].len = 4;
+ st->single_xfer[1].cs_change = 1;
+ /* powerdown */
+ st->single_xfer[2].tx_buf = &st->data;
+ st->single_xfer[2].len = 1;
+
+ spi_message_init(&st->single_msg);
+ spi_message_add_tail(&st->single_xfer[0], &st->single_msg);
+ spi_message_add_tail(&st->single_xfer[1], &st->single_msg);
+ spi_message_add_tail(&st->single_xfer[2], &st->single_msg);
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ &ad7266_trigger_handler, &iio_triggered_buffer_setup_ops);
+ if (ret)
+ goto error_free_gpios;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_buffer_cleanup;
+
+ return 0;
+
+error_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_free_gpios:
+ if (!st->fixed_addr)
+ gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
+error_disable_reg:
+ if (!IS_ERR_OR_NULL(st->reg))
+ regulator_disable(st->reg);
+error_put_reg:
+ if (!IS_ERR_OR_NULL(st->reg))
+ regulator_put(st->reg);
+
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad7266_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7266_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (!st->fixed_addr)
+ gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
+ if (!IS_ERR_OR_NULL(st->reg)) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7266_id[] = {
+ {"ad7265", 0},
+ {"ad7266", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7266_id);
+
+static struct spi_driver ad7266_driver = {
+ .driver = {
+ .name = "ad7266",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7266_probe,
+ .remove = ad7266_remove,
+ .id_table = ad7266_id,
+};
+module_spi_driver(ad7266_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD7266/65 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
new file mode 100644
index 00000000..03b77189
--- /dev/null
+++ b/drivers/iio/adc/ad7298.c
@@ -0,0 +1,408 @@
+/*
+ * AD7298 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/platform_data/ad7298.h>
+
+#define AD7298_WRITE (1 << 15) /* write to the control register */
+#define AD7298_REPEAT (1 << 14) /* repeated conversion enable */
+#define AD7298_CH(x) (1 << (13 - (x))) /* channel select */
+#define AD7298_TSENSE (1 << 5) /* temperature conversion enable */
+#define AD7298_EXTREF (1 << 2) /* external reference enable */
+#define AD7298_TAVG (1 << 1) /* temperature sensor averaging enable */
+#define AD7298_PDD (1 << 0) /* partial power down enable */
+
+#define AD7298_MAX_CHAN 8
+#define AD7298_BITS 12
+#define AD7298_STORAGE_BITS 16
+#define AD7298_INTREF_mV 2500
+
+#define AD7298_CH_TEMP 9
+
+#define RES_MASK(bits) ((1 << (bits)) - 1)
+
+struct ad7298_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned ext_ref;
+ struct spi_transfer ring_xfer[10];
+ struct spi_transfer scan_single_xfer[3];
+ struct spi_message ring_msg;
+ struct spi_message scan_single_msg;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 rx_buf[12] ____cacheline_aligned;
+ __be16 tx_buf[2];
+};
+
+#define AD7298_V_CHAN(index) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec ad7298_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = AD7298_CH_TEMP,
+ .scan_index = -1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ },
+ },
+ AD7298_V_CHAN(0),
+ AD7298_V_CHAN(1),
+ AD7298_V_CHAN(2),
+ AD7298_V_CHAN(3),
+ AD7298_V_CHAN(4),
+ AD7298_V_CHAN(5),
+ AD7298_V_CHAN(6),
+ AD7298_V_CHAN(7),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+/**
+ * ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask
+ **/
+static int ad7298_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *active_scan_mask)
+{
+ struct ad7298_state *st = iio_priv(indio_dev);
+ int i, m;
+ unsigned short command;
+ int scan_count;
+
+ /* Now compute overall size */
+ scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength);
+
+ command = AD7298_WRITE | st->ext_ref;
+
+ for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1)
+ if (test_bit(i, active_scan_mask))
+ command |= m;
+
+ st->tx_buf[0] = cpu_to_be16(command);
+
+ /* build spi ring message */
+ st->ring_xfer[0].tx_buf = &st->tx_buf[0];
+ st->ring_xfer[0].len = 2;
+ st->ring_xfer[0].cs_change = 1;
+ st->ring_xfer[1].tx_buf = &st->tx_buf[1];
+ st->ring_xfer[1].len = 2;
+ st->ring_xfer[1].cs_change = 1;
+
+ spi_message_init(&st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg);
+
+ for (i = 0; i < scan_count; i++) {
+ st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i];
+ st->ring_xfer[i + 2].len = 2;
+ st->ring_xfer[i + 2].cs_change = 1;
+ spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg);
+ }
+ /* make sure last transfer cs_change is not set */
+ st->ring_xfer[i + 1].cs_change = 0;
+
+ return 0;
+}
+
+/**
+ * ad7298_trigger_handler() bh of trigger launched polling to ring buffer
+ *
+ * Currently there is no option in this driver to disable the saving of
+ * timestamps within the ring.
+ **/
+static irqreturn_t ad7298_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7298_state *st = iio_priv(indio_dev);
+ s64 time_ns = 0;
+ int b_sent;
+
+ b_sent = spi_sync(st->spi, &st->ring_msg);
+ if (b_sent)
+ goto done;
+
+ if (indio_dev->scan_timestamp) {
+ time_ns = iio_get_time_ns();
+ memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64),
+ &time_ns, sizeof(time_ns));
+ }
+
+ iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf);
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch)
+{
+ int ret;
+ st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref |
+ (AD7298_CH(0) >> ch));
+
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ return ret;
+
+ return be16_to_cpu(st->rx_buf[0]);
+}
+
+static int ad7298_scan_temp(struct ad7298_state *st, int *val)
+{
+ int ret;
+ __be16 buf;
+
+ buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE |
+ AD7298_TAVG | st->ext_ref);
+
+ ret = spi_write(st->spi, (u8 *)&buf, 2);
+ if (ret)
+ return ret;
+
+ buf = cpu_to_be16(0);
+
+ ret = spi_write(st->spi, (u8 *)&buf, 2);
+ if (ret)
+ return ret;
+
+ usleep_range(101, 1000); /* sleep > 100us */
+
+ ret = spi_read(st->spi, (u8 *)&buf, 2);
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(be16_to_cpu(buf), 11);
+
+ return 0;
+}
+
+static int ad7298_get_ref_voltage(struct ad7298_state *st)
+{
+ int vref;
+
+ if (st->ext_ref) {
+ vref = regulator_get_voltage(st->reg);
+ if (vref < 0)
+ return vref;
+
+ return vref / 1000;
+ } else {
+ return AD7298_INTREF_mV;
+ }
+}
+
+static int ad7298_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad7298_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ ret = -EBUSY;
+ } else {
+ if (chan->address == AD7298_CH_TEMP)
+ ret = ad7298_scan_temp(st, val);
+ else
+ ret = ad7298_scan_direct(st, chan->address);
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret < 0)
+ return ret;
+
+ if (chan->address != AD7298_CH_TEMP)
+ *val = ret & RES_MASK(AD7298_BITS);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = ad7298_get_ref_voltage(st);
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_TEMP:
+ *val = ad7298_get_ref_voltage(st);
+ *val2 = 10;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 1093 - 2732500 / ad7298_get_ref_voltage(st);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_info ad7298_info = {
+ .read_raw = &ad7298_read_raw,
+ .update_scan_mode = ad7298_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad7298_probe(struct spi_device *spi)
+{
+ struct ad7298_platform_data *pdata = spi->dev.platform_data;
+ struct ad7298_state *st;
+ struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st));
+ int ret;
+
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ if (pdata && pdata->ext_ref)
+ st->ext_ref = AD7298_EXTREF;
+
+ if (st->ext_ref) {
+ st->reg = regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->reg)) {
+ ret = PTR_ERR(st->reg);
+ goto error_free;
+ }
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7298_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7298_channels);
+ indio_dev->info = &ad7298_info;
+
+ /* Setup default message */
+
+ st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
+ st->scan_single_xfer[0].len = 2;
+ st->scan_single_xfer[0].cs_change = 1;
+ st->scan_single_xfer[1].tx_buf = &st->tx_buf[1];
+ st->scan_single_xfer[1].len = 2;
+ st->scan_single_xfer[1].cs_change = 1;
+ st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
+ st->scan_single_xfer[2].len = 2;
+
+ spi_message_init(&st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &ad7298_trigger_handler, NULL);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_ring;
+
+ return 0;
+
+error_cleanup_ring:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_disable_reg:
+ if (st->ext_ref)
+ regulator_disable(st->reg);
+error_put_reg:
+ if (st->ext_ref)
+ regulator_put(st->reg);
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad7298_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7298_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (st->ext_ref) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7298_id[] = {
+ {"ad7298", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7298_id);
+
+static struct spi_driver ad7298_driver = {
+ .driver = {
+ .name = "ad7298",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7298_probe,
+ .remove = ad7298_remove,
+ .id_table = ad7298_id,
+};
+module_spi_driver(ad7298_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7298 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
new file mode 100644
index 00000000..2e98bef4
--- /dev/null
+++ b/drivers/iio/adc/ad7476.c
@@ -0,0 +1,332 @@
+/*
+ * AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define RES_MASK(bits) ((1 << (bits)) - 1)
+
+struct ad7476_state;
+
+struct ad7476_chip_info {
+ unsigned int int_vref_uv;
+ struct iio_chan_spec channel[2];
+ void (*reset)(struct ad7476_state *);
+};
+
+struct ad7476_state {
+ struct spi_device *spi;
+ const struct ad7476_chip_info *chip_info;
+ struct regulator *reg;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ * Make the buffer large enough for one 16 bit sample and one 64 bit
+ * aligned 64 bit timestamp.
+ */
+ unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)]
+ ____cacheline_aligned;
+};
+
+enum ad7476_supported_device_ids {
+ ID_AD7091R,
+ ID_AD7276,
+ ID_AD7277,
+ ID_AD7278,
+ ID_AD7466,
+ ID_AD7467,
+ ID_AD7468,
+ ID_AD7495,
+ ID_AD7940,
+};
+
+static irqreturn_t ad7476_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7476_state *st = iio_priv(indio_dev);
+ s64 time_ns;
+ int b_sent;
+
+ b_sent = spi_sync(st->spi, &st->msg);
+ if (b_sent < 0)
+ goto done;
+
+ time_ns = iio_get_time_ns();
+
+ if (indio_dev->scan_timestamp)
+ ((s64 *)st->data)[1] = time_ns;
+
+ iio_push_to_buffers(indio_dev, st->data);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void ad7091_reset(struct ad7476_state *st)
+{
+ /* Any transfers with 8 scl cycles will reset the device */
+ spi_read(st->spi, st->data, 1);
+}
+
+static int ad7476_scan_direct(struct ad7476_state *st)
+{
+ int ret;
+
+ ret = spi_sync(st->spi, &st->msg);
+ if (ret)
+ return ret;
+
+ return be16_to_cpup((__be16 *)st->data);
+}
+
+static int ad7476_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad7476_state *st = iio_priv(indio_dev);
+ int scale_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev))
+ ret = -EBUSY;
+ else
+ ret = ad7476_scan_direct(st);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret < 0)
+ return ret;
+ *val = (ret >> st->chip_info->channel[0].scan_type.shift) &
+ RES_MASK(st->chip_info->channel[0].scan_type.realbits);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (!st->chip_info->int_vref_uv) {
+ scale_uv = regulator_get_voltage(st->reg);
+ if (scale_uv < 0)
+ return scale_uv;
+ } else {
+ scale_uv = st->chip_info->int_vref_uv;
+ }
+ scale_uv >>= chan->scan_type.realbits;
+ *val = scale_uv / 1000;
+ *val2 = (scale_uv % 1000) * 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+}
+
+#define _AD7476_CHAN(bits, _shift, _info_mask_sep) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .info_mask_separate = _info_mask_sep, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = (_shift), \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
+ BIT(IIO_CHAN_INFO_RAW))
+#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
+ BIT(IIO_CHAN_INFO_RAW))
+#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
+
+static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
+ [ID_AD7091R] = {
+ .channel[0] = AD7091R_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ .reset = ad7091_reset,
+ },
+ [ID_AD7276] = {
+ .channel[0] = AD7940_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7277] = {
+ .channel[0] = AD7940_CHAN(10),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7278] = {
+ .channel[0] = AD7940_CHAN(8),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7466] = {
+ .channel[0] = AD7476_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7467] = {
+ .channel[0] = AD7476_CHAN(10),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7468] = {
+ .channel[0] = AD7476_CHAN(8),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7495] = {
+ .channel[0] = AD7476_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ .int_vref_uv = 2500000,
+ },
+ [ID_AD7940] = {
+ .channel[0] = AD7940_CHAN(14),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+};
+
+static const struct iio_info ad7476_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ad7476_read_raw,
+};
+
+static int ad7476_probe(struct spi_device *spi)
+{
+ struct ad7476_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ st = iio_priv(indio_dev);
+ st->chip_info =
+ &ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ st->reg = regulator_get(&spi->dev, "vcc");
+ if (IS_ERR(st->reg)) {
+ ret = PTR_ERR(st->reg);
+ goto error_free_dev;
+ }
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ /* Establish that the iio_dev is a child of the spi device */
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channel;
+ indio_dev->num_channels = 2;
+ indio_dev->info = &ad7476_info;
+ /* Setup default message */
+
+ st->xfer.rx_buf = &st->data;
+ st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8;
+
+ spi_message_init(&st->msg);
+ spi_message_add_tail(&st->xfer, &st->msg);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &ad7476_trigger_handler, NULL);
+ if (ret)
+ goto error_disable_reg;
+
+ if (st->chip_info->reset)
+ st->chip_info->reset(st);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_ring_unregister;
+ return 0;
+
+error_ring_unregister:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_disable_reg:
+ regulator_disable(st->reg);
+error_put_reg:
+ regulator_put(st->reg);
+error_free_dev:
+ iio_device_free(indio_dev);
+
+error_ret:
+ return ret;
+}
+
+static int ad7476_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7476_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7476_id[] = {
+ {"ad7091r", ID_AD7091R},
+ {"ad7273", ID_AD7277},
+ {"ad7274", ID_AD7276},
+ {"ad7276", ID_AD7276},
+ {"ad7277", ID_AD7277},
+ {"ad7278", ID_AD7278},
+ {"ad7466", ID_AD7466},
+ {"ad7467", ID_AD7467},
+ {"ad7468", ID_AD7468},
+ {"ad7475", ID_AD7466},
+ {"ad7476", ID_AD7466},
+ {"ad7476a", ID_AD7466},
+ {"ad7477", ID_AD7467},
+ {"ad7477a", ID_AD7467},
+ {"ad7478", ID_AD7468},
+ {"ad7478a", ID_AD7468},
+ {"ad7495", ID_AD7495},
+ {"ad7910", ID_AD7467},
+ {"ad7920", ID_AD7466},
+ {"ad7940", ID_AD7940},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7476_id);
+
+static struct spi_driver ad7476_driver = {
+ .driver = {
+ .name = "ad7476",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7476_probe,
+ .remove = ad7476_remove,
+ .id_table = ad7476_id,
+};
+module_spi_driver(ad7476_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c
new file mode 100644
index 00000000..5e8d1da6
--- /dev/null
+++ b/drivers/iio/adc/ad7791.c
@@ -0,0 +1,460 @@
+/*
+ * AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.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/adc/ad_sigma_delta.h>
+
+#include <linux/platform_data/ad7791.h>
+
+#define AD7791_REG_COMM 0x0 /* For writes */
+#define AD7791_REG_STATUS 0x0 /* For reads */
+#define AD7791_REG_MODE 0x1
+#define AD7791_REG_FILTER 0x2
+#define AD7791_REG_DATA 0x3
+
+#define AD7791_MODE_CONTINUOUS 0x00
+#define AD7791_MODE_SINGLE 0x02
+#define AD7791_MODE_POWERDOWN 0x03
+
+#define AD7791_CH_AIN1P_AIN1N 0x00
+#define AD7791_CH_AIN2 0x01
+#define AD7791_CH_AIN1N_AIN1N 0x02
+#define AD7791_CH_AVDD_MONITOR 0x03
+
+#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4)
+#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4)
+#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4)
+#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4)
+#define AD7791_FILTER_CLK_MASK (0x3 << 4)
+#define AD7791_FILTER_RATE_120 0x0
+#define AD7791_FILTER_RATE_100 0x1
+#define AD7791_FILTER_RATE_33_3 0x2
+#define AD7791_FILTER_RATE_20 0x3
+#define AD7791_FILTER_RATE_16_6 0x4
+#define AD7791_FILTER_RATE_16_7 0x5
+#define AD7791_FILTER_RATE_13_3 0x6
+#define AD7791_FILTER_RATE_9_5 0x7
+#define AD7791_FILTER_RATE_MASK 0x7
+
+#define AD7791_MODE_BUFFER BIT(1)
+#define AD7791_MODE_UNIPOLAR BIT(2)
+#define AD7791_MODE_BURNOUT BIT(3)
+#define AD7791_MODE_SEL_MASK (0x3 << 6)
+#define AD7791_MODE_SEL(x) ((x) << 6)
+
+#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \
+const struct iio_chan_spec name[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \
+ AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \
+ (bits), (storagebits), 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(4), \
+}
+
+#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \
+const struct iio_chan_spec name[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \
+ (bits), (storagebits), 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(3), \
+}
+
+static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32);
+static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16);
+static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32);
+
+enum {
+ AD7787,
+ AD7788,
+ AD7789,
+ AD7790,
+ AD7791,
+};
+
+enum ad7791_chip_info_flags {
+ AD7791_FLAG_HAS_FILTER = (1 << 0),
+ AD7791_FLAG_HAS_BUFFER = (1 << 1),
+ AD7791_FLAG_HAS_UNIPOLAR = (1 << 2),
+ AD7791_FLAG_HAS_BURNOUT = (1 << 3),
+};
+
+struct ad7791_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ enum ad7791_chip_info_flags flags;
+};
+
+static const struct ad7791_chip_info ad7791_chip_infos[] = {
+ [AD7787] = {
+ .channels = ad7787_channels,
+ .num_channels = ARRAY_SIZE(ad7787_channels),
+ .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+ AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
+ },
+ [AD7788] = {
+ .channels = ad7790_channels,
+ .num_channels = ARRAY_SIZE(ad7790_channels),
+ .flags = AD7791_FLAG_HAS_UNIPOLAR,
+ },
+ [AD7789] = {
+ .channels = ad7791_channels,
+ .num_channels = ARRAY_SIZE(ad7791_channels),
+ .flags = AD7791_FLAG_HAS_UNIPOLAR,
+ },
+ [AD7790] = {
+ .channels = ad7790_channels,
+ .num_channels = ARRAY_SIZE(ad7790_channels),
+ .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+ AD7791_FLAG_HAS_BURNOUT,
+ },
+ [AD7791] = {
+ .channels = ad7791_channels,
+ .num_channels = ARRAY_SIZE(ad7791_channels),
+ .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+ AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
+ },
+};
+
+struct ad7791_state {
+ struct ad_sigma_delta sd;
+ uint8_t mode;
+ uint8_t filter;
+
+ struct regulator *reg;
+ const struct ad7791_chip_info *info;
+};
+
+static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7791_state, sd);
+}
+
+static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ ad_sd_set_comm(sd, channel);
+
+ return 0;
+}
+
+static int ad7791_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd);
+
+ switch (mode) {
+ case AD_SD_MODE_CONTINUOUS:
+ mode = AD7791_MODE_CONTINUOUS;
+ break;
+ case AD_SD_MODE_SINGLE:
+ mode = AD7791_MODE_SINGLE;
+ break;
+ case AD_SD_MODE_IDLE:
+ case AD_SD_MODE_POWERDOWN:
+ mode = AD7791_MODE_POWERDOWN;
+ break;
+ }
+
+ st->mode &= ~AD7791_MODE_SEL_MASK;
+ st->mode |= AD7791_MODE_SEL(mode);
+
+ return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode);
+}
+
+static const struct ad_sigma_delta_info ad7791_sigma_delta_info = {
+ .set_channel = ad7791_set_channel,
+ .set_mode = ad7791_set_mode,
+ .has_registers = true,
+ .addr_shift = 4,
+ .read_mask = BIT(3),
+};
+
+static int ad7791_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct ad7791_state *st = iio_priv(indio_dev);
+ bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR);
+ unsigned long long scale_pv;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_OFFSET:
+ /**
+ * Unipolar: 0 to VREF
+ * Bipolar -VREF to VREF
+ **/
+ if (unipolar)
+ *val = 0;
+ else
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* The monitor channel uses an internal reference. */
+ if (chan->address == AD7791_CH_AVDD_MONITOR) {
+ scale_pv = 5850000000000ULL;
+ } else {
+ int voltage_uv;
+
+ voltage_uv = regulator_get_voltage(st->reg);
+ if (voltage_uv < 0)
+ return voltage_uv;
+ scale_pv = (unsigned long long)voltage_uv * 1000000;
+ }
+ if (unipolar)
+ scale_pv >>= chan->scan_type.realbits;
+ else
+ scale_pv >>= chan->scan_type.realbits - 1;
+ *val2 = do_div(scale_pv, 1000000000);
+ *val = scale_pv;
+
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+
+ return -EINVAL;
+}
+
+static const char * const ad7791_sample_freq_avail[] = {
+ [AD7791_FILTER_RATE_120] = "120",
+ [AD7791_FILTER_RATE_100] = "100",
+ [AD7791_FILTER_RATE_33_3] = "33.3",
+ [AD7791_FILTER_RATE_20] = "20",
+ [AD7791_FILTER_RATE_16_6] = "16.6",
+ [AD7791_FILTER_RATE_16_7] = "16.7",
+ [AD7791_FILTER_RATE_13_3] = "13.3",
+ [AD7791_FILTER_RATE_9_5] = "9.5",
+};
+
+static ssize_t ad7791_read_frequency(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7791_state *st = iio_priv(indio_dev);
+ unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK;
+
+ return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]);
+}
+
+static ssize_t ad7791_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);
+ struct ad7791_state *st = iio_priv(indio_dev);
+ int i, ret;
+
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ ret = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) {
+ if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) {
+
+ mutex_lock(&indio_dev->mlock);
+ st->filter &= ~AD7791_FILTER_RATE_MASK;
+ st->filter |= i;
+ ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
+ sizeof(st->filter), st->filter);
+ mutex_unlock(&indio_dev->mlock);
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ ad7791_read_frequency,
+ ad7791_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5");
+
+static struct attribute *ad7791_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7791_attribute_group = {
+ .attrs = ad7791_attributes,
+};
+
+static const struct iio_info ad7791_info = {
+ .read_raw = &ad7791_read_raw,
+ .attrs = &ad7791_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_info ad7791_no_filter_info = {
+ .read_raw = &ad7791_read_raw,
+ .validate_trigger = ad_sd_validate_trigger,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad7791_setup(struct ad7791_state *st,
+ struct ad7791_platform_data *pdata)
+{
+ /* Set to poweron-reset default values */
+ st->mode = AD7791_MODE_BUFFER;
+ st->filter = AD7791_FILTER_RATE_16_6;
+
+ if (!pdata)
+ return 0;
+
+ if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered)
+ st->mode &= ~AD7791_MODE_BUFFER;
+
+ if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) &&
+ pdata->burnout_current)
+ st->mode |= AD7791_MODE_BURNOUT;
+
+ if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar)
+ st->mode |= AD7791_MODE_UNIPOLAR;
+
+ return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode),
+ st->mode);
+}
+
+static int ad7791_probe(struct spi_device *spi)
+{
+ struct ad7791_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad7791_state *st;
+ int ret;
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "Missing IRQ.\n");
+ return -ENXIO;
+ }
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = regulator_get(&spi->dev, "refin");
+ if (IS_ERR(st->reg)) {
+ ret = PTR_ERR(st->reg);
+ goto err_iio_free;
+ }
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+
+ st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data];
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info);
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->info->channels;
+ indio_dev->num_channels = st->info->num_channels;
+ if (st->info->flags & AD7791_FLAG_HAS_FILTER)
+ indio_dev->info = &ad7791_info;
+ else
+ indio_dev->info = &ad7791_no_filter_info;
+
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad7791_setup(st, pdata);
+ if (ret)
+ goto error_remove_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_remove_trigger;
+
+ return 0;
+
+error_remove_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_disable_reg:
+ regulator_disable(st->reg);
+error_put_reg:
+ regulator_put(st->reg);
+err_iio_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad7791_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7791_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7791_spi_ids[] = {
+ { "ad7787", AD7787 },
+ { "ad7788", AD7788 },
+ { "ad7789", AD7789 },
+ { "ad7790", AD7790 },
+ { "ad7791", AD7791 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7791_spi_ids);
+
+static struct spi_driver ad7791_driver = {
+ .driver = {
+ .name = "ad7791",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7791_probe,
+ .remove = ad7791_remove,
+ .id_table = ad7791_spi_ids,
+};
+module_spi_driver(ad7791_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
new file mode 100644
index 00000000..334e31ff
--- /dev/null
+++ b/drivers/iio/adc/ad7793.c
@@ -0,0 +1,876 @@
+/*
+ * AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver
+ *
+ * Copyright 2011-2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.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/adc/ad_sigma_delta.h>
+#include <linux/platform_data/ad7793.h>
+
+/* Registers */
+#define AD7793_REG_COMM 0 /* Communications Register (WO, 8-bit) */
+#define AD7793_REG_STAT 0 /* Status Register (RO, 8-bit) */
+#define AD7793_REG_MODE 1 /* Mode Register (RW, 16-bit */
+#define AD7793_REG_CONF 2 /* Configuration Register (RW, 16-bit) */
+#define AD7793_REG_DATA 3 /* Data Register (RO, 16-/24-bit) */
+#define AD7793_REG_ID 4 /* ID Register (RO, 8-bit) */
+#define AD7793_REG_IO 5 /* IO Register (RO, 8-bit) */
+#define AD7793_REG_OFFSET 6 /* Offset Register (RW, 16-bit
+ * (AD7792)/24-bit (AD7793)) */
+#define AD7793_REG_FULLSALE 7 /* Full-Scale Register
+ * (RW, 16-bit (AD7792)/24-bit (AD7793)) */
+
+/* Communications Register Bit Designations (AD7793_REG_COMM) */
+#define AD7793_COMM_WEN (1 << 7) /* Write Enable */
+#define AD7793_COMM_WRITE (0 << 6) /* Write Operation */
+#define AD7793_COMM_READ (1 << 6) /* Read Operation */
+#define AD7793_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */
+#define AD7793_COMM_CREAD (1 << 2) /* Continuous Read of Data Register */
+
+/* Status Register Bit Designations (AD7793_REG_STAT) */
+#define AD7793_STAT_RDY (1 << 7) /* Ready */
+#define AD7793_STAT_ERR (1 << 6) /* Error (Overrange, Underrange) */
+#define AD7793_STAT_CH3 (1 << 2) /* Channel 3 */
+#define AD7793_STAT_CH2 (1 << 1) /* Channel 2 */
+#define AD7793_STAT_CH1 (1 << 0) /* Channel 1 */
+
+/* Mode Register Bit Designations (AD7793_REG_MODE) */
+#define AD7793_MODE_SEL(x) (((x) & 0x7) << 13) /* Operation Mode Select */
+#define AD7793_MODE_SEL_MASK (0x7 << 13) /* Operation Mode Select mask */
+#define AD7793_MODE_CLKSRC(x) (((x) & 0x3) << 6) /* ADC Clock Source Select */
+#define AD7793_MODE_RATE(x) ((x) & 0xF) /* Filter Update Rate Select */
+
+#define AD7793_MODE_CONT 0 /* Continuous Conversion Mode */
+#define AD7793_MODE_SINGLE 1 /* Single Conversion Mode */
+#define AD7793_MODE_IDLE 2 /* Idle Mode */
+#define AD7793_MODE_PWRDN 3 /* Power-Down Mode */
+#define AD7793_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */
+#define AD7793_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */
+#define AD7793_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */
+#define AD7793_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */
+
+#define AD7793_CLK_INT 0 /* Internal 64 kHz Clock not
+ * available at the CLK pin */
+#define AD7793_CLK_INT_CO 1 /* Internal 64 kHz Clock available
+ * at the CLK pin */
+#define AD7793_CLK_EXT 2 /* External 64 kHz Clock */
+#define AD7793_CLK_EXT_DIV2 3 /* External Clock divided by 2 */
+
+/* Configuration Register Bit Designations (AD7793_REG_CONF) */
+#define AD7793_CONF_VBIAS(x) (((x) & 0x3) << 14) /* Bias Voltage
+ * Generator Enable */
+#define AD7793_CONF_BO_EN (1 << 13) /* Burnout Current Enable */
+#define AD7793_CONF_UNIPOLAR (1 << 12) /* Unipolar/Bipolar Enable */
+#define AD7793_CONF_BOOST (1 << 11) /* Boost Enable */
+#define AD7793_CONF_GAIN(x) (((x) & 0x7) << 8) /* Gain Select */
+#define AD7793_CONF_REFSEL(x) ((x) << 6) /* INT/EXT Reference Select */
+#define AD7793_CONF_BUF (1 << 4) /* Buffered Mode Enable */
+#define AD7793_CONF_CHAN(x) ((x) & 0xf) /* Channel select */
+#define AD7793_CONF_CHAN_MASK 0xf /* Channel select mask */
+
+#define AD7793_CH_AIN1P_AIN1M 0 /* AIN1(+) - AIN1(-) */
+#define AD7793_CH_AIN2P_AIN2M 1 /* AIN2(+) - AIN2(-) */
+#define AD7793_CH_AIN3P_AIN3M 2 /* AIN3(+) - AIN3(-) */
+#define AD7793_CH_AIN1M_AIN1M 3 /* AIN1(-) - AIN1(-) */
+#define AD7793_CH_TEMP 6 /* Temp Sensor */
+#define AD7793_CH_AVDD_MONITOR 7 /* AVDD Monitor */
+
+#define AD7795_CH_AIN4P_AIN4M 4 /* AIN4(+) - AIN4(-) */
+#define AD7795_CH_AIN5P_AIN5M 5 /* AIN5(+) - AIN5(-) */
+#define AD7795_CH_AIN6P_AIN6M 6 /* AIN6(+) - AIN6(-) */
+#define AD7795_CH_AIN1M_AIN1M 8 /* AIN1(-) - AIN1(-) */
+
+/* ID Register Bit Designations (AD7793_REG_ID) */
+#define AD7785_ID 0xB
+#define AD7792_ID 0xA
+#define AD7793_ID 0xB
+#define AD7794_ID 0xF
+#define AD7795_ID 0xF
+#define AD7796_ID 0xA
+#define AD7797_ID 0xB
+#define AD7798_ID 0x8
+#define AD7799_ID 0x9
+#define AD7793_ID_MASK 0xF
+
+/* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */
+#define AD7793_IO_IEXC1_IOUT1_IEXC2_IOUT2 0 /* IEXC1 connect to IOUT1,
+ * IEXC2 connect to IOUT2 */
+#define AD7793_IO_IEXC1_IOUT2_IEXC2_IOUT1 1 /* IEXC1 connect to IOUT2,
+ * IEXC2 connect to IOUT1 */
+#define AD7793_IO_IEXC1_IEXC2_IOUT1 2 /* Both current sources
+ * IEXC1,2 connect to IOUT1 */
+#define AD7793_IO_IEXC1_IEXC2_IOUT2 3 /* Both current sources
+ * IEXC1,2 connect to IOUT2 */
+
+#define AD7793_IO_IXCEN_10uA (1 << 0) /* Excitation Current 10uA */
+#define AD7793_IO_IXCEN_210uA (2 << 0) /* Excitation Current 210uA */
+#define AD7793_IO_IXCEN_1mA (3 << 0) /* Excitation Current 1mA */
+
+/* NOTE:
+ * The AD7792/AD7793 features a dual use data out ready DOUT/RDY output.
+ * In order to avoid contentions on the SPI bus, it's therefore necessary
+ * to use spi bus locking.
+ *
+ * The DOUT/RDY output must also be wired to an interrupt capable GPIO.
+ */
+
+#define AD7793_FLAG_HAS_CLKSEL BIT(0)
+#define AD7793_FLAG_HAS_REFSEL BIT(1)
+#define AD7793_FLAG_HAS_VBIAS BIT(2)
+#define AD7793_HAS_EXITATION_CURRENT BIT(3)
+#define AD7793_FLAG_HAS_GAIN BIT(4)
+#define AD7793_FLAG_HAS_BUFFER BIT(5)
+
+struct ad7793_chip_info {
+ unsigned int id;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ unsigned int flags;
+
+ const struct iio_info *iio_info;
+ const u16 *sample_freq_avail;
+};
+
+struct ad7793_state {
+ const struct ad7793_chip_info *chip_info;
+ struct regulator *reg;
+ u16 int_vref_mv;
+ u16 mode;
+ u16 conf;
+ u32 scale_avail[8][2];
+
+ struct ad_sigma_delta sd;
+
+};
+
+enum ad7793_supported_device_ids {
+ ID_AD7785,
+ ID_AD7792,
+ ID_AD7793,
+ ID_AD7794,
+ ID_AD7795,
+ ID_AD7796,
+ ID_AD7797,
+ ID_AD7798,
+ ID_AD7799,
+};
+
+static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7793_state, sd);
+}
+
+static int ad7793_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd);
+
+ st->conf &= ~AD7793_CONF_CHAN_MASK;
+ st->conf |= AD7793_CONF_CHAN(channel);
+
+ return ad_sd_write_reg(&st->sd, AD7793_REG_CONF, 2, st->conf);
+}
+
+static int ad7793_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd);
+
+ st->mode &= ~AD7793_MODE_SEL_MASK;
+ st->mode |= AD7793_MODE_SEL(mode);
+
+ return ad_sd_write_reg(&st->sd, AD7793_REG_MODE, 2, st->mode);
+}
+
+static const struct ad_sigma_delta_info ad7793_sigma_delta_info = {
+ .set_channel = ad7793_set_channel,
+ .set_mode = ad7793_set_mode,
+ .has_registers = true,
+ .addr_shift = 3,
+ .read_mask = BIT(6),
+};
+
+static const struct ad_sd_calib_data ad7793_calib_arr[6] = {
+ {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN1P_AIN1M},
+ {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN1P_AIN1M},
+ {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN2P_AIN2M},
+ {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN2P_AIN2M},
+ {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN3P_AIN3M},
+ {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN3P_AIN3M}
+};
+
+static int ad7793_calibrate_all(struct ad7793_state *st)
+{
+ return ad_sd_calibrate_all(&st->sd, ad7793_calib_arr,
+ ARRAY_SIZE(ad7793_calib_arr));
+}
+
+static int ad7793_check_platform_data(struct ad7793_state *st,
+ const struct ad7793_platform_data *pdata)
+{
+ if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 ||
+ pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) &&
+ ((pdata->exitation_current != AD7793_IX_10uA) &&
+ (pdata->exitation_current != AD7793_IX_210uA)))
+ return -EINVAL;
+
+ if (!(st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) &&
+ pdata->clock_src != AD7793_CLK_SRC_INT)
+ return -EINVAL;
+
+ if (!(st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) &&
+ pdata->refsel != AD7793_REFSEL_REFIN1)
+ return -EINVAL;
+
+ if (!(st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) &&
+ pdata->bias_voltage != AD7793_BIAS_VOLTAGE_DISABLED)
+ return -EINVAL;
+
+ if (!(st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) &&
+ pdata->exitation_current != AD7793_IX_DISABLED)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ad7793_setup(struct iio_dev *indio_dev,
+ const struct ad7793_platform_data *pdata,
+ unsigned int vref_mv)
+{
+ struct ad7793_state *st = iio_priv(indio_dev);
+ int i, ret = -1;
+ unsigned long long scale_uv;
+ u32 id;
+
+ ret = ad7793_check_platform_data(st, pdata);
+ if (ret)
+ return ret;
+
+ /* reset the serial interface */
+ ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret));
+ if (ret < 0)
+ goto out;
+ usleep_range(500, 2000); /* Wait for at least 500us */
+
+ /* write/read test for device presence */
+ ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id);
+ if (ret)
+ goto out;
+
+ id &= AD7793_ID_MASK;
+
+ if (id != st->chip_info->id) {
+ dev_err(&st->sd.spi->dev, "device ID query failed\n");
+ goto out;
+ }
+
+ st->mode = AD7793_MODE_RATE(1);
+ st->conf = 0;
+
+ if (st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL)
+ st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src);
+ if (st->chip_info->flags & AD7793_FLAG_HAS_REFSEL)
+ st->conf |= AD7793_CONF_REFSEL(pdata->refsel);
+ if (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS)
+ st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage);
+ if (pdata->buffered || !(st->chip_info->flags & AD7793_FLAG_HAS_BUFFER))
+ st->conf |= AD7793_CONF_BUF;
+ if (pdata->boost_enable &&
+ (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS))
+ st->conf |= AD7793_CONF_BOOST;
+ if (pdata->burnout_current)
+ st->conf |= AD7793_CONF_BO_EN;
+ if (pdata->unipolar)
+ st->conf |= AD7793_CONF_UNIPOLAR;
+
+ if (!(st->chip_info->flags & AD7793_FLAG_HAS_GAIN))
+ st->conf |= AD7793_CONF_GAIN(7);
+
+ ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE);
+ if (ret)
+ goto out;
+
+ ret = ad7793_set_channel(&st->sd, 0);
+ if (ret)
+ goto out;
+
+ if (st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) {
+ ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1,
+ pdata->exitation_current |
+ (pdata->current_source_direction << 2));
+ if (ret)
+ goto out;
+ }
+
+ ret = ad7793_calibrate_all(st);
+ if (ret)
+ goto out;
+
+ /* Populate available ADC input ranges */
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
+ scale_uv = ((u64)vref_mv * 100000000)
+ >> (st->chip_info->channels[0].scan_type.realbits -
+ (!!(st->conf & AD7793_CONF_UNIPOLAR) ? 0 : 1));
+ scale_uv >>= i;
+
+ st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10;
+ st->scale_avail[i][0] = scale_uv;
+ }
+
+ return 0;
+out:
+ dev_err(&st->sd.spi->dev, "setup failed\n");
+ return ret;
+}
+
+static const u16 ad7793_sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39,
+ 33, 19, 17, 16, 12, 10, 8, 6, 4};
+
+static const u16 ad7797_sample_freq_avail[16] = {0, 0, 0, 123, 62, 50, 0,
+ 33, 0, 17, 16, 12, 10, 8, 6, 4};
+
+static ssize_t ad7793_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7793_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ st->chip_info->sample_freq_avail[AD7793_MODE_RATE(st->mode)]);
+}
+
+static ssize_t ad7793_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);
+ struct ad7793_state *st = iio_priv(indio_dev);
+ long lval;
+ int i, ret;
+
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ ret = kstrtol(buf, 10, &lval);
+ if (ret)
+ return ret;
+
+ if (lval == 0)
+ return -EINVAL;
+
+ ret = -EINVAL;
+
+ for (i = 0; i < 16; i++)
+ if (lval == st->chip_info->sample_freq_avail[i]) {
+ mutex_lock(&indio_dev->mlock);
+ st->mode &= ~AD7793_MODE_RATE(-1);
+ st->mode |= AD7793_MODE_RATE(i);
+ ad_sd_write_reg(&st->sd, AD7793_REG_MODE,
+ sizeof(st->mode), st->mode);
+ mutex_unlock(&indio_dev->mlock);
+ ret = 0;
+ }
+
+ return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ ad7793_read_frequency,
+ ad7793_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+ "470 242 123 62 50 39 33 19 17 16 12 10 8 6 4");
+
+static IIO_CONST_ATTR_NAMED(sampling_frequency_available_ad7797,
+ sampling_frequency_available, "123 62 50 33 17 16 12 10 8 6 4");
+
+static ssize_t ad7793_show_scale_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7793_state *st = iio_priv(indio_dev);
+ int i, len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
+ len += sprintf(buf + len, "%d.%09u ", st->scale_avail[i][0],
+ st->scale_avail[i][1]);
+
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available,
+ in_voltage-voltage_scale_available, S_IRUGO,
+ ad7793_show_scale_available, NULL, 0);
+
+static struct attribute *ad7793_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_in_m_in_scale_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7793_attribute_group = {
+ .attrs = ad7793_attributes,
+};
+
+static struct attribute *ad7797_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available_ad7797.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7797_attribute_group = {
+ .attrs = ad7797_attributes,
+};
+
+static int ad7793_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7793_state *st = iio_priv(indio_dev);
+ int ret;
+ unsigned long long scale_uv;
+ bool unipolar = !!(st->conf & AD7793_CONF_UNIPOLAR);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->differential) {
+ *val = st->
+ scale_avail[(st->conf >> 8) & 0x7][0];
+ *val2 = st->
+ scale_avail[(st->conf >> 8) & 0x7][1];
+ return IIO_VAL_INT_PLUS_NANO;
+ } else {
+ /* 1170mV / 2^23 * 6 */
+ scale_uv = (1170ULL * 1000000000ULL * 6ULL);
+ }
+ break;
+ case IIO_TEMP:
+ /* 1170mV / 0.81 mV/C / 2^23 */
+ scale_uv = 1444444444444444ULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1));
+ *val = 0;
+ *val2 = scale_uv;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ if (!unipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+
+ /* Kelvin to Celsius */
+ if (chan->type == IIO_TEMP) {
+ unsigned long long offset;
+ unsigned int shift;
+
+ shift = chan->scan_type.realbits - (unipolar ? 0 : 1);
+ offset = 273ULL << shift;
+ do_div(offset, 1444);
+ *val -= offset;
+ }
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int ad7793_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad7793_state *st = iio_priv(indio_dev);
+ int ret, i;
+ unsigned int tmp;
+
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ ret = -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
+ if (val2 == st->scale_avail[i][1]) {
+ ret = 0;
+ tmp = st->conf;
+ st->conf &= ~AD7793_CONF_GAIN(-1);
+ st->conf |= AD7793_CONF_GAIN(i);
+
+ if (tmp == st->conf)
+ break;
+
+ ad_sd_write_reg(&st->sd, AD7793_REG_CONF,
+ sizeof(st->conf), st->conf);
+ ad7793_calibrate_all(st);
+ break;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static int ad7793_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static const struct iio_info ad7793_info = {
+ .read_raw = &ad7793_read_raw,
+ .write_raw = &ad7793_write_raw,
+ .write_raw_get_fmt = &ad7793_write_raw_get_fmt,
+ .attrs = &ad7793_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_info ad7797_info = {
+ .read_raw = &ad7793_read_raw,
+ .write_raw = &ad7793_write_raw,
+ .write_raw_get_fmt = &ad7793_write_raw_get_fmt,
+ .attrs = &ad7793_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+ .driver_module = THIS_MODULE,
+};
+
+#define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \
+const struct iio_chan_spec _name##_channels[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \
+ AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \
+ AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \
+ AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \
+ AD_SD_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \
+ AD_SD_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \
+ IIO_CHAN_SOFT_TIMESTAMP(6), \
+}
+
+#define DECLARE_AD7795_CHANNELS(_name, _b, _sb) \
+const struct iio_chan_spec _name##_channels[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \
+ AD_SD_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \
+ AD_SD_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \
+ AD_SD_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(9), \
+}
+
+#define DECLARE_AD7797_CHANNELS(_name, _b, _sb) \
+const struct iio_chan_spec _name##_channels[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \
+ AD_SD_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \
+ AD_SD_TEMP_CHANNEL(2, AD7793_CH_TEMP, (_b), (_sb), 0), \
+ AD_SD_SUPPLY_CHANNEL(3, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(4), \
+}
+
+#define DECLARE_AD7799_CHANNELS(_name, _b, _sb) \
+const struct iio_chan_spec _name##_channels[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \
+ AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \
+ AD_SD_SUPPLY_CHANNEL(4, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(5), \
+}
+
+static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4);
+static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0);
+static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0);
+static DECLARE_AD7795_CHANNELS(ad7794, 16, 32);
+static DECLARE_AD7795_CHANNELS(ad7795, 24, 32);
+static DECLARE_AD7797_CHANNELS(ad7796, 16, 16);
+static DECLARE_AD7797_CHANNELS(ad7797, 24, 32);
+static DECLARE_AD7799_CHANNELS(ad7798, 16, 16);
+static DECLARE_AD7799_CHANNELS(ad7799, 24, 32);
+
+static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
+ [ID_AD7785] = {
+ .id = AD7785_ID,
+ .channels = ad7785_channels,
+ .num_channels = ARRAY_SIZE(ad7785_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7792] = {
+ .id = AD7792_ID,
+ .channels = ad7792_channels,
+ .num_channels = ARRAY_SIZE(ad7792_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7793] = {
+ .id = AD7793_ID,
+ .channels = ad7793_channels,
+ .num_channels = ARRAY_SIZE(ad7793_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7794] = {
+ .id = AD7794_ID,
+ .channels = ad7794_channels,
+ .num_channels = ARRAY_SIZE(ad7794_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7795] = {
+ .id = AD7795_ID,
+ .channels = ad7795_channels,
+ .num_channels = ARRAY_SIZE(ad7795_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7796] = {
+ .id = AD7796_ID,
+ .channels = ad7796_channels,
+ .num_channels = ARRAY_SIZE(ad7796_channels),
+ .iio_info = &ad7797_info,
+ .sample_freq_avail = ad7797_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL,
+ },
+ [ID_AD7797] = {
+ .id = AD7797_ID,
+ .channels = ad7797_channels,
+ .num_channels = ARRAY_SIZE(ad7797_channels),
+ .iio_info = &ad7797_info,
+ .sample_freq_avail = ad7797_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL,
+ },
+ [ID_AD7798] = {
+ .id = AD7798_ID,
+ .channels = ad7798_channels,
+ .num_channels = ARRAY_SIZE(ad7798_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7799] = {
+ .id = AD7799_ID,
+ .channels = ad7799_channels,
+ .num_channels = ARRAY_SIZE(ad7799_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+};
+
+static int ad7793_probe(struct spi_device *spi)
+{
+ const struct ad7793_platform_data *pdata = spi->dev.platform_data;
+ struct ad7793_state *st;
+ struct iio_dev *indio_dev;
+ int ret, vref_mv = 0;
+
+ if (!pdata) {
+ dev_err(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "no IRQ?\n");
+ return -ENODEV;
+ }
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info);
+
+ if (pdata->refsel != AD7793_REFSEL_INTERNAL) {
+ st->reg = regulator_get(&spi->dev, "refin");
+ if (IS_ERR(st->reg)) {
+ ret = PTR_ERR(st->reg);
+ goto error_device_free;
+ }
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+
+ vref_mv = regulator_get_voltage(st->reg);
+ if (vref_mv < 0) {
+ ret = vref_mv;
+ goto error_disable_reg;
+ }
+
+ vref_mv /= 1000;
+ } else {
+ vref_mv = 1170; /* Build-in ref */
+ }
+
+ st->chip_info =
+ &ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ indio_dev->info = st->chip_info->iio_info;
+
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad7793_setup(indio_dev, pdata, vref_mv);
+ if (ret)
+ goto error_remove_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_remove_trigger;
+
+ return 0;
+
+error_remove_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_disable_reg:
+ if (pdata->refsel != AD7793_REFSEL_INTERNAL)
+ regulator_disable(st->reg);
+error_put_reg:
+ if (pdata->refsel != AD7793_REFSEL_INTERNAL)
+ regulator_put(st->reg);
+error_device_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad7793_remove(struct spi_device *spi)
+{
+ const struct ad7793_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7793_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+
+ if (pdata->refsel != AD7793_REFSEL_INTERNAL) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7793_id[] = {
+ {"ad7785", ID_AD7785},
+ {"ad7792", ID_AD7792},
+ {"ad7793", ID_AD7793},
+ {"ad7794", ID_AD7794},
+ {"ad7795", ID_AD7795},
+ {"ad7796", ID_AD7796},
+ {"ad7797", ID_AD7797},
+ {"ad7798", ID_AD7798},
+ {"ad7799", ID_AD7799},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7793_id);
+
+static struct spi_driver ad7793_driver = {
+ .driver = {
+ .name = "ad7793",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7793_probe,
+ .remove = ad7793_remove,
+ .id_table = ad7793_id,
+};
+module_spi_driver(ad7793_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c
new file mode 100644
index 00000000..3394ebd4
--- /dev/null
+++ b/drivers/iio/adc/ad7887.c
@@ -0,0 +1,390 @@
+/*
+ * AD7887 SPI ADC driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/platform_data/ad7887.h>
+
+#define AD7887_REF_DIS (1 << 5) /* on-chip reference disable */
+#define AD7887_DUAL (1 << 4) /* dual-channel mode */
+#define AD7887_CH_AIN1 (1 << 3) /* convert on channel 1, DUAL=1 */
+#define AD7887_CH_AIN0 (0 << 3) /* convert on channel 0, DUAL=0,1 */
+#define AD7887_PM_MODE1 (0) /* CS based shutdown */
+#define AD7887_PM_MODE2 (1) /* full on */
+#define AD7887_PM_MODE3 (2) /* auto shutdown after conversion */
+#define AD7887_PM_MODE4 (3) /* standby mode */
+
+enum ad7887_channels {
+ AD7887_CH0,
+ AD7887_CH0_CH1,
+ AD7887_CH1,
+};
+
+#define RES_MASK(bits) ((1 << (bits)) - 1)
+
+/**
+ * struct ad7887_chip_info - chip specifc information
+ * @int_vref_mv: the internal reference voltage
+ * @channel: channel specification
+ */
+struct ad7887_chip_info {
+ u16 int_vref_mv;
+ struct iio_chan_spec channel[3];
+};
+
+struct ad7887_state {
+ struct spi_device *spi;
+ const struct ad7887_chip_info *chip_info;
+ struct regulator *reg;
+ struct spi_transfer xfer[4];
+ struct spi_message msg[3];
+ struct spi_message *ring_msg;
+ unsigned char tx_cmd_buf[4];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ * Buffer needs to be large enough to hold two 16 bit samples and a
+ * 64 bit aligned 64 bit timestamp.
+ */
+ unsigned char data[ALIGN(4, sizeof(s64)) + sizeof(s64)]
+ ____cacheline_aligned;
+};
+
+enum ad7887_supported_device_ids {
+ ID_AD7887
+};
+
+static int ad7887_ring_preenable(struct iio_dev *indio_dev)
+{
+ struct ad7887_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_sw_buffer_preenable(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ /* We know this is a single long so can 'cheat' */
+ switch (*indio_dev->active_scan_mask) {
+ case (1 << 0):
+ st->ring_msg = &st->msg[AD7887_CH0];
+ break;
+ case (1 << 1):
+ st->ring_msg = &st->msg[AD7887_CH1];
+ /* Dummy read: push CH1 setting down to hardware */
+ spi_sync(st->spi, st->ring_msg);
+ break;
+ case ((1 << 1) | (1 << 0)):
+ st->ring_msg = &st->msg[AD7887_CH0_CH1];
+ break;
+ }
+
+ return 0;
+}
+
+static int ad7887_ring_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad7887_state *st = iio_priv(indio_dev);
+
+ /* dummy read: restore default CH0 settin */
+ return spi_sync(st->spi, &st->msg[AD7887_CH0]);
+}
+
+/**
+ * ad7887_trigger_handler() bh of trigger launched polling to ring buffer
+ *
+ * Currently there is no option in this driver to disable the saving of
+ * timestamps within the ring.
+ **/
+static irqreturn_t ad7887_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7887_state *st = iio_priv(indio_dev);
+ s64 time_ns;
+ int b_sent;
+
+ b_sent = spi_sync(st->spi, st->ring_msg);
+ if (b_sent)
+ goto done;
+
+ time_ns = iio_get_time_ns();
+
+ if (indio_dev->scan_timestamp)
+ memcpy(st->data + indio_dev->scan_bytes - sizeof(s64),
+ &time_ns, sizeof(time_ns));
+
+ iio_push_to_buffers(indio_dev, st->data);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops ad7887_ring_setup_ops = {
+ .preenable = &ad7887_ring_preenable,
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &ad7887_ring_postdisable,
+};
+
+static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch)
+{
+ int ret = spi_sync(st->spi, &st->msg[ch]);
+ if (ret)
+ return ret;
+
+ return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1];
+}
+
+static int ad7887_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad7887_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev))
+ ret = -EBUSY;
+ else
+ ret = ad7887_scan_direct(st, chan->address);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret < 0)
+ return ret;
+ *val = ret >> chan->scan_type.shift;
+ *val &= RES_MASK(chan->scan_type.realbits);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (st->reg) {
+ *val = regulator_get_voltage(st->reg);
+ if (*val < 0)
+ return *val;
+ *val /= 1000;
+ } else {
+ *val = st->chip_info->int_vref_mv;
+ }
+
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+
+static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {
+ /*
+ * More devices added in future
+ */
+ [ID_AD7887] = {
+ .channel[0] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .address = 1,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ .shift = 0,
+ .endianness = IIO_BE,
+ },
+ },
+ .channel[1] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .address = 0,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ .shift = 0,
+ .endianness = IIO_BE,
+ },
+ },
+ .channel[2] = IIO_CHAN_SOFT_TIMESTAMP(2),
+ .int_vref_mv = 2500,
+ },
+};
+
+static const struct iio_info ad7887_info = {
+ .read_raw = &ad7887_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad7887_probe(struct spi_device *spi)
+{
+ struct ad7887_platform_data *pdata = spi->dev.platform_data;
+ struct ad7887_state *st;
+ struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st));
+ uint8_t mode;
+ int ret;
+
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ if (!pdata || !pdata->use_onchip_ref) {
+ st->reg = regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->reg)) {
+ ret = PTR_ERR(st->reg);
+ goto error_free;
+ }
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+ }
+
+ st->chip_info =
+ &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+
+ /* Estabilish that the iio_dev is a child of the spi device */
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad7887_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /* Setup default message */
+
+ mode = AD7887_PM_MODE4;
+ if (!pdata || !pdata->use_onchip_ref)
+ mode |= AD7887_REF_DIS;
+ if (pdata && pdata->en_dual)
+ mode |= AD7887_DUAL;
+
+ st->tx_cmd_buf[0] = AD7887_CH_AIN0 | mode;
+
+ st->xfer[0].rx_buf = &st->data[0];
+ st->xfer[0].tx_buf = &st->tx_cmd_buf[0];
+ st->xfer[0].len = 2;
+
+ spi_message_init(&st->msg[AD7887_CH0]);
+ spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]);
+
+ if (pdata && pdata->en_dual) {
+ st->tx_cmd_buf[2] = AD7887_CH_AIN1 | mode;
+
+ st->xfer[1].rx_buf = &st->data[0];
+ st->xfer[1].tx_buf = &st->tx_cmd_buf[2];
+ st->xfer[1].len = 2;
+
+ st->xfer[2].rx_buf = &st->data[2];
+ st->xfer[2].tx_buf = &st->tx_cmd_buf[0];
+ st->xfer[2].len = 2;
+
+ spi_message_init(&st->msg[AD7887_CH0_CH1]);
+ spi_message_add_tail(&st->xfer[1], &st->msg[AD7887_CH0_CH1]);
+ spi_message_add_tail(&st->xfer[2], &st->msg[AD7887_CH0_CH1]);
+
+ st->xfer[3].rx_buf = &st->data[2];
+ st->xfer[3].tx_buf = &st->tx_cmd_buf[2];
+ st->xfer[3].len = 2;
+
+ spi_message_init(&st->msg[AD7887_CH1]);
+ spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]);
+
+ indio_dev->channels = st->chip_info->channel;
+ indio_dev->num_channels = 3;
+ } else {
+ indio_dev->channels = &st->chip_info->channel[1];
+ indio_dev->num_channels = 2;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ &ad7887_trigger_handler, &ad7887_ring_setup_ops);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_unregister_ring;
+
+ return 0;
+error_unregister_ring:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_disable_reg:
+ if (st->reg)
+ regulator_disable(st->reg);
+error_put_reg:
+ if (st->reg)
+ regulator_put(st->reg);
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad7887_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7887_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (st->reg) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7887_id[] = {
+ {"ad7887", ID_AD7887},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7887_id);
+
+static struct spi_driver ad7887_driver = {
+ .driver = {
+ .name = "ad7887",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7887_probe,
+ .remove = ad7887_remove,
+ .id_table = ad7887_id,
+};
+module_spi_driver(ad7887_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7887 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
new file mode 100644
index 00000000..97fa0d3d
--- /dev/null
+++ b/drivers/iio/adc/ad7923.c
@@ -0,0 +1,383 @@
+/*
+ * AD7904/AD7914/AD7923/AD7924 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc (from AD7923 Driver)
+ * Copyright 2012 CS Systemes d'Information
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define AD7923_WRITE_CR (1 << 11) /* write control register */
+#define AD7923_RANGE (1 << 1) /* range to REFin */
+#define AD7923_CODING (1 << 0) /* coding is straight binary */
+#define AD7923_PM_MODE_AS (1) /* auto shutdown */
+#define AD7923_PM_MODE_FS (2) /* full shutdown */
+#define AD7923_PM_MODE_OPS (3) /* normal operation */
+#define AD7923_CHANNEL_0 (0) /* analog input 0 */
+#define AD7923_CHANNEL_1 (1) /* analog input 1 */
+#define AD7923_CHANNEL_2 (2) /* analog input 2 */
+#define AD7923_CHANNEL_3 (3) /* analog input 3 */
+#define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */
+#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */
+#define AD7923_SEQUENCE_ON (3) /* continuous sequence */
+
+#define AD7923_MAX_CHAN 4
+
+#define AD7923_PM_MODE_WRITE(mode) (mode << 4) /* write mode */
+#define AD7923_CHANNEL_WRITE(channel) (channel << 6) /* write channel */
+#define AD7923_SEQUENCE_WRITE(sequence) (((sequence & 1) << 3) \
+ + ((sequence & 2) << 9))
+ /* write sequence fonction */
+/* left shift for CR : bit 11 transmit in first */
+#define AD7923_SHIFT_REGISTER 4
+
+/* val = value, dec = left shift, bits = number of bits of the mask */
+#define EXTRACT(val, dec, bits) ((val >> dec) & ((1 << bits) - 1))
+
+struct ad7923_state {
+ struct spi_device *spi;
+ struct spi_transfer ring_xfer[5];
+ struct spi_transfer scan_single_xfer[2];
+ struct spi_message ring_msg;
+ struct spi_message scan_single_msg;
+
+ struct regulator *reg;
+
+ unsigned int settings;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 rx_buf[4] ____cacheline_aligned;
+ __be16 tx_buf[4];
+};
+
+struct ad7923_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+enum ad7923_id {
+ AD7904,
+ AD7914,
+ AD7924,
+};
+
+#define AD7923_V_CHAN(index, bits) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define DECLARE_AD7923_CHANNELS(name, bits) \
+const struct iio_chan_spec name ## _channels[] = { \
+ AD7923_V_CHAN(0, bits), \
+ AD7923_V_CHAN(1, bits), \
+ AD7923_V_CHAN(2, bits), \
+ AD7923_V_CHAN(3, bits), \
+ IIO_CHAN_SOFT_TIMESTAMP(4), \
+}
+
+static DECLARE_AD7923_CHANNELS(ad7904, 8);
+static DECLARE_AD7923_CHANNELS(ad7914, 10);
+static DECLARE_AD7923_CHANNELS(ad7924, 12);
+
+static const struct ad7923_chip_info ad7923_chip_info[] = {
+ [AD7904] = {
+ .channels = ad7904_channels,
+ .num_channels = ARRAY_SIZE(ad7904_channels),
+ },
+ [AD7914] = {
+ .channels = ad7914_channels,
+ .num_channels = ARRAY_SIZE(ad7914_channels),
+ },
+ [AD7924] = {
+ .channels = ad7924_channels,
+ .num_channels = ARRAY_SIZE(ad7924_channels),
+ },
+};
+
+/**
+ * ad7923_update_scan_mode() setup the spi transfer buffer for the new scan mask
+ **/
+static int ad7923_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *active_scan_mask)
+{
+ struct ad7923_state *st = iio_priv(indio_dev);
+ int i, cmd, len;
+
+ len = 0;
+ for_each_set_bit(i, active_scan_mask, AD7923_MAX_CHAN) {
+ cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) |
+ AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
+ st->settings;
+ cmd <<= AD7923_SHIFT_REGISTER;
+ st->tx_buf[len++] = cpu_to_be16(cmd);
+ }
+ /* build spi ring message */
+ st->ring_xfer[0].tx_buf = &st->tx_buf[0];
+ st->ring_xfer[0].len = len;
+ st->ring_xfer[0].cs_change = 1;
+
+ spi_message_init(&st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
+
+ for (i = 0; i < len; i++) {
+ st->ring_xfer[i + 1].rx_buf = &st->rx_buf[i];
+ st->ring_xfer[i + 1].len = 2;
+ st->ring_xfer[i + 1].cs_change = 1;
+ spi_message_add_tail(&st->ring_xfer[i + 1], &st->ring_msg);
+ }
+ /* make sure last transfer cs_change is not set */
+ st->ring_xfer[i + 1].cs_change = 0;
+
+ return 0;
+}
+
+/**
+ * ad7923_trigger_handler() bh of trigger launched polling to ring buffer
+ *
+ * Currently there is no option in this driver to disable the saving of
+ * timestamps within the ring.
+ **/
+static irqreturn_t ad7923_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7923_state *st = iio_priv(indio_dev);
+ s64 time_ns = 0;
+ int b_sent;
+
+ b_sent = spi_sync(st->spi, &st->ring_msg);
+ if (b_sent)
+ goto done;
+
+ if (indio_dev->scan_timestamp) {
+ time_ns = iio_get_time_ns();
+ memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64),
+ &time_ns, sizeof(time_ns));
+ }
+
+ iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf);
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ad7923_scan_direct(struct ad7923_state *st, unsigned ch)
+{
+ int ret, cmd;
+
+ cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(ch) |
+ AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
+ st->settings;
+ cmd <<= AD7923_SHIFT_REGISTER;
+ st->tx_buf[0] = cpu_to_be16(cmd);
+
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ return ret;
+
+ return be16_to_cpu(st->rx_buf[0]);
+}
+
+static int ad7923_get_range(struct ad7923_state *st)
+{
+ int vref;
+
+ vref = regulator_get_voltage(st->reg);
+ if (vref < 0)
+ return vref;
+
+ vref /= 1000;
+
+ if (!(st->settings & AD7923_RANGE))
+ vref *= 2;
+
+ return vref;
+}
+
+static int ad7923_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad7923_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev))
+ ret = -EBUSY;
+ else
+ ret = ad7923_scan_direct(st, chan->address);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret < 0)
+ return ret;
+
+ if (chan->address == EXTRACT(ret, 12, 4))
+ *val = EXTRACT(ret, 0, 12);
+ else
+ return -EIO;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = ad7923_get_range(st);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_info ad7923_info = {
+ .read_raw = &ad7923_read_raw,
+ .update_scan_mode = ad7923_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad7923_probe(struct spi_device *spi)
+{
+ struct ad7923_state *st;
+ struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st));
+ const struct ad7923_chip_info *info;
+ int ret;
+
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+ st->settings = AD7923_CODING | AD7923_RANGE |
+ AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS);
+
+ info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data];
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = info->channels;
+ indio_dev->num_channels = info->num_channels;
+ indio_dev->info = &ad7923_info;
+
+ /* Setup default message */
+
+ st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
+ st->scan_single_xfer[0].len = 2;
+ st->scan_single_xfer[0].cs_change = 1;
+ st->scan_single_xfer[1].rx_buf = &st->rx_buf[0];
+ st->scan_single_xfer[1].len = 2;
+
+ spi_message_init(&st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg);
+
+ st->reg = regulator_get(&spi->dev, "refin");
+ if (IS_ERR(st->reg)) {
+ ret = PTR_ERR(st->reg);
+ goto error_free;
+ }
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &ad7923_trigger_handler, NULL);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_ring;
+
+ return 0;
+
+error_cleanup_ring:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_disable_reg:
+ regulator_disable(st->reg);
+error_put_reg:
+ regulator_put(st->reg);
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad7923_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7923_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7923_id[] = {
+ {"ad7904", AD7904},
+ {"ad7914", AD7914},
+ {"ad7923", AD7924},
+ {"ad7924", AD7924},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7923_id);
+
+static struct spi_driver ad7923_driver = {
+ .driver = {
+ .name = "ad7923",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7923_probe,
+ .remove = ad7923_remove,
+ .id_table = ad7923_id,
+};
+module_spi_driver(ad7923_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
+MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
new file mode 100644
index 00000000..05d2733e
--- /dev/null
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -0,0 +1,558 @@
+/*
+ * Support code for Analog Devices Sigma-Delta ADCs
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.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/adc/ad_sigma_delta.h>
+
+#include <asm/unaligned.h>
+
+
+#define AD_SD_COMM_CHAN_MASK 0x3
+
+#define AD_SD_REG_COMM 0x00
+#define AD_SD_REG_DATA 0x03
+
+/**
+ * ad_sd_set_comm() - Set communications register
+ *
+ * @sigma_delta: The sigma delta device
+ * @comm: New value for the communications register
+ */
+void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm)
+{
+ /* Some variants use the lower two bits of the communications register
+ * to select the channel */
+ sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK;
+}
+EXPORT_SYMBOL_GPL(ad_sd_set_comm);
+
+/**
+ * ad_sd_write_reg() - Write a register
+ *
+ * @sigma_delta: The sigma delta device
+ * @reg: Address of the register
+ * @size: Size of the register (0-3)
+ * @val: Value to write to the register
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
+ unsigned int size, unsigned int val)
+{
+ uint8_t *data = sigma_delta->data;
+ struct spi_transfer t = {
+ .tx_buf = data,
+ .len = size + 1,
+ .cs_change = sigma_delta->bus_locked,
+ };
+ struct spi_message m;
+ int ret;
+
+ data[0] = (reg << sigma_delta->info->addr_shift) | sigma_delta->comm;
+
+ switch (size) {
+ case 3:
+ data[1] = val >> 16;
+ data[2] = val >> 8;
+ data[3] = val;
+ break;
+ case 2:
+ put_unaligned_be16(val, &data[1]);
+ break;
+ case 1:
+ data[1] = val;
+ break;
+ case 0:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ if (sigma_delta->bus_locked)
+ ret = spi_sync_locked(sigma_delta->spi, &m);
+ else
+ ret = spi_sync(sigma_delta->spi, &m);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ad_sd_write_reg);
+
+static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
+ unsigned int reg, unsigned int size, uint8_t *val)
+{
+ uint8_t *data = sigma_delta->data;
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = data,
+ .len = 1,
+ }, {
+ .rx_buf = val,
+ .len = size,
+ .cs_change = sigma_delta->bus_locked,
+ },
+ };
+ struct spi_message m;
+
+ spi_message_init(&m);
+
+ if (sigma_delta->info->has_registers) {
+ data[0] = reg << sigma_delta->info->addr_shift;
+ data[0] |= sigma_delta->info->read_mask;
+ spi_message_add_tail(&t[0], &m);
+ }
+ spi_message_add_tail(&t[1], &m);
+
+ if (sigma_delta->bus_locked)
+ ret = spi_sync_locked(sigma_delta->spi, &m);
+ else
+ ret = spi_sync(sigma_delta->spi, &m);
+
+ return ret;
+}
+
+/**
+ * ad_sd_read_reg() - Read a register
+ *
+ * @sigma_delta: The sigma delta device
+ * @reg: Address of the register
+ * @size: Size of the register (1-4)
+ * @val: Read value
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta,
+ unsigned int reg, unsigned int size, unsigned int *val)
+{
+ int ret;
+
+ ret = ad_sd_read_reg_raw(sigma_delta, reg, size, sigma_delta->data);
+ if (ret < 0)
+ goto out;
+
+ switch (size) {
+ case 4:
+ *val = get_unaligned_be32(sigma_delta->data);
+ break;
+ case 3:
+ *val = (sigma_delta->data[0] << 16) |
+ (sigma_delta->data[1] << 8) |
+ sigma_delta->data[2];
+ break;
+ case 2:
+ *val = get_unaligned_be16(sigma_delta->data);
+ break;
+ case 1:
+ *val = sigma_delta->data[0];
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ad_sd_read_reg);
+
+static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
+ unsigned int mode, unsigned int channel)
+{
+ int ret;
+
+ ret = ad_sigma_delta_set_channel(sigma_delta, channel);
+ if (ret)
+ return ret;
+
+ spi_bus_lock(sigma_delta->spi->master);
+ sigma_delta->bus_locked = true;
+ INIT_COMPLETION(sigma_delta->completion);
+
+ ret = ad_sigma_delta_set_mode(sigma_delta, mode);
+ if (ret < 0)
+ goto out;
+
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+ ret = wait_for_completion_timeout(&sigma_delta->completion, 2*HZ);
+ if (ret == 0) {
+ sigma_delta->irq_dis = true;
+ disable_irq_nosync(sigma_delta->spi->irq);
+ ret = -EIO;
+ } else {
+ ret = 0;
+ }
+out:
+ sigma_delta->bus_locked = false;
+ spi_bus_unlock(sigma_delta->spi->master);
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+
+ return ret;
+}
+
+/**
+ * ad_sd_calibrate_all() - Performs channel calibration
+ * @sigma_delta: The sigma delta device
+ * @cb: Array of channels and calibration type to perform
+ * @n: Number of items in cb
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
+ const struct ad_sd_calib_data *cb, unsigned int n)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < n; i++) {
+ ret = ad_sd_calibrate(sigma_delta, cb[i].mode, cb[i].channel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_calibrate_all);
+
+/**
+ * ad_sigma_delta_single_conversion() - Performs a single data conversion
+ * @indio_dev: The IIO device
+ * @chan: The conversion is done for this channel
+ * @val: Pointer to the location where to store the read value
+ *
+ * Returns: 0 on success, an error value otherwise.
+ */
+int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ unsigned int sample, raw_sample;
+ int ret = 0;
+
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ mutex_lock(&indio_dev->mlock);
+ ad_sigma_delta_set_channel(sigma_delta, chan->address);
+
+ spi_bus_lock(sigma_delta->spi->master);
+ sigma_delta->bus_locked = true;
+ INIT_COMPLETION(sigma_delta->completion);
+
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
+
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+ ret = wait_for_completion_interruptible_timeout(
+ &sigma_delta->completion, HZ);
+
+ sigma_delta->bus_locked = false;
+ spi_bus_unlock(sigma_delta->spi->master);
+
+ if (ret == 0)
+ ret = -EIO;
+ if (ret < 0)
+ goto out;
+
+ ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA,
+ DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
+ &raw_sample);
+
+out:
+ if (!sigma_delta->irq_dis) {
+ disable_irq_nosync(sigma_delta->spi->irq);
+ sigma_delta->irq_dis = true;
+ }
+
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret)
+ return ret;
+
+ sample = raw_sample >> chan->scan_type.shift;
+ sample &= (1 << chan->scan_type.realbits) - 1;
+ *val = sample;
+
+ ret = ad_sigma_delta_postprocess_sample(sigma_delta, raw_sample);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+}
+EXPORT_SYMBOL_GPL(ad_sigma_delta_single_conversion);
+
+static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ unsigned int channel;
+ int ret;
+
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ channel = find_first_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+ ret = ad_sigma_delta_set_channel(sigma_delta,
+ indio_dev->channels[channel].address);
+ if (ret)
+ goto err_predisable;
+
+ spi_bus_lock(sigma_delta->spi->master);
+ sigma_delta->bus_locked = true;
+ ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS);
+ if (ret)
+ goto err_unlock;
+
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+
+ return 0;
+
+err_unlock:
+ spi_bus_unlock(sigma_delta->spi->master);
+err_predisable:
+
+ return ret;
+}
+
+static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+ INIT_COMPLETION(sigma_delta->completion);
+ wait_for_completion_timeout(&sigma_delta->completion, HZ);
+
+ if (!sigma_delta->irq_dis) {
+ disable_irq_nosync(sigma_delta->spi->irq);
+ sigma_delta->irq_dis = true;
+ }
+
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+
+ sigma_delta->bus_locked = false;
+ return spi_bus_unlock(sigma_delta->spi->master);
+}
+
+static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ unsigned int reg_size;
+ uint8_t data[16];
+ int ret;
+
+ memset(data, 0x00, 16);
+
+ /* Guaranteed to be aligned with 8 byte boundary */
+ if (indio_dev->scan_timestamp)
+ ((s64 *)data)[1] = pf->timestamp;
+
+ reg_size = indio_dev->channels[0].scan_type.realbits +
+ indio_dev->channels[0].scan_type.shift;
+ reg_size = DIV_ROUND_UP(reg_size, 8);
+
+ switch (reg_size) {
+ case 4:
+ case 2:
+ case 1:
+ ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
+ reg_size, &data[0]);
+ break;
+ case 3:
+ /* We store 24 bit samples in a 32 bit word. Keep the upper
+ * byte set to zero. */
+ ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
+ reg_size, &data[1]);
+ break;
+ }
+
+ iio_push_to_buffers(indio_dev, (uint8_t *)data);
+
+ iio_trigger_notify_done(indio_dev->trig);
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
+ .preenable = &iio_sw_buffer_preenable,
+ .postenable = &ad_sd_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &ad_sd_buffer_postdisable,
+ .validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
+static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private)
+{
+ struct ad_sigma_delta *sigma_delta = private;
+
+ complete(&sigma_delta->completion);
+ disable_irq_nosync(irq);
+ sigma_delta->irq_dis = true;
+ iio_trigger_poll(sigma_delta->trig, iio_get_time_ns());
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ad_sd_validate_trigger() - validate_trigger callback for ad_sigma_delta devices
+ * @indio_dev: The IIO device
+ * @trig: The new trigger
+ *
+ * Returns: 0 if the 'trig' matches the trigger registered by the ad_sigma_delta
+ * device, -EINVAL otherwise.
+ */
+int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+ if (sigma_delta->trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_validate_trigger);
+
+static const struct iio_trigger_ops ad_sd_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+static int ad_sd_probe_trigger(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ sigma_delta->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
+ indio_dev->id);
+ if (sigma_delta->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ sigma_delta->trig->ops = &ad_sd_trigger_ops;
+ init_completion(&sigma_delta->completion);
+
+ ret = request_irq(sigma_delta->spi->irq,
+ ad_sd_data_rdy_trig_poll,
+ IRQF_TRIGGER_LOW,
+ indio_dev->name,
+ sigma_delta);
+ if (ret)
+ goto error_free_trig;
+
+ if (!sigma_delta->irq_dis) {
+ sigma_delta->irq_dis = true;
+ disable_irq_nosync(sigma_delta->spi->irq);
+ }
+ sigma_delta->trig->dev.parent = &sigma_delta->spi->dev;
+ iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta);
+
+ ret = iio_trigger_register(sigma_delta->trig);
+ if (ret)
+ goto error_free_irq;
+
+ /* select default trigger */
+ indio_dev->trig = iio_trigger_get(sigma_delta->trig);
+
+ return 0;
+
+error_free_irq:
+ free_irq(sigma_delta->spi->irq, sigma_delta);
+error_free_trig:
+ iio_trigger_free(sigma_delta->trig);
+error_ret:
+ return ret;
+}
+
+static void ad_sd_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+ iio_trigger_unregister(sigma_delta->trig);
+ free_irq(sigma_delta->spi->irq, sigma_delta);
+ iio_trigger_free(sigma_delta->trig);
+}
+
+/**
+ * ad_sd_setup_buffer_and_trigger() -
+ * @indio_dev: The IIO device
+ */
+int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ &ad_sd_trigger_handler, &ad_sd_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ ret = ad_sd_probe_trigger(indio_dev);
+ if (ret) {
+ iio_triggered_buffer_cleanup(indio_dev);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_setup_buffer_and_trigger);
+
+/**
+ * ad_sd_cleanup_buffer_and_trigger() -
+ * @indio_dev: The IIO device
+ */
+void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev)
+{
+ ad_sd_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL_GPL(ad_sd_cleanup_buffer_and_trigger);
+
+/**
+ * ad_sd_init() - Initializes a ad_sigma_delta struct
+ * @sigma_delta: The ad_sigma_delta device
+ * @indio_dev: The IIO device which the Sigma Delta device is used for
+ * @spi: The SPI device for the ad_sigma_delta device
+ * @info: Device specific callbacks and options
+ *
+ * This function needs to be called before any other operations are performed on
+ * the ad_sigma_delta struct.
+ */
+int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
+ struct spi_device *spi, const struct ad_sigma_delta_info *info)
+{
+ sigma_delta->spi = spi;
+ sigma_delta->info = info;
+ iio_device_set_drvdata(indio_dev, sigma_delta);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_init);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
new file mode 100644
index 00000000..14fdaf0f
--- /dev/null
+++ b/drivers/iio/adc/at91_adc.c
@@ -0,0 +1,796 @@
+/*
+ * Driver for the ADC present in the Atmel AT91 evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <linux/platform_data/at91_adc.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 <mach/at91_adc.h>
+
+#define AT91_ADC_CHAN(st, ch) \
+ (st->registers->channel_base + (ch * 4))
+#define at91_adc_readl(st, reg) \
+ (readl_relaxed(st->reg_base + reg))
+#define at91_adc_writel(st, reg, val) \
+ (writel_relaxed(val, st->reg_base + reg))
+
+struct at91_adc_state {
+ struct clk *adc_clk;
+ u16 *buffer;
+ unsigned long channels_mask;
+ struct clk *clk;
+ bool done;
+ int irq;
+ u16 last_value;
+ struct mutex lock;
+ u8 num_channels;
+ void __iomem *reg_base;
+ struct at91_adc_reg_desc *registers;
+ u8 startup_time;
+ u8 sample_hold_time;
+ bool sleep_mode;
+ struct iio_trigger **trig;
+ struct at91_adc_trigger *trigger_list;
+ u32 trigger_number;
+ bool use_external;
+ u32 vref_mv;
+ u32 res; /* resolution used for convertions */
+ bool low_res; /* the resolution corresponds to the lowest one */
+ wait_queue_head_t wq_data_avail;
+};
+
+static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *idev = pf->indio_dev;
+ struct at91_adc_state *st = iio_priv(idev);
+ int i, j = 0;
+
+ for (i = 0; i < idev->masklength; i++) {
+ if (!test_bit(i, idev->active_scan_mask))
+ continue;
+ st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, i));
+ j++;
+ }
+
+ if (idev->scan_timestamp) {
+ s64 *timestamp = (s64 *)((u8 *)st->buffer +
+ ALIGN(j, sizeof(s64)));
+ *timestamp = pf->timestamp;
+ }
+
+ iio_push_to_buffers(idev, (u8 *)st->buffer);
+
+ iio_trigger_notify_done(idev->trig);
+
+ /* Needed to ACK the DRDY interruption */
+ at91_adc_readl(st, AT91_ADC_LCDR);
+
+ enable_irq(st->irq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+{
+ struct iio_dev *idev = private;
+ struct at91_adc_state *st = iio_priv(idev);
+ u32 status = at91_adc_readl(st, st->registers->status_register);
+
+ if (!(status & st->registers->drdy_mask))
+ return IRQ_HANDLED;
+
+ if (iio_buffer_enabled(idev)) {
+ disable_irq_nosync(irq);
+ iio_trigger_poll(idev->trig, iio_get_time_ns());
+ } else {
+ st->last_value = at91_adc_readl(st, AT91_ADC_LCDR);
+ st->done = true;
+ wake_up_interruptible(&st->wq_data_avail);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int at91_adc_channel_init(struct iio_dev *idev)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ struct iio_chan_spec *chan_array, *timestamp;
+ int bit, idx = 0;
+
+ idev->num_channels = bitmap_weight(&st->channels_mask,
+ st->num_channels) + 1;
+
+ chan_array = devm_kzalloc(&idev->dev,
+ ((idev->num_channels + 1) *
+ sizeof(struct iio_chan_spec)),
+ GFP_KERNEL);
+
+ if (!chan_array)
+ return -ENOMEM;
+
+ for_each_set_bit(bit, &st->channels_mask, st->num_channels) {
+ struct iio_chan_spec *chan = chan_array + idx;
+
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->channel = bit;
+ chan->scan_index = idx;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = st->res;
+ chan->scan_type.storagebits = 16;
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ idx++;
+ }
+ timestamp = chan_array + idx;
+
+ timestamp->type = IIO_TIMESTAMP;
+ timestamp->channel = -1;
+ timestamp->scan_index = idx;
+ timestamp->scan_type.sign = 's';
+ timestamp->scan_type.realbits = 64;
+ timestamp->scan_type.storagebits = 64;
+
+ idev->channels = chan_array;
+ return idev->num_channels;
+}
+
+static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
+ struct at91_adc_trigger *triggers,
+ const char *trigger_name)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ int i;
+
+ for (i = 0; i < st->trigger_number; i++) {
+ char *name = kasprintf(GFP_KERNEL,
+ "%s-dev%d-%s",
+ idev->name,
+ idev->id,
+ triggers[i].name);
+ if (!name)
+ return -ENOMEM;
+
+ if (strcmp(trigger_name, name) == 0) {
+ kfree(name);
+ if (triggers[i].value == 0)
+ return -EINVAL;
+ return triggers[i].value;
+ }
+
+ kfree(name);
+ }
+
+ return -EINVAL;
+}
+
+static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *idev = iio_trigger_get_drvdata(trig);
+ struct at91_adc_state *st = iio_priv(idev);
+ struct iio_buffer *buffer = idev->buffer;
+ struct at91_adc_reg_desc *reg = st->registers;
+ u32 status = at91_adc_readl(st, reg->trigger_register);
+ int value;
+ u8 bit;
+
+ value = at91_adc_get_trigger_value_by_name(idev,
+ st->trigger_list,
+ idev->trig->name);
+ if (value < 0)
+ return value;
+
+ if (state) {
+ st->buffer = kmalloc(idev->scan_bytes, GFP_KERNEL);
+ if (st->buffer == NULL)
+ return -ENOMEM;
+
+ at91_adc_writel(st, reg->trigger_register,
+ status | value);
+
+ for_each_set_bit(bit, buffer->scan_mask,
+ st->num_channels) {
+ struct iio_chan_spec const *chan = idev->channels + bit;
+ at91_adc_writel(st, AT91_ADC_CHER,
+ AT91_ADC_CH(chan->channel));
+ }
+
+ at91_adc_writel(st, AT91_ADC_IER, reg->drdy_mask);
+
+ } else {
+ at91_adc_writel(st, AT91_ADC_IDR, reg->drdy_mask);
+
+ at91_adc_writel(st, reg->trigger_register,
+ status & ~value);
+
+ for_each_set_bit(bit, buffer->scan_mask,
+ st->num_channels) {
+ struct iio_chan_spec const *chan = idev->channels + bit;
+ at91_adc_writel(st, AT91_ADC_CHDR,
+ AT91_ADC_CH(chan->channel));
+ }
+ kfree(st->buffer);
+ }
+
+ return 0;
+}
+
+static const struct iio_trigger_ops at91_adc_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &at91_adc_configure_trigger,
+};
+
+static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
+ struct at91_adc_trigger *trigger)
+{
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = iio_trigger_alloc("%s-dev%d-%s", idev->name,
+ idev->id, trigger->name);
+ if (trig == NULL)
+ return NULL;
+
+ trig->dev.parent = idev->dev.parent;
+ iio_trigger_set_drvdata(trig, idev);
+ trig->ops = &at91_adc_trigger_ops;
+
+ ret = iio_trigger_register(trig);
+ if (ret)
+ return NULL;
+
+ return trig;
+}
+
+static int at91_adc_trigger_init(struct iio_dev *idev)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ int i, ret;
+
+ st->trig = devm_kzalloc(&idev->dev,
+ st->trigger_number * sizeof(st->trig),
+ GFP_KERNEL);
+
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ for (i = 0; i < st->trigger_number; i++) {
+ if (st->trigger_list[i].is_external && !(st->use_external))
+ continue;
+
+ st->trig[i] = at91_adc_allocate_trigger(idev,
+ st->trigger_list + i);
+ if (st->trig[i] == NULL) {
+ dev_err(&idev->dev,
+ "Could not allocate trigger %d\n", i);
+ ret = -ENOMEM;
+ goto error_trigger;
+ }
+ }
+
+ return 0;
+
+error_trigger:
+ for (i--; i >= 0; i--) {
+ iio_trigger_unregister(st->trig[i]);
+ iio_trigger_free(st->trig[i]);
+ }
+error_ret:
+ return ret;
+}
+
+static void at91_adc_trigger_remove(struct iio_dev *idev)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ int i;
+
+ for (i = 0; i < st->trigger_number; i++) {
+ iio_trigger_unregister(st->trig[i]);
+ iio_trigger_free(st->trig[i]);
+ }
+}
+
+static int at91_adc_buffer_init(struct iio_dev *idev)
+{
+ return iio_triggered_buffer_setup(idev, &iio_pollfunc_store_time,
+ &at91_adc_trigger_handler, NULL);
+}
+
+static void at91_adc_buffer_remove(struct iio_dev *idev)
+{
+ iio_triggered_buffer_cleanup(idev);
+}
+
+static int at91_adc_read_raw(struct iio_dev *idev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+
+ at91_adc_writel(st, AT91_ADC_CHER,
+ AT91_ADC_CH(chan->channel));
+ at91_adc_writel(st, AT91_ADC_IER, st->registers->drdy_mask);
+ at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_START);
+
+ ret = wait_event_interruptible_timeout(st->wq_data_avail,
+ st->done,
+ msecs_to_jiffies(1000));
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+
+ *val = st->last_value;
+
+ at91_adc_writel(st, AT91_ADC_CHDR,
+ AT91_ADC_CH(chan->channel));
+ at91_adc_writel(st, AT91_ADC_IDR, st->registers->drdy_mask);
+
+ st->last_value = 0;
+ st->done = false;
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int at91_adc_of_get_resolution(struct at91_adc_state *st,
+ struct platform_device *pdev)
+{
+ struct iio_dev *idev = iio_priv_to_dev(st);
+ struct device_node *np = pdev->dev.of_node;
+ int count, i, ret = 0;
+ char *res_name, *s;
+ u32 *resolutions;
+
+ count = of_property_count_strings(np, "atmel,adc-res-names");
+ if (count < 2) {
+ dev_err(&idev->dev, "You must specified at least two resolution names for "
+ "adc-res-names property in the DT\n");
+ return count;
+ }
+
+ resolutions = kmalloc(count * sizeof(*resolutions), GFP_KERNEL);
+ if (!resolutions)
+ return -ENOMEM;
+
+ if (of_property_read_u32_array(np, "atmel,adc-res", resolutions, count)) {
+ dev_err(&idev->dev, "Missing adc-res property in the DT.\n");
+ ret = -ENODEV;
+ goto ret;
+ }
+
+ if (of_property_read_string(np, "atmel,adc-use-res", (const char **)&res_name))
+ res_name = "highres";
+
+ for (i = 0; i < count; i++) {
+ if (of_property_read_string_index(np, "atmel,adc-res-names", i, (const char **)&s))
+ continue;
+
+ if (strcmp(res_name, s))
+ continue;
+
+ st->res = resolutions[i];
+ if (!strcmp(res_name, "lowres"))
+ st->low_res = true;
+ else
+ st->low_res = false;
+
+ dev_info(&idev->dev, "Resolution used: %u bits\n", st->res);
+ goto ret;
+ }
+
+ dev_err(&idev->dev, "There is no resolution for %s\n", res_name);
+
+ret:
+ kfree(resolutions);
+ return ret;
+}
+
+static int at91_adc_probe_dt(struct at91_adc_state *st,
+ struct platform_device *pdev)
+{
+ struct iio_dev *idev = iio_priv_to_dev(st);
+ struct device_node *node = pdev->dev.of_node;
+ struct device_node *trig_node;
+ int i = 0, ret;
+ u32 prop;
+
+ if (!node)
+ return -EINVAL;
+
+ st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers");
+
+ if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) {
+ dev_err(&idev->dev, "Missing adc-channels-used property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ st->channels_mask = prop;
+
+ if (of_property_read_u32(node, "atmel,adc-num-channels", &prop)) {
+ dev_err(&idev->dev, "Missing adc-num-channels property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ st->num_channels = prop;
+
+ st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode");
+
+ if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) {
+ dev_err(&idev->dev, "Missing adc-startup-time property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ st->startup_time = prop;
+
+ prop = 0;
+ of_property_read_u32(node, "atmel,adc-sample-hold-time", &prop);
+ st->sample_hold_time = prop;
+
+ if (of_property_read_u32(node, "atmel,adc-vref", &prop)) {
+ dev_err(&idev->dev, "Missing adc-vref property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ st->vref_mv = prop;
+
+ ret = at91_adc_of_get_resolution(st, pdev);
+ if (ret)
+ goto error_ret;
+
+ st->registers = devm_kzalloc(&idev->dev,
+ sizeof(struct at91_adc_reg_desc),
+ GFP_KERNEL);
+ if (!st->registers) {
+ dev_err(&idev->dev, "Could not allocate register memory.\n");
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ if (of_property_read_u32(node, "atmel,adc-channel-base", &prop)) {
+ dev_err(&idev->dev, "Missing adc-channel-base property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ st->registers->channel_base = prop;
+
+ if (of_property_read_u32(node, "atmel,adc-drdy-mask", &prop)) {
+ dev_err(&idev->dev, "Missing adc-drdy-mask property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ st->registers->drdy_mask = prop;
+
+ if (of_property_read_u32(node, "atmel,adc-status-register", &prop)) {
+ dev_err(&idev->dev, "Missing adc-status-register property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ st->registers->status_register = prop;
+
+ if (of_property_read_u32(node, "atmel,adc-trigger-register", &prop)) {
+ dev_err(&idev->dev, "Missing adc-trigger-register property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ st->registers->trigger_register = prop;
+
+ st->trigger_number = of_get_child_count(node);
+ st->trigger_list = devm_kzalloc(&idev->dev, st->trigger_number *
+ sizeof(struct at91_adc_trigger),
+ GFP_KERNEL);
+ if (!st->trigger_list) {
+ dev_err(&idev->dev, "Could not allocate trigger list memory.\n");
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ for_each_child_of_node(node, trig_node) {
+ struct at91_adc_trigger *trig = st->trigger_list + i;
+ const char *name;
+
+ if (of_property_read_string(trig_node, "trigger-name", &name)) {
+ dev_err(&idev->dev, "Missing trigger-name property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ trig->name = name;
+
+ if (of_property_read_u32(trig_node, "trigger-value", &prop)) {
+ dev_err(&idev->dev, "Missing trigger-value property in the DT.\n");
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ trig->value = prop;
+ trig->is_external = of_property_read_bool(trig_node, "trigger-external");
+ i++;
+ }
+
+ return 0;
+
+error_ret:
+ return ret;
+}
+
+static int at91_adc_probe_pdata(struct at91_adc_state *st,
+ struct platform_device *pdev)
+{
+ struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return -EINVAL;
+
+ st->use_external = pdata->use_external_triggers;
+ st->vref_mv = pdata->vref;
+ st->channels_mask = pdata->channels_used;
+ st->num_channels = pdata->num_channels;
+ st->startup_time = pdata->startup_time;
+ st->trigger_number = pdata->trigger_number;
+ st->trigger_list = pdata->trigger_list;
+ st->registers = pdata->registers;
+
+ return 0;
+}
+
+static const struct iio_info at91_adc_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &at91_adc_read_raw,
+};
+
+static int at91_adc_probe(struct platform_device *pdev)
+{
+ unsigned int prsc, mstrclk, ticks, adc_clk, shtim;
+ int ret;
+ struct iio_dev *idev;
+ struct at91_adc_state *st;
+ struct resource *res;
+ u32 reg;
+
+ idev = iio_device_alloc(sizeof(struct at91_adc_state));
+ if (idev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ st = iio_priv(idev);
+
+ if (pdev->dev.of_node)
+ ret = at91_adc_probe_dt(st, pdev);
+ else
+ ret = at91_adc_probe_pdata(st, pdev);
+
+ if (ret) {
+ dev_err(&pdev->dev, "No platform data available.\n");
+ ret = -EINVAL;
+ goto error_free_device;
+ }
+
+ platform_set_drvdata(pdev, idev);
+
+ idev->dev.parent = &pdev->dev;
+ idev->name = dev_name(&pdev->dev);
+ idev->modes = INDIO_DIRECT_MODE;
+ idev->info = &at91_adc_info;
+
+ st->irq = platform_get_irq(pdev, 0);
+ if (st->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ ID is designated\n");
+ ret = -ENODEV;
+ goto error_free_device;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ st->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(st->reg_base)) {
+ ret = PTR_ERR(st->reg_base);
+ goto error_free_device;
+ }
+
+ /*
+ * Disable all IRQs before setting up the handler
+ */
+ at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST);
+ at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF);
+ ret = request_irq(st->irq,
+ at91_adc_eoc_trigger,
+ 0,
+ pdev->dev.driver->name,
+ idev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+ goto error_free_device;
+ }
+
+ st->clk = devm_clk_get(&pdev->dev, "adc_clk");
+ if (IS_ERR(st->clk)) {
+ dev_err(&pdev->dev, "Failed to get the clock.\n");
+ ret = PTR_ERR(st->clk);
+ goto error_free_irq;
+ }
+
+ ret = clk_prepare_enable(st->clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the clock.\n");
+ goto error_free_irq;
+ }
+
+ st->adc_clk = devm_clk_get(&pdev->dev, "adc_op_clk");
+ if (IS_ERR(st->adc_clk)) {
+ dev_err(&pdev->dev, "Failed to get the ADC clock.\n");
+ ret = PTR_ERR(st->adc_clk);
+ goto error_disable_clk;
+ }
+
+ ret = clk_prepare_enable(st->adc_clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the ADC clock.\n");
+ goto error_disable_clk;
+ }
+
+ /*
+ * Prescaler rate computation using the formula from the Atmel's
+ * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
+ * specified by the electrical characteristics of the board.
+ */
+ mstrclk = clk_get_rate(st->clk);
+ adc_clk = clk_get_rate(st->adc_clk);
+ prsc = (mstrclk / (2 * adc_clk)) - 1;
+
+ if (!st->startup_time) {
+ dev_err(&pdev->dev, "No startup time available.\n");
+ ret = -EINVAL;
+ goto error_disable_adc_clk;
+ }
+
+ /*
+ * Number of ticks needed to cover the startup time of the ADC as
+ * defined in the electrical characteristics of the board, divided by 8.
+ * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
+ */
+ ticks = round_up((st->startup_time * adc_clk /
+ 1000000) - 1, 8) / 8;
+ /*
+ * a minimal Sample and Hold Time is necessary for the ADC to guarantee
+ * the best converted final value between two channels selection
+ * The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock
+ */
+ shtim = round_up((st->sample_hold_time * adc_clk /
+ 1000000) - 1, 1);
+
+ reg = AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL;
+ reg |= AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP;
+ if (st->low_res)
+ reg |= AT91_ADC_LOWRES;
+ if (st->sleep_mode)
+ reg |= AT91_ADC_SLEEP;
+ reg |= AT91_ADC_SHTIM_(shtim) & AT91_ADC_SHTIM;
+ at91_adc_writel(st, AT91_ADC_MR, reg);
+
+ /* Setup the ADC channels available on the board */
+ ret = at91_adc_channel_init(idev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't initialize the channels.\n");
+ goto error_disable_adc_clk;
+ }
+
+ init_waitqueue_head(&st->wq_data_avail);
+ mutex_init(&st->lock);
+
+ ret = at91_adc_buffer_init(idev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
+ goto error_disable_adc_clk;
+ }
+
+ ret = at91_adc_trigger_init(idev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
+ goto error_unregister_buffer;
+ }
+
+ ret = iio_device_register(idev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ goto error_remove_triggers;
+ }
+
+ return 0;
+
+error_remove_triggers:
+ at91_adc_trigger_remove(idev);
+error_unregister_buffer:
+ at91_adc_buffer_remove(idev);
+error_disable_adc_clk:
+ clk_disable_unprepare(st->adc_clk);
+error_disable_clk:
+ clk_disable_unprepare(st->clk);
+error_free_irq:
+ free_irq(st->irq, idev);
+error_free_device:
+ iio_device_free(idev);
+error_ret:
+ return ret;
+}
+
+static int at91_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *idev = platform_get_drvdata(pdev);
+ struct at91_adc_state *st = iio_priv(idev);
+
+ iio_device_unregister(idev);
+ at91_adc_trigger_remove(idev);
+ at91_adc_buffer_remove(idev);
+ clk_disable_unprepare(st->adc_clk);
+ clk_disable_unprepare(st->clk);
+ free_irq(st->irq, idev);
+ iio_device_free(idev);
+
+ return 0;
+}
+
+static const struct of_device_id at91_adc_dt_ids[] = {
+ { .compatible = "atmel,at91sam9260-adc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, at91_adc_dt_ids);
+
+static struct platform_driver at91_adc_driver = {
+ .probe = at91_adc_probe,
+ .remove = at91_adc_remove,
+ .driver = {
+ .name = "at91_adc",
+ .of_match_table = of_match_ptr(at91_adc_dt_ids),
+ },
+};
+
+module_platform_driver(at91_adc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
new file mode 100644
index 00000000..b3d03d33
--- /dev/null
+++ b/drivers/iio/adc/exynos_adc.c
@@ -0,0 +1,452 @@
+/*
+ * exynos_adc.c - Support for ADC in EXYNOS SoCs
+ *
+ * 8 ~ 10 channel, 10/12-bit ADC
+ *
+ * Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_platform.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+enum adc_version {
+ ADC_V1,
+ ADC_V2
+};
+
+/* EXYNOS4412/5250 ADC_V1 registers definitions */
+#define ADC_V1_CON(x) ((x) + 0x00)
+#define ADC_V1_DLY(x) ((x) + 0x08)
+#define ADC_V1_DATX(x) ((x) + 0x0C)
+#define ADC_V1_INTCLR(x) ((x) + 0x18)
+#define ADC_V1_MUX(x) ((x) + 0x1c)
+
+/* Future ADC_V2 registers definitions */
+#define ADC_V2_CON1(x) ((x) + 0x00)
+#define ADC_V2_CON2(x) ((x) + 0x04)
+#define ADC_V2_STAT(x) ((x) + 0x08)
+#define ADC_V2_INT_EN(x) ((x) + 0x10)
+#define ADC_V2_INT_ST(x) ((x) + 0x14)
+#define ADC_V2_VER(x) ((x) + 0x20)
+
+/* Bit definitions for ADC_V1 */
+#define ADC_V1_CON_RES (1u << 16)
+#define ADC_V1_CON_PRSCEN (1u << 14)
+#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6)
+#define ADC_V1_CON_STANDBY (1u << 2)
+
+/* Bit definitions for ADC_V2 */
+#define ADC_V2_CON1_SOFT_RESET (1u << 2)
+
+#define ADC_V2_CON2_OSEL (1u << 10)
+#define ADC_V2_CON2_ESEL (1u << 9)
+#define ADC_V2_CON2_HIGHF (1u << 8)
+#define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4)
+#define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0)
+#define ADC_V2_CON2_ACH_MASK 0xF
+
+#define MAX_ADC_V2_CHANNELS 10
+#define MAX_ADC_V1_CHANNELS 8
+
+/* Bit definitions common for ADC_V1 and ADC_V2 */
+#define ADC_CON_EN_START (1u << 0)
+#define ADC_DATX_MASK 0xFFF
+
+#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(1000))
+
+struct exynos_adc {
+ void __iomem *regs;
+ void __iomem *enable_reg;
+ struct clk *clk;
+ unsigned int irq;
+ struct regulator *vdd;
+
+ struct completion completion;
+
+ u32 value;
+ unsigned int version;
+};
+
+static const struct of_device_id exynos_adc_match[] = {
+ { .compatible = "samsung,exynos-adc-v1", .data = (void *)ADC_V1 },
+ { .compatible = "samsung,exynos-adc-v2", .data = (void *)ADC_V2 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_adc_match);
+
+static inline unsigned int exynos_adc_get_version(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(exynos_adc_match, pdev->dev.of_node);
+ return (unsigned int)match->data;
+}
+
+static int exynos_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct exynos_adc *info = iio_priv(indio_dev);
+ unsigned long timeout;
+ u32 con1, con2;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ /* Select the channel to be used and Trigger conversion */
+ if (info->version == ADC_V2) {
+ con2 = readl(ADC_V2_CON2(info->regs));
+ con2 &= ~ADC_V2_CON2_ACH_MASK;
+ con2 |= ADC_V2_CON2_ACH_SEL(chan->address);
+ writel(con2, ADC_V2_CON2(info->regs));
+
+ con1 = readl(ADC_V2_CON1(info->regs));
+ writel(con1 | ADC_CON_EN_START,
+ ADC_V2_CON1(info->regs));
+ } else {
+ writel(chan->address, ADC_V1_MUX(info->regs));
+
+ con1 = readl(ADC_V1_CON(info->regs));
+ writel(con1 | ADC_CON_EN_START,
+ ADC_V1_CON(info->regs));
+ }
+
+ timeout = wait_for_completion_interruptible_timeout
+ (&info->completion, EXYNOS_ADC_TIMEOUT);
+ *val = info->value;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ return IIO_VAL_INT;
+}
+
+static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
+{
+ struct exynos_adc *info = (struct exynos_adc *)dev_id;
+
+ /* Read value */
+ info->value = readl(ADC_V1_DATX(info->regs)) &
+ ADC_DATX_MASK;
+ /* clear irq */
+ if (info->version == ADC_V2)
+ writel(1, ADC_V2_INT_ST(info->regs));
+ else
+ writel(1, ADC_V1_INTCLR(info->regs));
+
+ complete(&info->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int exynos_adc_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct exynos_adc *info = iio_priv(indio_dev);
+
+ if (readval == NULL)
+ return -EINVAL;
+
+ *readval = readl(info->regs + reg);
+
+ return 0;
+}
+
+static const struct iio_info exynos_adc_iio_info = {
+ .read_raw = &exynos_read_raw,
+ .debugfs_reg_access = &exynos_adc_reg_access,
+ .driver_module = THIS_MODULE,
+};
+
+#define ADC_CHANNEL(_index, _id) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .address = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .datasheet_name = _id, \
+}
+
+static const struct iio_chan_spec exynos_adc_iio_channels[] = {
+ ADC_CHANNEL(0, "adc0"),
+ ADC_CHANNEL(1, "adc1"),
+ ADC_CHANNEL(2, "adc2"),
+ ADC_CHANNEL(3, "adc3"),
+ ADC_CHANNEL(4, "adc4"),
+ ADC_CHANNEL(5, "adc5"),
+ ADC_CHANNEL(6, "adc6"),
+ ADC_CHANNEL(7, "adc7"),
+ ADC_CHANNEL(8, "adc8"),
+ ADC_CHANNEL(9, "adc9"),
+};
+
+static int exynos_adc_remove_devices(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static void exynos_adc_hw_init(struct exynos_adc *info)
+{
+ u32 con1, con2;
+
+ if (info->version == ADC_V2) {
+ con1 = ADC_V2_CON1_SOFT_RESET;
+ writel(con1, ADC_V2_CON1(info->regs));
+
+ con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
+ ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0);
+ writel(con2, ADC_V2_CON2(info->regs));
+
+ /* Enable interrupts */
+ writel(1, ADC_V2_INT_EN(info->regs));
+ } else {
+ /* set default prescaler values and Enable prescaler */
+ con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
+
+ /* Enable 12-bit ADC resolution */
+ con1 |= ADC_V1_CON_RES;
+ writel(con1, ADC_V1_CON(info->regs));
+ }
+}
+
+static int exynos_adc_probe(struct platform_device *pdev)
+{
+ struct exynos_adc *info = NULL;
+ struct device_node *np = pdev->dev.of_node;
+ struct iio_dev *indio_dev = NULL;
+ struct resource *mem;
+ int ret = -ENODEV;
+ int irq;
+
+ if (!np)
+ return ret;
+
+ indio_dev = iio_device_alloc(sizeof(struct exynos_adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->regs = devm_request_and_ioremap(&pdev->dev, mem);
+ if (!info->regs) {
+ ret = -ENOMEM;
+ goto err_iio;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ info->enable_reg = devm_request_and_ioremap(&pdev->dev, mem);
+ if (!info->enable_reg) {
+ ret = -ENOMEM;
+ goto err_iio;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ ret = irq;
+ goto err_iio;
+ }
+
+ info->irq = irq;
+
+ init_completion(&info->completion);
+
+ ret = request_irq(info->irq, exynos_adc_isr,
+ 0, dev_name(&pdev->dev), info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+ info->irq);
+ goto err_iio;
+ }
+
+ writel(1, info->enable_reg);
+
+ info->clk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
+ PTR_ERR(info->clk));
+ ret = PTR_ERR(info->clk);
+ goto err_irq;
+ }
+
+ info->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(info->vdd)) {
+ dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
+ PTR_ERR(info->vdd));
+ ret = PTR_ERR(info->vdd);
+ goto err_irq;
+ }
+
+ info->version = exynos_adc_get_version(pdev);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &exynos_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = exynos_adc_iio_channels;
+
+ if (info->version == ADC_V1)
+ indio_dev->num_channels = MAX_ADC_V1_CHANNELS;
+ else
+ indio_dev->num_channels = MAX_ADC_V2_CHANNELS;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_irq;
+
+ ret = regulator_enable(info->vdd);
+ if (ret)
+ goto err_iio_dev;
+
+ clk_prepare_enable(info->clk);
+
+ exynos_adc_hw_init(info);
+
+ ret = of_platform_populate(np, exynos_adc_match, NULL, &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed adding child nodes\n");
+ goto err_of_populate;
+ }
+
+ return 0;
+
+err_of_populate:
+ device_for_each_child(&pdev->dev, NULL,
+ exynos_adc_remove_devices);
+ regulator_disable(info->vdd);
+ clk_disable_unprepare(info->clk);
+err_iio_dev:
+ iio_device_unregister(indio_dev);
+err_irq:
+ free_irq(info->irq, info);
+err_iio:
+ iio_device_free(indio_dev);
+ return ret;
+}
+
+static int exynos_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct exynos_adc *info = iio_priv(indio_dev);
+
+ device_for_each_child(&pdev->dev, NULL,
+ exynos_adc_remove_devices);
+ regulator_disable(info->vdd);
+ clk_disable_unprepare(info->clk);
+ writel(0, info->enable_reg);
+ iio_device_unregister(indio_dev);
+ free_irq(info->irq, info);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int exynos_adc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct exynos_adc *info = iio_priv(indio_dev);
+ u32 con;
+
+ if (info->version == ADC_V2) {
+ con = readl(ADC_V2_CON1(info->regs));
+ con &= ~ADC_CON_EN_START;
+ writel(con, ADC_V2_CON1(info->regs));
+ } else {
+ con = readl(ADC_V1_CON(info->regs));
+ con |= ADC_V1_CON_STANDBY;
+ writel(con, ADC_V1_CON(info->regs));
+ }
+
+ clk_disable_unprepare(info->clk);
+ writel(0, info->enable_reg);
+ regulator_disable(info->vdd);
+
+ return 0;
+}
+
+static int exynos_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct exynos_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(info->vdd);
+ if (ret)
+ return ret;
+
+ writel(1, info->enable_reg);
+ clk_prepare_enable(info->clk);
+
+ exynos_adc_hw_init(info);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops,
+ exynos_adc_suspend,
+ exynos_adc_resume);
+
+static struct platform_driver exynos_adc_driver = {
+ .probe = exynos_adc_probe,
+ .remove = exynos_adc_remove,
+ .driver = {
+ .name = "exynos-adc",
+ .owner = THIS_MODULE,
+ .of_match_table = exynos_adc_match,
+ .pm = &exynos_adc_pm_ops,
+ },
+};
+
+module_platform_driver(exynos_adc_driver);
+
+MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c
new file mode 100644
index 00000000..62bc39e9
--- /dev/null
+++ b/drivers/iio/adc/lp8788_adc.c
@@ -0,0 +1,258 @@
+/*
+ * TI LP8788 MFD - ADC driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * 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/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/mfd/lp8788.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* register address */
+#define LP8788_ADC_CONF 0x60
+#define LP8788_ADC_RAW 0x61
+#define LP8788_ADC_DONE 0x63
+
+#define ADC_CONV_START 1
+
+struct lp8788_adc {
+ struct lp8788 *lp;
+ struct iio_map *map;
+ struct mutex lock;
+};
+
+static const int lp8788_scale[LPADC_MAX] = {
+ [LPADC_VBATT_5P5] = 1343101,
+ [LPADC_VIN_CHG] = 3052503,
+ [LPADC_IBATT] = 610500,
+ [LPADC_IC_TEMP] = 61050,
+ [LPADC_VBATT_6P0] = 1465201,
+ [LPADC_VBATT_5P0] = 1221001,
+ [LPADC_ADC1] = 610500,
+ [LPADC_ADC2] = 610500,
+ [LPADC_VDD] = 1025641,
+ [LPADC_VCOIN] = 757020,
+ [LPADC_ADC3] = 610500,
+ [LPADC_ADC4] = 610500,
+};
+
+static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id,
+ int *val)
+{
+ unsigned int msb;
+ unsigned int lsb;
+ unsigned int result;
+ u8 data;
+ u8 rawdata[2];
+ int size = ARRAY_SIZE(rawdata);
+ int retry = 5;
+ int ret;
+
+ data = (id << 1) | ADC_CONV_START;
+ ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data);
+ if (ret)
+ goto err_io;
+
+ /* retry until adc conversion is done */
+ data = 0;
+ while (retry--) {
+ usleep_range(100, 200);
+
+ ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data);
+ if (ret)
+ goto err_io;
+
+ /* conversion done */
+ if (data)
+ break;
+ }
+
+ ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size);
+ if (ret)
+ goto err_io;
+
+ msb = (rawdata[0] << 4) & 0x00000ff0;
+ lsb = (rawdata[1] >> 4) & 0x0000000f;
+ result = msb | lsb;
+ *val = result;
+
+ return 0;
+
+err_io:
+ return ret;
+}
+
+static int lp8788_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lp8788_adc *adc = iio_priv(indio_dev);
+ enum lp8788_adc_id id = chan->channel;
+ int ret;
+
+ mutex_lock(&adc->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = lp8788_scale[id] / 1000000;
+ *val2 = lp8788_scale[id] % 1000000;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static const struct iio_info lp8788_adc_info = {
+ .read_raw = &lp8788_adc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define LP8788_CHAN(_id, _type) { \
+ .type = _type, \
+ .indexed = 1, \
+ .channel = LPADC_##_id, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = #_id, \
+}
+
+static const struct iio_chan_spec lp8788_adc_channels[] = {
+ [LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE),
+ [LPADC_VIN_CHG] = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE),
+ [LPADC_IBATT] = LP8788_CHAN(IBATT, IIO_CURRENT),
+ [LPADC_IC_TEMP] = LP8788_CHAN(IC_TEMP, IIO_TEMP),
+ [LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE),
+ [LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE),
+ [LPADC_ADC1] = LP8788_CHAN(ADC1, IIO_VOLTAGE),
+ [LPADC_ADC2] = LP8788_CHAN(ADC2, IIO_VOLTAGE),
+ [LPADC_VDD] = LP8788_CHAN(VDD, IIO_VOLTAGE),
+ [LPADC_VCOIN] = LP8788_CHAN(VCOIN, IIO_VOLTAGE),
+ [LPADC_ADC3] = LP8788_CHAN(ADC3, IIO_VOLTAGE),
+ [LPADC_ADC4] = LP8788_CHAN(ADC4, IIO_VOLTAGE),
+};
+
+/* default maps used by iio consumer (lp8788-charger driver) */
+static struct iio_map lp8788_default_iio_maps[] = {
+ {
+ .consumer_dev_name = "lp8788-charger",
+ .consumer_channel = "lp8788_vbatt_5p0",
+ .adc_channel_label = "VBATT_5P0",
+ },
+ {
+ .consumer_dev_name = "lp8788-charger",
+ .consumer_channel = "lp8788_adc1",
+ .adc_channel_label = "ADC1",
+ },
+ { }
+};
+
+static int lp8788_iio_map_register(struct iio_dev *indio_dev,
+ struct lp8788_platform_data *pdata,
+ struct lp8788_adc *adc)
+{
+ struct iio_map *map;
+ int ret;
+
+ map = (!pdata || !pdata->adc_pdata) ?
+ lp8788_default_iio_maps : pdata->adc_pdata;
+
+ ret = iio_map_array_register(indio_dev, map);
+ if (ret) {
+ dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
+ return ret;
+ }
+
+ adc->map = map;
+ return 0;
+}
+
+static int lp8788_adc_probe(struct platform_device *pdev)
+{
+ struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
+ struct iio_dev *indio_dev;
+ struct lp8788_adc *adc;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ 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;
+
+ mutex_init(&adc->lock);
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &lp8788_adc_info;
+ indio_dev->channels = lp8788_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "iio dev register err: %d\n", ret);
+ goto err_iio_device;
+ }
+
+ return 0;
+
+err_iio_device:
+ iio_map_array_unregister(indio_dev);
+err_iio_map:
+ iio_device_free(indio_dev);
+ return ret;
+}
+
+static int lp8788_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+ iio_map_array_unregister(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver lp8788_adc_driver = {
+ .probe = lp8788_adc_probe,
+ .remove = lp8788_adc_remove,
+ .driver = {
+ .name = LP8788_DEV_ADC,
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(lp8788_adc_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lp8788-adc");
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
new file mode 100644
index 00000000..b2b5dcbf
--- /dev/null
+++ b/drivers/iio/adc/max1363.c
@@ -0,0 +1,1667 @@
+ /*
+ * iio/adc/max1363.c
+ * Copyright (C) 2008-2010 Jonathan Cameron
+ *
+ * based on linux/drivers/i2c/chips/max123x
+ * Copyright (C) 2002-2004 Stefan Eletzhofer
+ *
+ * based on linux/drivers/acron/char/pcf8583.c
+ * Copyright (C) 2000 Russell King
+ *
+ * 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.
+ *
+ * max1363.c
+ *
+ * Partial support for max1363 and similar chips.
+ *
+ * Not currently implemented.
+ *
+ * - Control of internal reference.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define MAX1363_SETUP_BYTE(a) ((a) | 0x80)
+
+/* There is a fair bit more defined here than currently
+ * used, but the intention is to support everything these
+ * chips do in the long run */
+
+/* see data sheets */
+/* max1363 and max1236, max1237, max1238, max1239 */
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD 0x00
+#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF 0x20
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT 0x40
+#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT 0x60
+#define MAX1363_SETUP_POWER_UP_INT_REF 0x10
+#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00
+
+/* think about including max11600 etc - more settings */
+#define MAX1363_SETUP_EXT_CLOCK 0x08
+#define MAX1363_SETUP_INT_CLOCK 0x00
+#define MAX1363_SETUP_UNIPOLAR 0x00
+#define MAX1363_SETUP_BIPOLAR 0x04
+#define MAX1363_SETUP_RESET 0x00
+#define MAX1363_SETUP_NORESET 0x02
+/* max1363 only - though don't care on others.
+ * For now monitor modes are not implemented as the relevant
+ * line is not connected on my test board.
+ * The definitions are here as I intend to add this soon.
+ */
+#define MAX1363_SETUP_MONITOR_SETUP 0x01
+
+/* Specific to the max1363 */
+#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4))
+#define MAX1363_MON_INT_ENABLE 0x01
+
+/* defined for readability reasons */
+/* All chips */
+#define MAX1363_CONFIG_BYTE(a) ((a))
+
+#define MAX1363_CONFIG_SE 0x01
+#define MAX1363_CONFIG_DE 0x00
+#define MAX1363_CONFIG_SCAN_TO_CS 0x00
+#define MAX1363_CONFIG_SCAN_SINGLE_8 0x20
+#define MAX1363_CONFIG_SCAN_MONITOR_MODE 0x40
+#define MAX1363_CONFIG_SCAN_SINGLE_1 0x60
+/* max123{6-9} only */
+#define MAX1236_SCAN_MID_TO_CHANNEL 0x40
+
+/* max1363 only - merely part of channel selects or don't care for others */
+#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18
+
+#define MAX1363_CHANNEL_SEL(a) ((a) << 1)
+
+/* max1363 strictly 0x06 - but doesn't matter */
+#define MAX1363_CHANNEL_SEL_MASK 0x1E
+#define MAX1363_SCAN_MASK 0x60
+#define MAX1363_SE_DE_MASK 0x01
+
+#define MAX1363_MAX_CHANNELS 25
+/**
+ * struct max1363_mode - scan mode information
+ * @conf: The corresponding value of the configuration register
+ * @modemask: Bit mask corresponding to channels enabled in this mode
+ */
+struct max1363_mode {
+ int8_t conf;
+ DECLARE_BITMAP(modemask, MAX1363_MAX_CHANNELS);
+};
+
+/* This must be maintained along side the max1363_mode_table in max1363_core */
+enum max1363_modes {
+ /* Single read of a single channel */
+ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11,
+ /* Differential single read */
+ d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+ d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+ /* Scan to channel and mid to channel where overlapping */
+ s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6,
+ s6to7, s0to7, s6to8, s0to8, s6to9,
+ s0to9, s6to10, s0to10, s6to11, s0to11,
+ /* Differential scan to channel and mid to channel where overlapping */
+ d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9,
+ d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2,
+ d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8,
+ d7m6to11m10, d1m0to11m10,
+};
+
+/**
+ * struct max1363_chip_info - chip specifc information
+ * @info: iio core function callbacks structure
+ * @channels: channel specification
+ * @num_channels: number of channels
+ * @mode_list: array of available scan modes
+ * @default_mode: the scan mode in which the chip starts up
+ * @int_vref_mv: the internal reference voltage
+ * @num_modes: number of modes
+ * @bits: accuracy of the adc in bits
+ */
+struct max1363_chip_info {
+ const struct iio_info *info;
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ const enum max1363_modes *mode_list;
+ enum max1363_modes default_mode;
+ u16 int_vref_mv;
+ u8 num_modes;
+ u8 bits;
+};
+
+/**
+ * struct max1363_state - driver instance specific data
+ * @client: i2c_client
+ * @setupbyte: cache of current device setup byte
+ * @configbyte: cache of current device config byte
+ * @chip_info: chip model specific constants, available modes, etc.
+ * @current_mode: the scan mode of this chip
+ * @requestedmask: a valid requested set of channels
+ * @reg: supply regulator
+ * @monitor_on: whether monitor mode is enabled
+ * @monitor_speed: parameter corresponding to device monitor speed setting
+ * @mask_high: bitmask for enabled high thresholds
+ * @mask_low: bitmask for enabled low thresholds
+ * @thresh_high: high threshold values
+ * @thresh_low: low threshold values
+ * @vref: Reference voltage regulator
+ * @vref_uv: Actual (external or internal) reference voltage
+ */
+struct max1363_state {
+ struct i2c_client *client;
+ u8 setupbyte;
+ u8 configbyte;
+ const struct max1363_chip_info *chip_info;
+ const struct max1363_mode *current_mode;
+ u32 requestedmask;
+ struct regulator *reg;
+
+ /* Using monitor modes and buffer at the same time is
+ currently not supported */
+ bool monitor_on;
+ unsigned int monitor_speed:3;
+ u8 mask_high;
+ u8 mask_low;
+ /* 4x unipolar first then the fours bipolar ones */
+ s16 thresh_high[8];
+ s16 thresh_low[8];
+ struct regulator *vref;
+ u32 vref_uv;
+};
+
+#define MAX1363_MODE_SINGLE(_num, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_SINGLE_1 \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask, \
+ }
+
+#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_TO_CS \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask, \
+ }
+
+/* note not available for max1363 hence naming */
+#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1236_SCAN_MID_TO_CHANNEL \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask \
+}
+
+#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_nump) \
+ | MAX1363_CONFIG_SCAN_SINGLE_1 \
+ | MAX1363_CONFIG_DE, \
+ .modemask[0] = _mask \
+ }
+
+/* Can't think how to automate naming so specify for now */
+#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_TO_CS \
+ | MAX1363_CONFIG_DE, \
+ .modemask[0] = _mask \
+ }
+
+/* note only available for max1363 hence naming */
+#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1236_SCAN_MID_TO_CHANNEL \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask \
+}
+
+static const struct max1363_mode max1363_mode_table[] = {
+ /* All of the single channel options first */
+ MAX1363_MODE_SINGLE(0, 1 << 0),
+ MAX1363_MODE_SINGLE(1, 1 << 1),
+ MAX1363_MODE_SINGLE(2, 1 << 2),
+ MAX1363_MODE_SINGLE(3, 1 << 3),
+ MAX1363_MODE_SINGLE(4, 1 << 4),
+ MAX1363_MODE_SINGLE(5, 1 << 5),
+ MAX1363_MODE_SINGLE(6, 1 << 6),
+ MAX1363_MODE_SINGLE(7, 1 << 7),
+ MAX1363_MODE_SINGLE(8, 1 << 8),
+ MAX1363_MODE_SINGLE(9, 1 << 9),
+ MAX1363_MODE_SINGLE(10, 1 << 10),
+ MAX1363_MODE_SINGLE(11, 1 << 11),
+
+ MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12),
+ MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13),
+ MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14),
+ MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15),
+ MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16),
+ MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17),
+ MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18),
+ MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19),
+ MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20),
+ MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21),
+ MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22),
+ MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23),
+
+ /* The multichannel scans next */
+ MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003),
+ MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C),
+ MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F),
+ MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F),
+ MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F),
+ MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF),
+
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000),
+};
+
+static const struct max1363_mode
+*max1363_match_mode(const unsigned long *mask,
+ const struct max1363_chip_info *ci)
+{
+ int i;
+ if (mask)
+ for (i = 0; i < ci->num_modes; i++)
+ if (bitmap_subset(mask,
+ max1363_mode_table[ci->mode_list[i]].
+ modemask,
+ MAX1363_MAX_CHANNELS))
+ return &max1363_mode_table[ci->mode_list[i]];
+ return NULL;
+}
+
+static int max1363_write_basic_config(struct i2c_client *client,
+ unsigned char d1,
+ unsigned char d2)
+{
+ u8 tx_buf[2] = {d1, d2};
+
+ return i2c_master_send(client, tx_buf, 2);
+}
+
+static int max1363_set_scan_mode(struct max1363_state *st)
+{
+ st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK
+ | MAX1363_SCAN_MASK
+ | MAX1363_SE_DE_MASK);
+ st->configbyte |= st->current_mode->conf;
+
+ return max1363_write_basic_config(st->client,
+ st->setupbyte,
+ st->configbyte);
+}
+
+static int max1363_read_single_chan(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ long m)
+{
+ int ret = 0;
+ s32 data;
+ u8 rxbuf[2];
+ struct max1363_state *st = iio_priv(indio_dev);
+ struct i2c_client *client = st->client;
+
+ mutex_lock(&indio_dev->mlock);
+ /*
+ * If monitor mode is enabled, the method for reading a single
+ * channel will have to be rather different and has not yet
+ * been implemented.
+ *
+ * Also, cannot read directly if buffered capture enabled.
+ */
+ if (st->monitor_on || iio_buffer_enabled(indio_dev)) {
+ ret = -EBUSY;
+ goto error_ret;
+ }
+
+ /* Check to see if current scan mode is correct */
+ if (st->current_mode != &max1363_mode_table[chan->address]) {
+ /* Update scan mode if needed */
+ st->current_mode = &max1363_mode_table[chan->address];
+ ret = max1363_set_scan_mode(st);
+ if (ret < 0)
+ goto error_ret;
+ }
+ if (st->chip_info->bits != 8) {
+ /* Get reading */
+ data = i2c_master_recv(client, rxbuf, 2);
+ if (data < 0) {
+ ret = data;
+ goto error_ret;
+ }
+ data = (rxbuf[1] | rxbuf[0] << 8) &
+ ((1 << st->chip_info->bits) - 1);
+ } else {
+ /* Get reading */
+ data = i2c_master_recv(client, rxbuf, 1);
+ if (data < 0) {
+ ret = data;
+ goto error_ret;
+ }
+ data = rxbuf[0];
+ }
+ *val = data;
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+
+}
+
+static int max1363_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+ int ret;
+ unsigned long scale_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = max1363_read_single_chan(indio_dev, chan, val, m);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = st->vref_uv >> st->chip_info->bits;
+ *val = scale_uv / 1000;
+ *val2 = (scale_uv % 1000) * 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Applies to max1363 */
+static const enum max1363_modes max1363_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ s0to1, s0to2, s0to3,
+ d0m1, d2m3, d1m0, d3m2,
+ d0m1to2m3, d1m0to3m2,
+};
+
+#define MAX1363_EV_M \
+ (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) \
+ | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING))
+
+#define MAX1363_CHAN_U(num, addr, si, bits, evmask) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = num, \
+ .address = addr, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = "AIN"#num, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = bits, \
+ .storagebits = (bits > 8) ? 16 : 8, \
+ .endianness = IIO_BE, \
+ }, \
+ .scan_index = si, \
+ .event_mask = evmask, \
+ }
+
+/* bipolar channel */
+#define MAX1363_CHAN_B(num, num2, addr, si, bits, evmask) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .differential = 1, \
+ .indexed = 1, \
+ .channel = num, \
+ .channel2 = num2, \
+ .address = addr, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = "AIN"#num"-AIN"#num2, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = bits, \
+ .storagebits = (bits > 8) ? 16 : 8, \
+ .endianness = IIO_BE, \
+ }, \
+ .scan_index = si, \
+ .event_mask = evmask, \
+ }
+
+#define MAX1363_4X_CHANS(bits, em) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits, em), \
+ MAX1363_CHAN_U(1, _s1, 1, bits, em), \
+ MAX1363_CHAN_U(2, _s2, 2, bits, em), \
+ MAX1363_CHAN_U(3, _s3, 3, bits, em), \
+ MAX1363_CHAN_B(0, 1, d0m1, 4, bits, em), \
+ MAX1363_CHAN_B(2, 3, d2m3, 5, bits, em), \
+ MAX1363_CHAN_B(1, 0, d1m0, 6, bits, em), \
+ MAX1363_CHAN_B(3, 2, d3m2, 7, bits, em), \
+ IIO_CHAN_SOFT_TIMESTAMP(8) \
+ }
+
+static const struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8, 0);
+static const struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10, 0);
+static const struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12, 0);
+static const struct iio_chan_spec max1361_channels[] =
+ MAX1363_4X_CHANS(10, MAX1363_EV_M);
+static const struct iio_chan_spec max1363_channels[] =
+ MAX1363_4X_CHANS(12, MAX1363_EV_M);
+
+/* Applies to max1236, max1237 */
+static const enum max1363_modes max1236_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ s0to1, s0to2, s0to3,
+ d0m1, d2m3, d1m0, d3m2,
+ d0m1to2m3, d1m0to3m2,
+ s2to3,
+};
+
+/* Applies to max1238, max1239 */
+static const enum max1363_modes max1238_mode_list[] = {
+ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11,
+ s0to1, s0to2, s0to3, s0to4, s0to5, s0to6,
+ s0to7, s0to8, s0to9, s0to10, s0to11,
+ d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+ d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+ d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11,
+ d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10,
+ s6to7, s6to8, s6to9, s6to10, s6to11,
+ d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10,
+};
+
+#define MAX1363_12X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits, 0), \
+ MAX1363_CHAN_U(1, _s1, 1, bits, 0), \
+ MAX1363_CHAN_U(2, _s2, 2, bits, 0), \
+ MAX1363_CHAN_U(3, _s3, 3, bits, 0), \
+ MAX1363_CHAN_U(4, _s4, 4, bits, 0), \
+ MAX1363_CHAN_U(5, _s5, 5, bits, 0), \
+ MAX1363_CHAN_U(6, _s6, 6, bits, 0), \
+ MAX1363_CHAN_U(7, _s7, 7, bits, 0), \
+ MAX1363_CHAN_U(8, _s8, 8, bits, 0), \
+ MAX1363_CHAN_U(9, _s9, 9, bits, 0), \
+ MAX1363_CHAN_U(10, _s10, 10, bits, 0), \
+ MAX1363_CHAN_U(11, _s11, 11, bits, 0), \
+ MAX1363_CHAN_B(0, 1, d0m1, 12, bits, 0), \
+ MAX1363_CHAN_B(2, 3, d2m3, 13, bits, 0), \
+ MAX1363_CHAN_B(4, 5, d4m5, 14, bits, 0), \
+ MAX1363_CHAN_B(6, 7, d6m7, 15, bits, 0), \
+ MAX1363_CHAN_B(8, 9, d8m9, 16, bits, 0), \
+ MAX1363_CHAN_B(10, 11, d10m11, 17, bits, 0), \
+ MAX1363_CHAN_B(1, 0, d1m0, 18, bits, 0), \
+ MAX1363_CHAN_B(3, 2, d3m2, 19, bits, 0), \
+ MAX1363_CHAN_B(5, 4, d5m4, 20, bits, 0), \
+ MAX1363_CHAN_B(7, 6, d7m6, 21, bits, 0), \
+ MAX1363_CHAN_B(9, 8, d9m8, 22, bits, 0), \
+ MAX1363_CHAN_B(11, 10, d11m10, 23, bits, 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(24) \
+ }
+static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8);
+static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10);
+static const struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12);
+
+static const enum max1363_modes max11607_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ s0to1, s0to2, s0to3,
+ s2to3,
+ d0m1, d2m3, d1m0, d3m2,
+ d0m1to2m3, d1m0to3m2,
+};
+
+static const enum max1363_modes max11608_mode_list[] = {
+ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7,
+ s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7,
+ s6to7,
+ d0m1, d2m3, d4m5, d6m7,
+ d1m0, d3m2, d5m4, d7m6,
+ d0m1to2m3, d0m1to4m5, d0m1to6m7,
+ d1m0to3m2, d1m0to5m4, d1m0to7m6,
+};
+
+#define MAX1363_8X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits, 0), \
+ MAX1363_CHAN_U(1, _s1, 1, bits, 0), \
+ MAX1363_CHAN_U(2, _s2, 2, bits, 0), \
+ MAX1363_CHAN_U(3, _s3, 3, bits, 0), \
+ MAX1363_CHAN_U(4, _s4, 4, bits, 0), \
+ MAX1363_CHAN_U(5, _s5, 5, bits, 0), \
+ MAX1363_CHAN_U(6, _s6, 6, bits, 0), \
+ MAX1363_CHAN_U(7, _s7, 7, bits, 0), \
+ MAX1363_CHAN_B(0, 1, d0m1, 8, bits, 0), \
+ MAX1363_CHAN_B(2, 3, d2m3, 9, bits, 0), \
+ MAX1363_CHAN_B(4, 5, d4m5, 10, bits, 0), \
+ MAX1363_CHAN_B(6, 7, d6m7, 11, bits, 0), \
+ MAX1363_CHAN_B(1, 0, d1m0, 12, bits, 0), \
+ MAX1363_CHAN_B(3, 2, d3m2, 13, bits, 0), \
+ MAX1363_CHAN_B(5, 4, d5m4, 14, bits, 0), \
+ MAX1363_CHAN_B(7, 6, d7m6, 15, bits, 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(16) \
+}
+static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8);
+static const struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10);
+static const struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12);
+
+static const enum max1363_modes max11644_mode_list[] = {
+ _s0, _s1, s0to1, d0m1, d1m0,
+};
+
+#define MAX1363_2X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits, 0), \
+ MAX1363_CHAN_U(1, _s1, 1, bits, 0), \
+ MAX1363_CHAN_B(0, 1, d0m1, 2, bits, 0), \
+ MAX1363_CHAN_B(1, 0, d1m0, 3, bits, 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(4) \
+ }
+
+static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10);
+static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12);
+
+enum { max1361,
+ max1362,
+ max1363,
+ max1364,
+ max1036,
+ max1037,
+ max1038,
+ max1039,
+ max1136,
+ max1137,
+ max1138,
+ max1139,
+ max1236,
+ max1237,
+ max1238,
+ max1239,
+ max11600,
+ max11601,
+ max11602,
+ max11603,
+ max11604,
+ max11605,
+ max11606,
+ max11607,
+ max11608,
+ max11609,
+ max11610,
+ max11611,
+ max11612,
+ max11613,
+ max11614,
+ max11615,
+ max11616,
+ max11617,
+ max11644,
+ max11645,
+ max11646,
+ max11647
+};
+
+static const int max1363_monitor_speeds[] = { 133000, 665000, 33300, 16600,
+ 8300, 4200, 2000, 1000 };
+
+static ssize_t max1363_monitor_show_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct max1363_state *st = iio_priv(dev_to_iio_dev(dev));
+ return sprintf(buf, "%d\n", max1363_monitor_speeds[st->monitor_speed]);
+}
+
+static ssize_t max1363_monitor_store_freq(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct max1363_state *st = iio_priv(indio_dev);
+ int i, ret;
+ unsigned long val;
+ bool found = false;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++)
+ if (val == max1363_monitor_speeds[i]) {
+ found = true;
+ break;
+ }
+ if (!found)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ st->monitor_speed = i;
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
+ max1363_monitor_show_freq,
+ max1363_monitor_store_freq);
+
+static IIO_CONST_ATTR(sampling_frequency_available,
+ "133000 665000 33300 16600 8300 4200 2000 1000");
+
+static int max1363_read_thresh(struct iio_dev *indio_dev,
+ u64 event_code,
+ int *val)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING)
+ *val = st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)];
+ else
+ *val = st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)];
+ return 0;
+}
+
+static int max1363_write_thresh(struct iio_dev *indio_dev,
+ u64 event_code,
+ int val)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+ /* make it handle signed correctly as well */
+ switch (st->chip_info->bits) {
+ case 10:
+ if (val > 0x3FF)
+ return -EINVAL;
+ break;
+ case 12:
+ if (val > 0xFFF)
+ return -EINVAL;
+ break;
+ }
+
+ switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+ case IIO_EV_DIR_FALLING:
+ st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val;
+ break;
+ case IIO_EV_DIR_RISING:
+ st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val;
+ break;
+ }
+
+ return 0;
+}
+
+static const u64 max1363_event_codes[] = {
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+};
+
+static irqreturn_t max1363_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct max1363_state *st = iio_priv(indio_dev);
+ s64 timestamp = iio_get_time_ns();
+ unsigned long mask, loc;
+ u8 rx;
+ u8 tx[2] = { st->setupbyte,
+ MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 };
+
+ i2c_master_recv(st->client, &rx, 1);
+ mask = rx;
+ for_each_set_bit(loc, &mask, 8)
+ iio_push_event(indio_dev, max1363_event_codes[loc], timestamp);
+ i2c_master_send(st->client, tx, 2);
+
+ return IRQ_HANDLED;
+}
+
+static int max1363_read_event_config(struct iio_dev *indio_dev,
+ u64 event_code)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+ int val;
+ int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
+
+ mutex_lock(&indio_dev->mlock);
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING)
+ val = (1 << number) & st->mask_low;
+ else
+ val = (1 << number) & st->mask_high;
+ mutex_unlock(&indio_dev->mlock);
+
+ return val;
+}
+
+static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
+{
+ u8 *tx_buf;
+ int ret, i = 3, j;
+ unsigned long numelements;
+ int len;
+ const long *modemask;
+
+ if (!enabled) {
+ /* transition to buffered capture is not currently supported */
+ st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP;
+ st->configbyte &= ~MAX1363_SCAN_MASK;
+ st->monitor_on = false;
+ return max1363_write_basic_config(st->client,
+ st->setupbyte,
+ st->configbyte);
+ }
+
+ /* Ensure we are in the relevant mode */
+ st->setupbyte |= MAX1363_SETUP_MONITOR_SETUP;
+ st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK
+ | MAX1363_SCAN_MASK
+ | MAX1363_SE_DE_MASK);
+ st->configbyte |= MAX1363_CONFIG_SCAN_MONITOR_MODE;
+ if ((st->mask_low | st->mask_high) & 0x0F) {
+ st->configbyte |= max1363_mode_table[s0to3].conf;
+ modemask = max1363_mode_table[s0to3].modemask;
+ } else if ((st->mask_low | st->mask_high) & 0x30) {
+ st->configbyte |= max1363_mode_table[d0m1to2m3].conf;
+ modemask = max1363_mode_table[d0m1to2m3].modemask;
+ } else {
+ st->configbyte |= max1363_mode_table[d1m0to3m2].conf;
+ modemask = max1363_mode_table[d1m0to3m2].modemask;
+ }
+ numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS);
+ len = 3 * numelements + 3;
+ tx_buf = kmalloc(len, GFP_KERNEL);
+ if (!tx_buf) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ tx_buf[0] = st->configbyte;
+ tx_buf[1] = st->setupbyte;
+ tx_buf[2] = (st->monitor_speed << 1);
+
+ /*
+ * So we need to do yet another bit of nefarious scan mode
+ * setup to match what we need.
+ */
+ for (j = 0; j < 8; j++)
+ if (test_bit(j, modemask)) {
+ /* Establish the mode is in the scan */
+ if (st->mask_low & (1 << j)) {
+ tx_buf[i] = (st->thresh_low[j] >> 4) & 0xFF;
+ tx_buf[i + 1] = (st->thresh_low[j] << 4) & 0xF0;
+ } else if (j < 4) {
+ tx_buf[i] = 0;
+ tx_buf[i + 1] = 0;
+ } else {
+ tx_buf[i] = 0x80;
+ tx_buf[i + 1] = 0;
+ }
+ if (st->mask_high & (1 << j)) {
+ tx_buf[i + 1] |=
+ (st->thresh_high[j] >> 8) & 0x0F;
+ tx_buf[i + 2] = st->thresh_high[j] & 0xFF;
+ } else if (j < 4) {
+ tx_buf[i + 1] |= 0x0F;
+ tx_buf[i + 2] = 0xFF;
+ } else {
+ tx_buf[i + 1] |= 0x07;
+ tx_buf[i + 2] = 0xFF;
+ }
+ i += 3;
+ }
+
+
+ ret = i2c_master_send(st->client, tx_buf, len);
+ if (ret < 0)
+ goto error_ret;
+ if (ret != len) {
+ ret = -EIO;
+ goto error_ret;
+ }
+
+ /*
+ * Now that we hopefully have sensible thresholds in place it is
+ * time to turn the interrupts on.
+ * It is unclear from the data sheet if this should be necessary
+ * (i.e. whether monitor mode setup is atomic) but it appears to
+ * be in practice.
+ */
+ tx_buf[0] = st->setupbyte;
+ tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0;
+ ret = i2c_master_send(st->client, tx_buf, 2);
+ if (ret < 0)
+ goto error_ret;
+ if (ret != 2) {
+ ret = -EIO;
+ goto error_ret;
+ }
+ ret = 0;
+ st->monitor_on = true;
+error_ret:
+
+ kfree(tx_buf);
+
+ return ret;
+}
+
+/*
+ * To keep this manageable we always use one of 3 scan modes.
+ * Scan 0...3, 0-1,2-3 and 1-0,3-2
+ */
+
+static inline int __max1363_check_event_mask(int thismask, int checkmask)
+{
+ int ret = 0;
+ /* Is it unipolar */
+ if (thismask < 4) {
+ if (checkmask & ~0x0F) {
+ ret = -EBUSY;
+ goto error_ret;
+ }
+ } else if (thismask < 6) {
+ if (checkmask & ~0x30) {
+ ret = -EBUSY;
+ goto error_ret;
+ }
+ } else if (checkmask & ~0xC0)
+ ret = -EBUSY;
+error_ret:
+ return ret;
+}
+
+static int max1363_write_event_config(struct iio_dev *indio_dev,
+ u64 event_code,
+ int state)
+{
+ int ret = 0;
+ struct max1363_state *st = iio_priv(indio_dev);
+ u16 unifiedmask;
+ int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
+
+ mutex_lock(&indio_dev->mlock);
+ unifiedmask = st->mask_low | st->mask_high;
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) {
+
+ if (state == 0)
+ st->mask_low &= ~(1 << number);
+ else {
+ ret = __max1363_check_event_mask((1 << number),
+ unifiedmask);
+ if (ret)
+ goto error_ret;
+ st->mask_low |= (1 << number);
+ }
+ } else {
+ if (state == 0)
+ st->mask_high &= ~(1 << number);
+ else {
+ ret = __max1363_check_event_mask((1 << number),
+ unifiedmask);
+ if (ret)
+ goto error_ret;
+ st->mask_high |= (1 << number);
+ }
+ }
+
+ max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low));
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+/*
+ * As with scan_elements, only certain sets of these can
+ * be combined.
+ */
+static struct attribute *max1363_event_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group max1363_event_attribute_group = {
+ .attrs = max1363_event_attributes,
+ .name = "events",
+};
+
+static int max1363_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+
+ /*
+ * Need to figure out the current mode based upon the requested
+ * scan mask in iio_dev
+ */
+ st->current_mode = max1363_match_mode(scan_mask, st->chip_info);
+ if (!st->current_mode)
+ return -EINVAL;
+ max1363_set_scan_mode(st);
+ return 0;
+}
+
+static const struct iio_info max1238_info = {
+ .read_raw = &max1363_read_raw,
+ .driver_module = THIS_MODULE,
+ .update_scan_mode = &max1363_update_scan_mode,
+};
+
+static const struct iio_info max1363_info = {
+ .read_event_value = &max1363_read_thresh,
+ .write_event_value = &max1363_write_thresh,
+ .read_event_config = &max1363_read_event_config,
+ .write_event_config = &max1363_write_event_config,
+ .read_raw = &max1363_read_raw,
+ .update_scan_mode = &max1363_update_scan_mode,
+ .driver_module = THIS_MODULE,
+ .event_attrs = &max1363_event_attribute_group,
+};
+
+/* max1363 and max1368 tested - rest from data sheet */
+static const struct max1363_chip_info max1363_chip_info_tbl[] = {
+ [max1361] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1361_channels,
+ .num_channels = ARRAY_SIZE(max1361_channels),
+ .info = &max1363_info,
+ },
+ [max1362] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1361_channels,
+ .num_channels = ARRAY_SIZE(max1361_channels),
+ .info = &max1363_info,
+ },
+ [max1363] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
+ .info = &max1363_info,
+ },
+ [max1364] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
+ .info = &max1363_info,
+ },
+ [max1036] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max1037] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max1038] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max1039] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max1136] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max1137] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max1138] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max1139] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max1236] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ },
+ [max1237] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ },
+ [max1238] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max1239] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11600] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max11601] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max11602] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11602_channels,
+ .num_channels = ARRAY_SIZE(max11602_channels),
+ },
+ [max11603] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11602_channels,
+ .num_channels = ARRAY_SIZE(max11602_channels),
+ },
+ [max11604] = {
+ .bits = 8,
+ .int_vref_mv = 4098,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max11605] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max11606] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max11607] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max11608] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11608_channels,
+ .num_channels = ARRAY_SIZE(max11608_channels),
+ },
+ [max11609] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11608_channels,
+ .num_channels = ARRAY_SIZE(max11608_channels),
+ },
+ [max11610] = {
+ .bits = 10,
+ .int_vref_mv = 4098,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max11611] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max11612] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
+ },
+ [max11613] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
+ },
+ [max11614] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11614_channels,
+ .num_channels = ARRAY_SIZE(max11614_channels),
+ },
+ [max11615] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11614_channels,
+ .num_channels = ARRAY_SIZE(max11614_channels),
+ },
+ [max11616] = {
+ .bits = 12,
+ .int_vref_mv = 4098,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11617] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11644] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11644_channels,
+ .num_channels = ARRAY_SIZE(max11644_channels),
+ },
+ [max11645] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11644_channels,
+ .num_channels = ARRAY_SIZE(max11644_channels),
+ },
+ [max11646] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11646_channels,
+ .num_channels = ARRAY_SIZE(max11646_channels),
+ },
+ [max11647] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11646_channels,
+ .num_channels = ARRAY_SIZE(max11646_channels),
+ },
+};
+
+static int max1363_initial_setup(struct max1363_state *st)
+{
+ st->setupbyte = MAX1363_SETUP_INT_CLOCK
+ | MAX1363_SETUP_UNIPOLAR
+ | MAX1363_SETUP_NORESET;
+
+ if (st->vref)
+ st->setupbyte |= MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF;
+ else
+ st->setupbyte |= MAX1363_SETUP_POWER_UP_INT_REF
+ | MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT;
+
+ /* Set scan mode writes the config anyway so wait until then */
+ st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte);
+ st->current_mode = &max1363_mode_table[st->chip_info->default_mode];
+ st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte);
+
+ return max1363_set_scan_mode(st);
+}
+
+static int max1363_alloc_scan_masks(struct iio_dev *indio_dev)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+ unsigned long *masks;
+ int i;
+
+ masks = devm_kzalloc(&indio_dev->dev,
+ BITS_TO_LONGS(MAX1363_MAX_CHANNELS) * sizeof(long) *
+ (st->chip_info->num_modes + 1), GFP_KERNEL);
+ if (!masks)
+ return -ENOMEM;
+
+ for (i = 0; i < st->chip_info->num_modes; i++)
+ bitmap_copy(masks + BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i,
+ max1363_mode_table[st->chip_info->mode_list[i]]
+ .modemask, MAX1363_MAX_CHANNELS);
+
+ indio_dev->available_scan_masks = masks;
+
+ return 0;
+}
+
+static irqreturn_t max1363_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct max1363_state *st = iio_priv(indio_dev);
+ s64 time_ns;
+ __u8 *rxbuf;
+ int b_sent;
+ size_t d_size;
+ unsigned long numvals = bitmap_weight(st->current_mode->modemask,
+ MAX1363_MAX_CHANNELS);
+
+ /* Ensure the timestamp is 8 byte aligned */
+ if (st->chip_info->bits != 8)
+ d_size = numvals*2;
+ else
+ d_size = numvals;
+ if (indio_dev->scan_timestamp) {
+ d_size += sizeof(s64);
+ if (d_size % sizeof(s64))
+ d_size += sizeof(s64) - (d_size % sizeof(s64));
+ }
+ /* Monitor mode prevents reading. Whilst not currently implemented
+ * might as well have this test in here in the meantime as it does
+ * no harm.
+ */
+ if (numvals == 0)
+ goto done;
+
+ rxbuf = kmalloc(d_size, GFP_KERNEL);
+ if (rxbuf == NULL)
+ goto done;
+ if (st->chip_info->bits != 8)
+ b_sent = i2c_master_recv(st->client, rxbuf, numvals*2);
+ else
+ b_sent = i2c_master_recv(st->client, rxbuf, numvals);
+ if (b_sent < 0)
+ goto done_free;
+
+ time_ns = iio_get_time_ns();
+
+ if (indio_dev->scan_timestamp)
+ memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns));
+ iio_push_to_buffers(indio_dev, rxbuf);
+
+done_free:
+ kfree(rxbuf);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops max1363_buffered_setup_ops = {
+ .postenable = &iio_triggered_buffer_postenable,
+ .preenable = &iio_sw_buffer_preenable,
+ .predisable = &iio_triggered_buffer_predisable,
+};
+
+static int max1363_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct max1363_state *st;
+ struct iio_dev *indio_dev;
+ struct regulator *vref;
+
+ indio_dev = iio_device_alloc(sizeof(struct max1363_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ 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;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = devm_regulator_get(&client->dev, "vcc");
+ if (IS_ERR(st->reg)) {
+ ret = PTR_ERR(st->reg);
+ goto error_unregister_map;
+ }
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_unregister_map;
+
+ /* this is only used for device removal purposes */
+ i2c_set_clientdata(client, indio_dev);
+
+ st->chip_info = &max1363_chip_info_tbl[id->driver_data];
+ st->client = client;
+
+ st->vref_uv = st->chip_info->int_vref_mv * 1000;
+ vref = devm_regulator_get(&client->dev, "vref");
+ if (!IS_ERR(vref)) {
+ int vref_uv;
+
+ ret = regulator_enable(vref);
+ if (ret)
+ goto error_disable_reg;
+ st->vref = vref;
+ vref_uv = regulator_get_voltage(vref);
+ if (vref_uv <= 0) {
+ ret = -EINVAL;
+ goto error_disable_reg;
+ }
+ st->vref_uv = vref_uv;
+ }
+
+ ret = max1363_alloc_scan_masks(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ /* Establish that the iio_dev is a child of the i2c device */
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ indio_dev->info = st->chip_info->info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ ret = max1363_initial_setup(st);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &max1363_trigger_handler, &max1363_buffered_setup_ops);
+ if (ret)
+ goto error_disable_reg;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, st->client->irq,
+ NULL,
+ &max1363_event_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "max1363_event",
+ indio_dev);
+
+ if (ret)
+ goto error_uninit_buffer;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto error_uninit_buffer;
+
+ return 0;
+
+error_uninit_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_disable_reg:
+ if (st->vref)
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+error_unregister_map:
+ iio_map_array_unregister(indio_dev);
+error_free_device:
+ iio_device_free(indio_dev);
+error_out:
+ return ret;
+}
+
+static int max1363_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct max1363_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (st->vref)
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+ iio_map_array_unregister(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id max1363_id[] = {
+ { "max1361", max1361 },
+ { "max1362", max1362 },
+ { "max1363", max1363 },
+ { "max1364", max1364 },
+ { "max1036", max1036 },
+ { "max1037", max1037 },
+ { "max1038", max1038 },
+ { "max1039", max1039 },
+ { "max1136", max1136 },
+ { "max1137", max1137 },
+ { "max1138", max1138 },
+ { "max1139", max1139 },
+ { "max1236", max1236 },
+ { "max1237", max1237 },
+ { "max1238", max1238 },
+ { "max1239", max1239 },
+ { "max11600", max11600 },
+ { "max11601", max11601 },
+ { "max11602", max11602 },
+ { "max11603", max11603 },
+ { "max11604", max11604 },
+ { "max11605", max11605 },
+ { "max11606", max11606 },
+ { "max11607", max11607 },
+ { "max11608", max11608 },
+ { "max11609", max11609 },
+ { "max11610", max11610 },
+ { "max11611", max11611 },
+ { "max11612", max11612 },
+ { "max11613", max11613 },
+ { "max11614", max11614 },
+ { "max11615", max11615 },
+ { "max11616", max11616 },
+ { "max11617", max11617 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max1363_id);
+
+static struct i2c_driver max1363_driver = {
+ .driver = {
+ .name = "max1363",
+ },
+ .probe = max1363_probe,
+ .remove = max1363_remove,
+ .id_table = max1363_id,
+};
+module_i2c_driver(max1363_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Maxim 1363 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
new file mode 100644
index 00000000..2826faae
--- /dev/null
+++ b/drivers/iio/adc/ti-adc081c.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * 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/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/regulator/consumer.h>
+
+struct adc081c {
+ struct i2c_client *i2c;
+ struct regulator *ref;
+};
+
+#define REG_CONV_RES 0x00
+
+static int adc081c_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int *value,
+ int *shift, long mask)
+{
+ struct adc081c *adc = iio_priv(iio);
+ int err;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES);
+ if (err < 0)
+ return err;
+
+ *value = (err >> 4) & 0xff;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ err = regulator_get_voltage(adc->ref);
+ if (err < 0)
+ return err;
+
+ *value = err / 1000;
+ *shift = 8;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adc081c_channel = {
+ .type = IIO_VOLTAGE,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+};
+
+static const struct iio_info adc081c_info = {
+ .read_raw = adc081c_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int adc081c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *iio;
+ struct adc081c *adc;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ iio = iio_device_alloc(sizeof(*adc));
+ if (!iio)
+ return -ENOMEM;
+
+ adc = iio_priv(iio);
+ adc->i2c = client;
+
+ adc->ref = regulator_get(&client->dev, "vref");
+ if (IS_ERR(adc->ref)) {
+ err = PTR_ERR(adc->ref);
+ goto iio_free;
+ }
+
+ err = regulator_enable(adc->ref);
+ if (err < 0)
+ goto regulator_put;
+
+ iio->dev.parent = &client->dev;
+ iio->name = dev_name(&client->dev);
+ iio->modes = INDIO_DIRECT_MODE;
+ iio->info = &adc081c_info;
+
+ iio->channels = &adc081c_channel;
+ iio->num_channels = 1;
+
+ err = iio_device_register(iio);
+ if (err < 0)
+ goto regulator_disable;
+
+ i2c_set_clientdata(client, iio);
+
+ return 0;
+
+regulator_disable:
+ regulator_disable(adc->ref);
+regulator_put:
+ regulator_put(adc->ref);
+iio_free:
+ iio_device_free(iio);
+
+ return err;
+}
+
+static int adc081c_remove(struct i2c_client *client)
+{
+ struct iio_dev *iio = i2c_get_clientdata(client);
+ struct adc081c *adc = iio_priv(iio);
+
+ iio_device_unregister(iio);
+ regulator_disable(adc->ref);
+ regulator_put(adc->ref);
+ iio_device_free(iio);
+
+ return 0;
+}
+
+static const struct i2c_device_id adc081c_id[] = {
+ { "adc081c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adc081c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id adc081c_of_match[] = {
+ { .compatible = "ti,adc081c" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adc081c_of_match);
+#endif
+
+static struct i2c_driver adc081c_driver = {
+ .driver = {
+ .name = "adc081c",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adc081c_of_match),
+ },
+ .probe = adc081c_probe,
+ .remove = adc081c_remove,
+ .id_table = adc081c_id,
+};
+module_i2c_driver(adc081c_driver);
+
+MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
+MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
new file mode 100644
index 00000000..5f9a7e7d
--- /dev/null
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -0,0 +1,260 @@
+/*
+ * TI ADC MFD driver
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+
+#include <linux/mfd/ti_am335x_tscadc.h>
+#include <linux/platform_data/ti_am335x_adc.h>
+
+struct tiadc_device {
+ struct ti_tscadc_dev *mfd_tscadc;
+ int channels;
+};
+
+static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
+{
+ return readl(adc->mfd_tscadc->tscadc_base + reg);
+}
+
+static void tiadc_writel(struct tiadc_device *adc, unsigned int reg,
+ unsigned int val)
+{
+ writel(val, adc->mfd_tscadc->tscadc_base + reg);
+}
+
+static void tiadc_step_config(struct tiadc_device *adc_dev)
+{
+ unsigned int stepconfig;
+ int i, channels = 0, steps;
+
+ /*
+ * There are 16 configurable steps and 8 analog input
+ * lines available which are shared between Touchscreen and ADC.
+ *
+ * Steps backwards i.e. from 16 towards 0 are used by ADC
+ * depending on number of input lines needed.
+ * Channel would represent which analog input
+ * needs to be given to ADC to digitalize data.
+ */
+
+ steps = TOTAL_STEPS - adc_dev->channels;
+ channels = TOTAL_CHANNELS - adc_dev->channels;
+
+ stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
+
+ for (i = (steps + 1); i <= TOTAL_STEPS; i++) {
+ tiadc_writel(adc_dev, REG_STEPCONFIG(i),
+ stepconfig | STEPCONFIG_INP(channels));
+ tiadc_writel(adc_dev, REG_STEPDELAY(i),
+ STEPCONFIG_OPENDLY);
+ channels++;
+ }
+ tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
+}
+
+static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
+{
+ struct iio_chan_spec *chan_array;
+ int i;
+
+ indio_dev->num_channels = channels;
+ chan_array = kcalloc(indio_dev->num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+
+ if (chan_array == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < (indio_dev->num_channels); i++) {
+ struct iio_chan_spec *chan = chan_array + i;
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->channel = i;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ }
+
+ indio_dev->channels = chan_array;
+
+ return indio_dev->num_channels;
+}
+
+static void tiadc_channels_remove(struct iio_dev *indio_dev)
+{
+ kfree(indio_dev->channels);
+}
+
+static int tiadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int i;
+ unsigned int fifo1count, readx1;
+
+ /*
+ * When the sub-system is first enabled,
+ * the sequencer will always start with the
+ * lowest step (1) and continue until step (16).
+ * For ex: If we have enabled 4 ADC channels and
+ * currently use only 1 out of them, the
+ * sequencer still configures all the 4 steps,
+ * leading to 3 unwanted data.
+ * Hence we need to flush out this data.
+ */
+
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++) {
+ readx1 = tiadc_readl(adc_dev, REG_FIFO1);
+ if (i == chan->channel)
+ *val = readx1 & 0xfff;
+ }
+ tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
+
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info tiadc_info = {
+ .read_raw = &tiadc_read_raw,
+};
+
+static int tiadc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct tiadc_device *adc_dev;
+ struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
+ struct mfd_tscadc_board *pdata;
+ int err;
+
+ pdata = tscadc_dev->dev->platform_data;
+ if (!pdata || !pdata->adc_init) {
+ dev_err(&pdev->dev, "Could not find platform data\n");
+ return -EINVAL;
+ }
+
+ indio_dev = iio_device_alloc(sizeof(struct tiadc_device));
+ if (indio_dev == NULL) {
+ dev_err(&pdev->dev, "failed to allocate iio device\n");
+ err = -ENOMEM;
+ goto err_ret;
+ }
+ adc_dev = iio_priv(indio_dev);
+
+ adc_dev->mfd_tscadc = tscadc_dev;
+ adc_dev->channels = pdata->adc_init->adc_channels;
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &tiadc_info;
+
+ tiadc_step_config(adc_dev);
+
+ err = tiadc_channel_init(indio_dev, adc_dev->channels);
+ if (err < 0)
+ goto err_free_device;
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto err_free_channels;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return 0;
+
+err_free_channels:
+ tiadc_channels_remove(indio_dev);
+err_free_device:
+ iio_device_free(indio_dev);
+err_ret:
+ return err;
+}
+
+static int tiadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+ tiadc_channels_remove(indio_dev);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tiadc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
+ unsigned int idle;
+
+ if (!device_may_wakeup(tscadc_dev->dev)) {
+ idle = tiadc_readl(adc_dev, REG_CTRL);
+ idle &= ~(CNTRLREG_TSCSSENB);
+ tiadc_writel(adc_dev, REG_CTRL, (idle |
+ CNTRLREG_POWERDOWN));
+ }
+
+ return 0;
+}
+
+static int tiadc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int restore;
+
+ /* Make sure ADC is powered up */
+ restore = tiadc_readl(adc_dev, REG_CTRL);
+ restore &= ~(CNTRLREG_POWERDOWN);
+ tiadc_writel(adc_dev, REG_CTRL, restore);
+
+ tiadc_step_config(adc_dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tiadc_pm_ops = {
+ .suspend = tiadc_suspend,
+ .resume = tiadc_resume,
+};
+#define TIADC_PM_OPS (&tiadc_pm_ops)
+#else
+#define TIADC_PM_OPS NULL
+#endif
+
+static struct platform_driver tiadc_driver = {
+ .driver = {
+ .name = "tiadc",
+ .owner = THIS_MODULE,
+ .pm = TIADC_PM_OPS,
+ },
+ .probe = tiadc_probe,
+ .remove = tiadc_remove,
+};
+
+module_platform_driver(tiadc_driver);
+
+MODULE_DESCRIPTION("TI ADC controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c
new file mode 100644
index 00000000..56ac481c
--- /dev/null
+++ b/drivers/iio/adc/viperboard_adc.c
@@ -0,0 +1,181 @@
+/*
+ * Nano River Technologies viperboard IIO ADC driver
+ *
+ * (C) 2012 by Lemonage GmbH
+ * Author: Lars Poeschel <poeschel@lemonage.de>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb.h>
+#include <linux/iio/iio.h>
+
+#include <linux/mfd/viperboard.h>
+
+#define VPRBRD_ADC_CMD_GET 0x00
+
+struct vprbrd_adc_msg {
+ u8 cmd;
+ u8 chan;
+ u8 val;
+} __packed;
+
+struct vprbrd_adc {
+ struct vprbrd *vb;
+};
+
+#define VPRBRD_ADC_CHANNEL(_index) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ }, \
+}
+
+static struct iio_chan_spec const vprbrd_adc_iio_channels[] = {
+ VPRBRD_ADC_CHANNEL(0),
+ VPRBRD_ADC_CHANNEL(1),
+ VPRBRD_ADC_CHANNEL(2),
+ VPRBRD_ADC_CHANNEL(3),
+};
+
+static int vprbrd_iio_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long info)
+{
+ int ret, error = 0;
+ struct vprbrd_adc *adc = iio_priv(iio_dev);
+ struct vprbrd *vb = adc->vb;
+ struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&vb->lock);
+
+ admsg->cmd = VPRBRD_ADC_CMD_GET;
+ admsg->chan = chan->scan_index;
+ admsg->val = 0x00;
+
+ ret = usb_control_msg(vb->usb_dev,
+ usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
+ VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg,
+ sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
+ if (ret != sizeof(struct vprbrd_adc_msg)) {
+ dev_err(&iio_dev->dev, "usb send error on adc read\n");
+ error = -EREMOTEIO;
+ }
+
+ ret = usb_control_msg(vb->usb_dev,
+ usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
+ VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg,
+ sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
+
+ *val = admsg->val;
+
+ mutex_unlock(&vb->lock);
+
+ if (ret != sizeof(struct vprbrd_adc_msg)) {
+ dev_err(&iio_dev->dev, "usb recv error on adc read\n");
+ error = -EREMOTEIO;
+ }
+
+ if (error)
+ goto error;
+
+ return IIO_VAL_INT;
+ default:
+ error = -EINVAL;
+ break;
+ }
+error:
+ return error;
+}
+
+static const struct iio_info vprbrd_adc_iio_info = {
+ .read_raw = &vprbrd_iio_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int vprbrd_adc_probe(struct platform_device *pdev)
+{
+ struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
+ struct vprbrd_adc *adc;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ /* registering iio */
+ indio_dev = iio_device_alloc(sizeof(*adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(indio_dev);
+ adc->vb = vb;
+ indio_dev->name = "viperboard adc";
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &vprbrd_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = vprbrd_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register iio (adc)");
+ goto error;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return 0;
+
+error:
+ iio_device_free(indio_dev);
+ return ret;
+}
+
+static int vprbrd_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver vprbrd_adc_driver = {
+ .driver = {
+ .name = "viperboard-adc",
+ .owner = THIS_MODULE,
+ },
+ .probe = vprbrd_adc_probe,
+ .remove = vprbrd_adc_remove,
+};
+
+module_platform_driver(vprbrd_adc_driver);
+
+MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
+MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:viperboard-adc");
diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig
new file mode 100644
index 00000000..05d707ed
--- /dev/null
+++ b/drivers/iio/amplifiers/Kconfig
@@ -0,0 +1,17 @@
+#
+# Gain Amplifiers, etc.
+#
+menu "Amplifiers"
+
+config AD8366
+ tristate "Analog Devices AD8366 VGA"
+ depends on SPI
+ select BITREVERSE
+ help
+ Say yes here to build support for Analog Devices AD8366
+ SPI Dual-Digital Variable Gain Amplifier (VGA).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad8366.
+
+endmenu
diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile
new file mode 100644
index 00000000..a6ca3669
--- /dev/null
+++ b/drivers/iio/amplifiers/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile iio/amplifiers
+#
+
+obj-$(CONFIG_AD8366) += ad8366.o
diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c
new file mode 100644
index 00000000..d354554b
--- /dev/null
+++ b/drivers/iio/amplifiers/ad8366.c
@@ -0,0 +1,222 @@
+/*
+ * AD8366 SPI Dual-Digital Variable Gain Amplifier (VGA)
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitrev.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+struct ad8366_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned char ch[2];
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ unsigned char data[2] ____cacheline_aligned;
+};
+
+static int ad8366_write(struct iio_dev *indio_dev,
+ unsigned char ch_a, char unsigned ch_b)
+{
+ struct ad8366_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ch_a = bitrev8(ch_a & 0x3F);
+ ch_b = bitrev8(ch_b & 0x3F);
+
+ st->data[0] = ch_b >> 4;
+ st->data[1] = (ch_b << 4) | (ch_a >> 2);
+
+ ret = spi_write(st->spi, st->data, ARRAY_SIZE(st->data));
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "write failed (%d)", ret);
+
+ return ret;
+}
+
+static int ad8366_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad8366_state *st = iio_priv(indio_dev);
+ int ret;
+ unsigned code;
+
+ mutex_lock(&indio_dev->mlock);
+ switch (m) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ code = st->ch[chan->channel];
+
+ /* Values in dB */
+ code = code * 253 + 4500;
+ *val = code / 1000;
+ *val2 = (code % 1000) * 1000;
+
+ ret = IIO_VAL_INT_PLUS_MICRO_DB;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+};
+
+static int ad8366_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad8366_state *st = iio_priv(indio_dev);
+ unsigned code;
+ int ret;
+
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+
+ /* Values in dB */
+ code = (((u8)val * 1000) + ((u32)val2 / 1000));
+
+ if (code > 20500 || code < 4500)
+ return -EINVAL;
+
+ code = (code - 4500) / 253;
+
+ mutex_lock(&indio_dev->mlock);
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ st->ch[chan->channel] = code;
+ ret = ad8366_write(indio_dev, st->ch[0], st->ch[1]);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static const struct iio_info ad8366_info = {
+ .read_raw = &ad8366_read_raw,
+ .write_raw = &ad8366_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define AD8366_CHAN(_channel) { \
+ .type = IIO_VOLTAGE, \
+ .output = 1, \
+ .indexed = 1, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN),\
+}
+
+static const struct iio_chan_spec ad8366_channels[] = {
+ AD8366_CHAN(0),
+ AD8366_CHAN(1),
+};
+
+static int ad8366_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ad8366_state *st;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad8366_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad8366_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad8366_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ad8366_write(indio_dev, 0 , 0);
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+error_put_reg:
+ if (!IS_ERR(st->reg))
+ regulator_put(st->reg);
+
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad8366_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad8366_state *st = iio_priv(indio_dev);
+ struct regulator *reg = st->reg;
+
+ iio_device_unregister(indio_dev);
+
+ if (!IS_ERR(reg)) {
+ regulator_disable(reg);
+ regulator_put(reg);
+ }
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad8366_id[] = {
+ {"ad8366", 0},
+ {}
+};
+
+static struct spi_driver ad8366_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ad8366_probe,
+ .remove = ad8366_remove,
+ .id_table = ad8366_id,
+};
+
+module_spi_driver(ad8366_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD8366 VGA");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c
new file mode 100644
index 00000000..9d19ba74
--- /dev/null
+++ b/drivers/iio/buffer_cb.c
@@ -0,0 +1,116 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/consumer.h>
+
+struct iio_cb_buffer {
+ struct iio_buffer buffer;
+ int (*cb)(u8 *data, void *private);
+ void *private;
+ struct iio_channel *channels;
+};
+
+static int iio_buffer_cb_store_to(struct iio_buffer *buffer, u8 *data)
+{
+ struct iio_cb_buffer *cb_buff = container_of(buffer,
+ struct iio_cb_buffer,
+ buffer);
+
+ return cb_buff->cb(data, cb_buff->private);
+}
+
+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(struct device *dev,
+ int (*cb)(u8 *data,
+ void *private),
+ void *private)
+{
+ int ret;
+ struct iio_cb_buffer *cb_buff;
+ struct iio_dev *indio_dev;
+ struct iio_channel *chan;
+
+ cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL);
+ if (cb_buff == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ cb_buff->private = private;
+ cb_buff->cb = cb;
+ cb_buff->buffer.access = &iio_cb_access;
+ INIT_LIST_HEAD(&cb_buff->buffer.demux_list);
+
+ 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;
+ }
+
+ indio_dev = cb_buff->channels[0].indio_dev;
+ cb_buff->buffer.scan_mask
+ = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
+ GFP_KERNEL);
+ if (cb_buff->buffer.scan_mask == NULL) {
+ ret = -ENOMEM;
+ goto error_release_channels;
+ }
+ chan = &cb_buff->channels[0];
+ while (chan->indio_dev) {
+ if (chan->indio_dev != indio_dev) {
+ ret = -EINVAL;
+ goto error_free_scan_mask;
+ }
+ set_bit(chan->channel->scan_index,
+ cb_buff->buffer.scan_mask);
+ chan++;
+ }
+
+ return cb_buff;
+
+error_free_scan_mask:
+ kfree(cb_buff->buffer.scan_mask);
+error_release_channels:
+ iio_channel_release_all(cb_buff->channels);
+error_free_cb_buff:
+ kfree(cb_buff);
+error_ret:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
+
+int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
+{
+ return iio_update_buffers(cb_buff->channels[0].indio_dev,
+ &cb_buff->buffer,
+ NULL);
+}
+EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
+
+void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff)
+{
+ iio_update_buffers(cb_buff->channels[0].indio_dev,
+ NULL,
+ &cb_buff->buffer);
+}
+EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
+
+void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff)
+{
+ kfree(cb_buff->buffer.scan_mask);
+ iio_channel_release_all(cb_buff->channels);
+ kfree(cb_buff);
+}
+EXPORT_SYMBOL_GPL(iio_channel_release_all_cb);
+
+struct iio_channel
+*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer)
+{
+ return cb_buffer->channels;
+}
+EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
new file mode 100644
index 00000000..0b6e97d1
--- /dev/null
+++ b/drivers/iio/common/Kconfig
@@ -0,0 +1,6 @@
+#
+# IIO common modules
+#
+
+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
new file mode 100644
index 00000000..c2352beb
--- /dev/null
+++ b/drivers/iio/common/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the IIO common modules.
+# Common modules contains modules, which can be shared among multiple
+# IIO modules. For example if the trigger processing is common for
+# multiple IIO modules then this can be moved to a common module
+# instead of duplicating in each module.
+#
+
+obj-y += hid-sensors/
+obj-y += st_sensors/
diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig
new file mode 100644
index 00000000..1178121b
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/Kconfig
@@ -0,0 +1,37 @@
+#
+# Hid Sensor common modules
+#
+menu "Hid Sensor IIO Common"
+
+config HID_SENSOR_IIO_COMMON
+ tristate "Common modules for all HID Sensor IIO drivers"
+ depends on HID_SENSOR_HUB
+ select HID_SENSOR_IIO_TRIGGER if IIO_BUFFER
+ help
+ Say yes here to build support for HID sensor to use
+ HID sensor common processing for attributes and IIO triggers.
+ There are many attributes which can be shared among multiple
+ HID sensor drivers, this module contains processing for those
+ attributes.
+
+config HID_SENSOR_IIO_TRIGGER
+ tristate "Common module (trigger) for all HID Sensor IIO drivers"
+ depends on HID_SENSOR_HUB && HID_SENSOR_IIO_COMMON
+ select IIO_TRIGGER
+ help
+ Say yes here to build trigger support for HID sensors.
+ Triggers will be send if all requested attributes were read.
+
+ If this driver is compiled as a module, it will be named
+ hid-sensor-trigger.
+
+config HID_SENSOR_ENUM_BASE_QUIRKS
+ bool "ENUM base quirks for HID Sensor IIO drivers"
+ depends on HID_SENSOR_IIO_COMMON
+ help
+ Say yes here to build support for sensor hub FW using
+ enumeration, which is using 1 as base instead of 0.
+ Since logical minimum is still set 0 instead of 1,
+ there is no easy way to differentiate.
+
+endmenu
diff --git a/drivers/iio/common/hid-sensors/Makefile b/drivers/iio/common/hid-sensors/Makefile
new file mode 100644
index 00000000..22e7c5a8
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Hid sensor common modules.
+#
+
+obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o
+obj-$(CONFIG_HID_SENSOR_IIO_TRIGGER) += hid-sensor-trigger.o
+hid-sensor-iio-common-y := hid-sensor-attributes.o
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
new file mode 100644
index 00000000..75b54730
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -0,0 +1,249 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+static int pow_10(unsigned power)
+{
+ int i;
+ int ret = 1;
+ for (i = 0; i < power; ++i)
+ ret = ret * 10;
+
+ return ret;
+}
+
+static void simple_div(int dividend, int divisor, int *whole,
+ int *micro_frac)
+{
+ int rem;
+ int exp = 0;
+
+ *micro_frac = 0;
+ if (divisor == 0) {
+ *whole = 0;
+ return;
+ }
+ *whole = dividend/divisor;
+ rem = dividend % divisor;
+ if (rem) {
+ while (rem <= divisor) {
+ rem *= 10;
+ exp++;
+ }
+ *micro_frac = (rem / divisor) * pow_10(6-exp);
+ }
+}
+
+static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2)
+{
+ *val1 = no/pow_10(exp);
+ *val2 = no%pow_10(exp) * pow_10(6-exp);
+}
+
+/*
+VTF format uses exponent and variable size format.
+For example if the size is 2 bytes
+0x0067 with VTF16E14 format -> +1.03
+To convert just change to 0x67 to decimal and use two decimal as E14 stands
+for 10^-2.
+Negative numbers are 2's complement
+*/
+static void convert_from_vtf_format(u32 value, int size, int exp,
+ int *val1, int *val2)
+{
+ int sign = 1;
+
+ if (value & BIT(size*8 - 1)) {
+ value = ((1LL << (size * 8)) - value);
+ sign = -1;
+ }
+ exp = hid_sensor_convert_exponent(exp);
+ if (exp >= 0) {
+ *val1 = sign * value * pow_10(exp);
+ *val2 = 0;
+ } else {
+ split_micro_fraction(value, -exp, val1, val2);
+ if (*val1)
+ *val1 = sign * (*val1);
+ else
+ *val2 = sign * (*val2);
+ }
+}
+
+static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)
+{
+ u32 value;
+ int sign = 1;
+
+ if (val1 < 0 || val2 < 0)
+ sign = -1;
+ exp = hid_sensor_convert_exponent(exp);
+ if (exp < 0) {
+ value = abs(val1) * pow_10(-exp);
+ value += abs(val2) / pow_10(6+exp);
+ } else
+ value = abs(val1) / pow_10(exp);
+ if (sign < 0)
+ value = ((1LL << (size * 8)) - value);
+
+ return value;
+}
+
+int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
+ int *val1, int *val2)
+{
+ s32 value;
+ int ret;
+
+ ret = sensor_hub_get_feature(st->hsdev,
+ st->poll.report_id,
+ st->poll.index, &value);
+ if (ret < 0 || value < 0) {
+ *val1 = *val2 = 0;
+ return -EINVAL;
+ } else {
+ if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
+ simple_div(1000, value, val1, val2);
+ else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
+ simple_div(1, value, val1, val2);
+ else {
+ *val1 = *val2 = 0;
+ return -EINVAL;
+ }
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+EXPORT_SYMBOL(hid_sensor_read_samp_freq_value);
+
+int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
+ int val1, int val2)
+{
+ s32 value;
+ int ret;
+
+ if (val1 < 0 || val2 < 0)
+ ret = -EINVAL;
+
+ value = val1 * pow_10(6) + val2;
+ if (value) {
+ if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
+ value = pow_10(9)/value;
+ else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
+ value = pow_10(6)/value;
+ else
+ value = 0;
+ }
+ ret = sensor_hub_set_feature(st->hsdev,
+ st->poll.report_id,
+ st->poll.index, value);
+ if (ret < 0 || value < 0)
+ ret = -EINVAL;
+
+ return ret;
+}
+EXPORT_SYMBOL(hid_sensor_write_samp_freq_value);
+
+int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st,
+ int *val1, int *val2)
+{
+ s32 value;
+ int ret;
+
+ ret = sensor_hub_get_feature(st->hsdev,
+ st->sensitivity.report_id,
+ st->sensitivity.index, &value);
+ if (ret < 0 || value < 0) {
+ *val1 = *val2 = 0;
+ return -EINVAL;
+ } else {
+ convert_from_vtf_format(value, st->sensitivity.size,
+ st->sensitivity.unit_expo,
+ val1, val2);
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value);
+
+int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
+ int val1, int val2)
+{
+ s32 value;
+ int ret;
+
+ value = convert_to_vtf_format(st->sensitivity.size,
+ st->sensitivity.unit_expo,
+ val1, val2);
+ ret = sensor_hub_set_feature(st->hsdev,
+ st->sensitivity.report_id,
+ st->sensitivity.index, value);
+ if (ret < 0 || value < 0)
+ ret = -EINVAL;
+
+ return ret;
+}
+EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
+
+int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ struct hid_sensor_common *st)
+{
+
+ sensor_hub_input_get_attribute_info(hsdev,
+ HID_FEATURE_REPORT, usage_id,
+ HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
+ &st->poll);
+
+ sensor_hub_input_get_attribute_info(hsdev,
+ HID_FEATURE_REPORT, usage_id,
+ HID_USAGE_SENSOR_PROP_REPORT_STATE,
+ &st->report_state);
+
+ sensor_hub_input_get_attribute_info(hsdev,
+ HID_FEATURE_REPORT, usage_id,
+ HID_USAGE_SENSOR_PROY_POWER_STATE,
+ &st->power_state);
+
+ sensor_hub_input_get_attribute_info(hsdev,
+ HID_FEATURE_REPORT, usage_id,
+ HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
+ &st->sensitivity);
+
+ hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n",
+ st->poll.index, st->poll.report_id,
+ st->report_state.index, st->report_state.report_id,
+ st->power_state.index, st->power_state.report_id,
+ st->sensitivity.index, st->sensitivity.report_id);
+
+ return 0;
+}
+EXPORT_SYMBOL(hid_sensor_parse_common_attributes);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_DESCRIPTION("HID Sensor common attribute processing");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
new file mode 100644
index 00000000..87419c41
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -0,0 +1,100 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/sysfs.h>
+#include "hid-sensor-trigger.h"
+
+static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct hid_sensor_common *st = iio_trigger_get_drvdata(trig);
+ int state_val;
+
+ state_val = state ? 1 : 0;
+ if (IS_ENABLED(CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS))
+ ++state_val;
+ st->data_ready = state;
+ sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
+ st->power_state.index,
+ (s32)state_val);
+
+ sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
+ st->report_state.index,
+ (s32)state_val);
+
+ return 0;
+}
+
+void hid_sensor_remove_trigger(struct iio_dev *indio_dev)
+{
+ iio_trigger_unregister(indio_dev->trig);
+ iio_trigger_free(indio_dev->trig);
+ indio_dev->trig = NULL;
+}
+EXPORT_SYMBOL(hid_sensor_remove_trigger);
+
+static const struct iio_trigger_ops hid_sensor_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &hid_sensor_data_rdy_trigger_set_state,
+};
+
+int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
+ struct hid_sensor_common *attrb)
+{
+ int ret;
+ struct iio_trigger *trig;
+
+ trig = iio_trigger_alloc("%s-dev%d", name, indio_dev->id);
+ if (trig == NULL) {
+ dev_err(&indio_dev->dev, "Trigger Allocate Failed\n");
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ trig->dev.parent = indio_dev->dev.parent;
+ iio_trigger_set_drvdata(trig, attrb);
+ trig->ops = &hid_sensor_trigger_ops;
+ ret = iio_trigger_register(trig);
+
+ if (ret) {
+ dev_err(&indio_dev->dev, "Trigger Register Failed\n");
+ goto error_free_trig;
+ }
+ indio_dev->trig = trig;
+
+ return ret;
+
+error_free_trig:
+ iio_trigger_free(trig);
+error_ret:
+ return ret;
+}
+EXPORT_SYMBOL(hid_sensor_setup_trigger);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_DESCRIPTION("HID Sensor trigger processing");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
new file mode 100644
index 00000000..9a873147
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
@@ -0,0 +1,26 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef _HID_SENSOR_TRIGGER_H
+#define _HID_SENSOR_TRIGGER_H
+
+int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
+ struct hid_sensor_common *attrb);
+void hid_sensor_remove_trigger(struct iio_dev *indio_dev);
+
+#endif
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
new file mode 100644
index 00000000..865f1ca3
--- /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 00000000..9f3e24f3
--- /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 00000000..09b236d6
--- /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 00000000..ed9bc8ae
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -0,0 +1,447 @@
+/*
+ * 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 = {0, 0};
+ 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 = 0;
+ 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)
+{
+ u8 tmp_value;
+ int err = -EINVAL;
+ bool found = false;
+ struct st_sensor_odr_avl odr_out = {0, 0};
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+ if (enable) {
+ 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;
+
+ err = st_sensors_set_enable(indio_dev, false);
+ }
+ 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 00000000..38af9440
--- /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 00000000..251baf6a
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -0,0 +1,121 @@
+/*
+ * 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)
+{
+ 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;
+
+ err = spi_sync_transfer(to_spi_device(dev), xfers, ARRAY_SIZE(xfers));
+ 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)
+{
+ 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;
+
+ err = spi_sync_transfer(to_spi_device(dev), &xfers, 1);
+ 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 00000000..8d8ca6f1
--- /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;
+
+ iio_trigger_set_drvdata(sdata->trig, 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 = iio_trigger_get(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/dac/Kconfig b/drivers/iio/dac/Kconfig
new file mode 100644
index 00000000..b61160bd
--- /dev/null
+++ b/drivers/iio/dac/Kconfig
@@ -0,0 +1,154 @@
+#
+# DAC drivers
+#
+menu "Digital to analog converters"
+
+config AD5064
+ tristate "Analog Devices AD5064 and similar multi-channel DAC driver"
+ depends on (SPI_MASTER && I2C!=m) || I2C
+ help
+ Say yes here to build support for Analog Devices AD5024, AD5025, AD5044,
+ AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R, AD5648, AD5666, AD5668,
+ AD5669R Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5064.
+
+config AD5360
+ tristate "Analog Devices Analog Devices AD5360/61/62/63/70/71/73 DAC driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5360, AD5361,
+ AD5362, AD5363, AD5370, AD5371, AD5373 multi-channel
+ Digital to Analog Converters (DAC).
+
+ To compile this driver as module choose M here: the module will be called
+ ad5360.
+
+config AD5380
+ tristate "Analog Devices AD5380/81/82/83/84/90/91/92 DAC driver"
+ depends on (SPI_MASTER && I2C!=m) || I2C
+ select REGMAP_I2C if I2C
+ select REGMAP_SPI if SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5380, AD5381,
+ AD5382, AD5383, AD5384, AD5390, AD5391, AD5392 multi-channel
+ Digital to Analog Converters (DAC).
+
+ To compile this driver as module choose M here: the module will be called
+ ad5380.
+
+config AD5421
+ tristate "Analog Devices AD5421 DAC driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5421 loop-powered
+ digital-to-analog convertors (DAC).
+
+ To compile this driver as module choose M here: the module will be called
+ ad5421.
+
+config AD5624R_SPI
+ tristate "Analog Devices AD5624/44/64R DAC spi driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5624R, AD5644R and
+ AD5664R converters (DAC). This driver uses the common SPI interface.
+
+config AD5446
+ tristate "Analog Devices AD5446 and similar single channel DACs driver"
+ depends on (SPI_MASTER && I2C!=m) || I2C
+ help
+ Say yes here to build support for Analog Devices AD5300, AD5301, AD5310,
+ AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453,
+ AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612,
+ AD5620, AD5621, AD5622, AD5640, AD5660, AD5662 DACs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5446.
+
+config AD5449
+ tristate "Analog Device AD5449 and similar DACs driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5415, AD5426, AD5429,
+ AD5432, AD5439, AD5443, AD5449 Digital to Analog Converters.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5449.
+
+config AD5504
+ tristate "Analog Devices AD5504/AD5501 DAC SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5504, AD5501,
+ High Voltage Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5504.
+
+config AD5755
+ tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5755, AD5755-1,
+ AD5757, AD5735, AD5737 quad channel Digital to
+ Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5755.
+
+config AD5764
+ tristate "Analog Devices AD5764/64R/44/44R DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744,
+ AD5744R Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5764.
+
+config AD5791
+ tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5760, AD5780,
+ AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to
+ Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5791.
+
+config AD5686
+ tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5686R, AD5685R,
+ AD5684R, AD5791 Voltage Output Digital to
+ Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5686.
+
+config MAX517
+ tristate "Maxim MAX517/518/519 DAC driver"
+ depends on I2C
+ help
+ If you say yes here you get support for the Maxim chips MAX517,
+ MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs).
+
+ This driver can also be built as a module. If so, the module
+ will be called max517.
+
+config MCP4725
+ tristate "MCP4725 DAC driver"
+ depends on I2C
+ ---help---
+ Say Y here if you want to build a driver for the Microchip
+ MCP 4725 12-bit digital-to-analog converter (DAC) with I2C
+ interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mcp4725.
+
+endmenu
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
new file mode 100644
index 00000000..5b528ebb
--- /dev/null
+++ b/drivers/iio/dac/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for industrial I/O DAC drivers
+#
+
+obj-$(CONFIG_AD5360) += ad5360.o
+obj-$(CONFIG_AD5380) += ad5380.o
+obj-$(CONFIG_AD5421) += ad5421.o
+obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
+obj-$(CONFIG_AD5064) += ad5064.o
+obj-$(CONFIG_AD5504) += ad5504.o
+obj-$(CONFIG_AD5446) += ad5446.o
+obj-$(CONFIG_AD5449) += ad5449.o
+obj-$(CONFIG_AD5755) += ad5755.o
+obj-$(CONFIG_AD5764) += ad5764.o
+obj-$(CONFIG_AD5791) += ad5791.o
+obj-$(CONFIG_AD5686) += ad5686.o
+obj-$(CONFIG_MAX517) += max517.o
+obj-$(CONFIG_MCP4725) += mcp4725.o
diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c
new file mode 100644
index 00000000..aa26d50a
--- /dev/null
+++ b/drivers/iio/dac/ad5064.c
@@ -0,0 +1,688 @@
+/*
+ * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R,
+ * AD5648, AD5666, AD5668, AD5669R Digital to analog converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AD5064_MAX_DAC_CHANNELS 8
+#define AD5064_MAX_VREFS 4
+
+#define AD5064_ADDR(x) ((x) << 20)
+#define AD5064_CMD(x) ((x) << 24)
+
+#define AD5064_ADDR_ALL_DAC 0xF
+
+#define AD5064_CMD_WRITE_INPUT_N 0x0
+#define AD5064_CMD_UPDATE_DAC_N 0x1
+#define AD5064_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
+#define AD5064_CMD_WRITE_INPUT_N_UPDATE_N 0x3
+#define AD5064_CMD_POWERDOWN_DAC 0x4
+#define AD5064_CMD_CLEAR 0x5
+#define AD5064_CMD_LDAC_MASK 0x6
+#define AD5064_CMD_RESET 0x7
+#define AD5064_CMD_CONFIG 0x8
+
+#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1)
+#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0)
+
+#define AD5064_LDAC_PWRDN_NONE 0x0
+#define AD5064_LDAC_PWRDN_1K 0x1
+#define AD5064_LDAC_PWRDN_100K 0x2
+#define AD5064_LDAC_PWRDN_3STATE 0x3
+
+/**
+ * struct ad5064_chip_info - chip specific information
+ * @shared_vref: whether the vref supply is shared between channels
+ * @internal_vref: internal reference voltage. 0 if the chip has no internal
+ * vref.
+ * @channel: channel specification
+ * @num_channels: number of channels
+ */
+
+struct ad5064_chip_info {
+ bool shared_vref;
+ unsigned long internal_vref;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+struct ad5064_state;
+
+typedef int (*ad5064_write_func)(struct ad5064_state *st, unsigned int cmd,
+ unsigned int addr, unsigned int val);
+
+/**
+ * struct ad5064_state - driver instance specific data
+ * @dev: the device for this driver instance
+ * @chip_info: chip model specific constants, available modes etc
+ * @vref_reg: vref supply regulators
+ * @pwr_down: whether channel is powered down
+ * @pwr_down_mode: channel's current power down mode
+ * @dac_cache: current DAC raw value (chip does not support readback)
+ * @use_internal_vref: set to true if the internal reference voltage should be
+ * used.
+ * @write: register write callback
+ * @data: i2c/spi transfer buffers
+ */
+
+struct ad5064_state {
+ struct device *dev;
+ const struct ad5064_chip_info *chip_info;
+ struct regulator_bulk_data vref_reg[AD5064_MAX_VREFS];
+ bool pwr_down[AD5064_MAX_DAC_CHANNELS];
+ u8 pwr_down_mode[AD5064_MAX_DAC_CHANNELS];
+ unsigned int dac_cache[AD5064_MAX_DAC_CHANNELS];
+ bool use_internal_vref;
+
+ ad5064_write_func write;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ u8 i2c[3];
+ __be32 spi;
+ } data ____cacheline_aligned;
+};
+
+enum ad5064_type {
+ ID_AD5024,
+ ID_AD5025,
+ ID_AD5044,
+ ID_AD5045,
+ ID_AD5064,
+ ID_AD5064_1,
+ ID_AD5065,
+ ID_AD5628_1,
+ ID_AD5628_2,
+ ID_AD5648_1,
+ ID_AD5648_2,
+ ID_AD5666_1,
+ ID_AD5666_2,
+ ID_AD5668_1,
+ ID_AD5668_2,
+};
+
+static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
+ unsigned int addr, unsigned int val, unsigned int shift)
+{
+ val <<= shift;
+
+ return st->write(st, cmd, addr, val);
+}
+
+static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
+ const struct iio_chan_spec *chan)
+{
+ unsigned int val;
+ int ret;
+
+ val = (0x1 << chan->address);
+
+ if (st->pwr_down[chan->channel])
+ val |= st->pwr_down_mode[chan->channel] << 8;
+
+ ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0);
+
+ return ret;
+}
+
+static const char * const ad5064_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "three_state",
+};
+
+static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode[chan->channel] - 1;
+}
+
+static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ st->pwr_down_mode[chan->channel] = mode + 1;
+
+ ret = ad5064_sync_powerdown_mode(st, chan);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static const struct iio_enum ad5064_powerdown_mode_enum = {
+ .items = ad5064_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5064_powerdown_modes),
+ .get = ad5064_get_powerdown_mode,
+ .set = ad5064_set_powerdown_mode,
+};
+
+static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->pwr_down[chan->channel]);
+}
+
+static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+ st->pwr_down[chan->channel] = pwr_down;
+
+ ret = ad5064_sync_powerdown_mode(st, chan);
+ mutex_unlock(&indio_dev->mlock);
+ return ret ? ret : len;
+}
+
+static int ad5064_get_vref(struct ad5064_state *st,
+ struct iio_chan_spec const *chan)
+{
+ unsigned int i;
+
+ if (st->use_internal_vref)
+ return st->chip_info->internal_vref;
+
+ i = st->chip_info->shared_vref ? 0 : chan->channel;
+ return regulator_get_voltage(st->vref_reg[i].consumer);
+}
+
+static int ad5064_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+ int scale_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ *val = st->dac_cache[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = ad5064_get_vref(st, chan);
+ if (scale_uv < 0)
+ return scale_uv;
+
+ scale_uv = (scale_uv * 100) >> chan->scan_type.realbits;
+ *val = scale_uv / 100000;
+ *val2 = (scale_uv % 100000) * 10;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int ad5064_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5064_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N,
+ chan->address, val, chan->scan_type.shift);
+ if (ret == 0)
+ st->dac_cache[chan->channel] = val;
+ mutex_unlock(&indio_dev->mlock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad5064_info = {
+ .read_raw = ad5064_read_raw,
+ .write_raw = ad5064_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5064_read_dac_powerdown,
+ .write = ad5064_write_dac_powerdown,
+ },
+ IIO_ENUM("powerdown_mode", false, &ad5064_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum),
+ { },
+};
+
+#define AD5064_CHANNEL(chan, addr, bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .address = addr, \
+ .scan_type = IIO_ST('u', (bits), 16, 20 - (bits)), \
+ .ext_info = ad5064_ext_info, \
+}
+
+#define DECLARE_AD5064_CHANNELS(name, bits) \
+const struct iio_chan_spec name[] = { \
+ AD5064_CHANNEL(0, 0, bits), \
+ AD5064_CHANNEL(1, 1, bits), \
+ AD5064_CHANNEL(2, 2, bits), \
+ AD5064_CHANNEL(3, 3, bits), \
+ AD5064_CHANNEL(4, 4, bits), \
+ AD5064_CHANNEL(5, 5, bits), \
+ AD5064_CHANNEL(6, 6, bits), \
+ AD5064_CHANNEL(7, 7, bits), \
+}
+
+#define DECLARE_AD5065_CHANNELS(name, bits) \
+const struct iio_chan_spec name[] = { \
+ AD5064_CHANNEL(0, 0, bits), \
+ AD5064_CHANNEL(1, 3, bits), \
+}
+
+static DECLARE_AD5064_CHANNELS(ad5024_channels, 12);
+static DECLARE_AD5064_CHANNELS(ad5044_channels, 14);
+static DECLARE_AD5064_CHANNELS(ad5064_channels, 16);
+
+static DECLARE_AD5065_CHANNELS(ad5025_channels, 12);
+static DECLARE_AD5065_CHANNELS(ad5045_channels, 14);
+static DECLARE_AD5065_CHANNELS(ad5065_channels, 16);
+
+static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {
+ [ID_AD5024] = {
+ .shared_vref = false,
+ .channels = ad5024_channels,
+ .num_channels = 4,
+ },
+ [ID_AD5025] = {
+ .shared_vref = false,
+ .channels = ad5025_channels,
+ .num_channels = 2,
+ },
+ [ID_AD5044] = {
+ .shared_vref = false,
+ .channels = ad5044_channels,
+ .num_channels = 4,
+ },
+ [ID_AD5045] = {
+ .shared_vref = false,
+ .channels = ad5045_channels,
+ .num_channels = 2,
+ },
+ [ID_AD5064] = {
+ .shared_vref = false,
+ .channels = ad5064_channels,
+ .num_channels = 4,
+ },
+ [ID_AD5064_1] = {
+ .shared_vref = true,
+ .channels = ad5064_channels,
+ .num_channels = 4,
+ },
+ [ID_AD5065] = {
+ .shared_vref = false,
+ .channels = ad5065_channels,
+ .num_channels = 2,
+ },
+ [ID_AD5628_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5024_channels,
+ .num_channels = 8,
+ },
+ [ID_AD5628_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5024_channels,
+ .num_channels = 8,
+ },
+ [ID_AD5648_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5044_channels,
+ .num_channels = 8,
+ },
+ [ID_AD5648_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5044_channels,
+ .num_channels = 8,
+ },
+ [ID_AD5666_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5064_channels,
+ .num_channels = 4,
+ },
+ [ID_AD5666_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5064_channels,
+ .num_channels = 4,
+ },
+ [ID_AD5668_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5064_channels,
+ .num_channels = 8,
+ },
+ [ID_AD5668_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5064_channels,
+ .num_channels = 8,
+ },
+};
+
+static inline unsigned int ad5064_num_vref(struct ad5064_state *st)
+{
+ return st->chip_info->shared_vref ? 1 : st->chip_info->num_channels;
+}
+
+static const char * const ad5064_vref_names[] = {
+ "vrefA",
+ "vrefB",
+ "vrefC",
+ "vrefD",
+};
+
+static const char * const ad5064_vref_name(struct ad5064_state *st,
+ unsigned int vref)
+{
+ return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref];
+}
+
+static int ad5064_probe(struct device *dev, enum ad5064_type type,
+ const char *name, ad5064_write_func write)
+{
+ struct iio_dev *indio_dev;
+ struct ad5064_state *st;
+ unsigned int midscale;
+ unsigned int i;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+
+ st->chip_info = &ad5064_chip_info_tbl[type];
+ st->dev = dev;
+ st->write = write;
+
+ for (i = 0; i < ad5064_num_vref(st); ++i)
+ st->vref_reg[i].supply = ad5064_vref_name(st, i);
+
+ ret = regulator_bulk_get(dev, ad5064_num_vref(st),
+ st->vref_reg);
+ if (ret) {
+ if (!st->chip_info->internal_vref)
+ goto error_free;
+ st->use_internal_vref = true;
+ ret = ad5064_write(st, AD5064_CMD_CONFIG, 0,
+ AD5064_CONFIG_INT_VREF_ENABLE, 0);
+ if (ret) {
+ dev_err(dev, "Failed to enable internal vref: %d\n",
+ ret);
+ goto error_free;
+ }
+ } else {
+ ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg);
+ if (ret)
+ goto error_free_reg;
+ }
+
+ indio_dev->dev.parent = dev;
+ indio_dev->name = name;
+ indio_dev->info = &ad5064_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ midscale = (1 << indio_dev->channels[0].scan_type.realbits) / 2;
+
+ for (i = 0; i < st->chip_info->num_channels; ++i) {
+ st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K;
+ st->dac_cache[i] = midscale;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!st->use_internal_vref)
+ regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
+error_free_reg:
+ if (!st->use_internal_vref)
+ regulator_bulk_free(ad5064_num_vref(st), st->vref_reg);
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad5064_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad5064_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (!st->use_internal_vref) {
+ regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
+ regulator_bulk_free(ad5064_num_vref(st), st->vref_reg);
+ }
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd,
+ unsigned int addr, unsigned int val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+
+ st->data.spi = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val);
+ return spi_write(spi, &st->data.spi, sizeof(st->data.spi));
+}
+
+static int ad5064_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ return ad5064_probe(&spi->dev, id->driver_data, id->name,
+ ad5064_spi_write);
+}
+
+static int ad5064_spi_remove(struct spi_device *spi)
+{
+ return ad5064_remove(&spi->dev);
+}
+
+static const struct spi_device_id ad5064_spi_ids[] = {
+ {"ad5024", ID_AD5024},
+ {"ad5025", ID_AD5025},
+ {"ad5044", ID_AD5044},
+ {"ad5045", ID_AD5045},
+ {"ad5064", ID_AD5064},
+ {"ad5064-1", ID_AD5064_1},
+ {"ad5065", ID_AD5065},
+ {"ad5628-1", ID_AD5628_1},
+ {"ad5628-2", ID_AD5628_2},
+ {"ad5648-1", ID_AD5648_1},
+ {"ad5648-2", ID_AD5648_2},
+ {"ad5666-1", ID_AD5666_1},
+ {"ad5666-2", ID_AD5666_2},
+ {"ad5668-1", ID_AD5668_1},
+ {"ad5668-2", ID_AD5668_2},
+ {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5064_spi_ids);
+
+static struct spi_driver ad5064_spi_driver = {
+ .driver = {
+ .name = "ad5064",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5064_spi_probe,
+ .remove = ad5064_spi_remove,
+ .id_table = ad5064_spi_ids,
+};
+
+static int __init ad5064_spi_register_driver(void)
+{
+ return spi_register_driver(&ad5064_spi_driver);
+}
+
+static void ad5064_spi_unregister_driver(void)
+{
+ spi_unregister_driver(&ad5064_spi_driver);
+}
+
+#else
+
+static inline int ad5064_spi_register_driver(void) { return 0; }
+static inline void ad5064_spi_unregister_driver(void) { }
+
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int ad5064_i2c_write(struct ad5064_state *st, unsigned int cmd,
+ unsigned int addr, unsigned int val)
+{
+ struct i2c_client *i2c = to_i2c_client(st->dev);
+
+ st->data.i2c[0] = (cmd << 4) | addr;
+ put_unaligned_be16(val, &st->data.i2c[1]);
+ return i2c_master_send(i2c, st->data.i2c, 3);
+}
+
+static int ad5064_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return ad5064_probe(&i2c->dev, id->driver_data, id->name,
+ ad5064_i2c_write);
+}
+
+static int ad5064_i2c_remove(struct i2c_client *i2c)
+{
+ return ad5064_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5064_i2c_ids[] = {
+ {"ad5629-1", ID_AD5628_1},
+ {"ad5629-2", ID_AD5628_2},
+ {"ad5629-3", ID_AD5628_2}, /* similar enough to ad5629-2 */
+ {"ad5669-1", ID_AD5668_1},
+ {"ad5669-2", ID_AD5668_2},
+ {"ad5669-3", ID_AD5668_2}, /* similar enough to ad5669-2 */
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids);
+
+static struct i2c_driver ad5064_i2c_driver = {
+ .driver = {
+ .name = "ad5064",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5064_i2c_probe,
+ .remove = ad5064_i2c_remove,
+ .id_table = ad5064_i2c_ids,
+};
+
+static int __init ad5064_i2c_register_driver(void)
+{
+ return i2c_add_driver(&ad5064_i2c_driver);
+}
+
+static void __exit ad5064_i2c_unregister_driver(void)
+{
+ i2c_del_driver(&ad5064_i2c_driver);
+}
+
+#else
+
+static inline int ad5064_i2c_register_driver(void) { return 0; }
+static inline void ad5064_i2c_unregister_driver(void) { }
+
+#endif
+
+static int __init ad5064_init(void)
+{
+ int ret;
+
+ ret = ad5064_spi_register_driver();
+ if (ret)
+ return ret;
+
+ ret = ad5064_i2c_register_driver();
+ if (ret) {
+ ad5064_spi_unregister_driver();
+ return ret;
+ }
+
+ return 0;
+}
+module_init(ad5064_init);
+
+static void __exit ad5064_exit(void)
+{
+ ad5064_i2c_unregister_driver();
+ ad5064_spi_unregister_driver();
+}
+module_exit(ad5064_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5024 and similar multi-channel DACs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c
new file mode 100644
index 00000000..80923af4
--- /dev/null
+++ b/drivers/iio/dac/ad5360.c
@@ -0,0 +1,565 @@
+/*
+ * Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373
+ * multi-channel Digital to Analog Converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AD5360_CMD(x) ((x) << 22)
+#define AD5360_ADDR(x) ((x) << 16)
+
+#define AD5360_READBACK_TYPE(x) ((x) << 13)
+#define AD5360_READBACK_ADDR(x) ((x) << 7)
+
+#define AD5360_CHAN_ADDR(chan) ((chan) + 0x8)
+
+#define AD5360_CMD_WRITE_DATA 0x3
+#define AD5360_CMD_WRITE_OFFSET 0x2
+#define AD5360_CMD_WRITE_GAIN 0x1
+#define AD5360_CMD_SPECIAL_FUNCTION 0x0
+
+/* Special function register addresses */
+#define AD5360_REG_SF_NOP 0x0
+#define AD5360_REG_SF_CTRL 0x1
+#define AD5360_REG_SF_OFS(x) (0x2 + (x))
+#define AD5360_REG_SF_READBACK 0x5
+
+#define AD5360_SF_CTRL_PWR_DOWN BIT(0)
+
+#define AD5360_READBACK_X1A 0x0
+#define AD5360_READBACK_X1B 0x1
+#define AD5360_READBACK_OFFSET 0x2
+#define AD5360_READBACK_GAIN 0x3
+#define AD5360_READBACK_SF 0x4
+
+
+/**
+ * struct ad5360_chip_info - chip specific information
+ * @channel_template: channel specification template
+ * @num_channels: number of channels
+ * @channels_per_group: number of channels per group
+ * @num_vrefs: number of vref supplies for the chip
+*/
+
+struct ad5360_chip_info {
+ struct iio_chan_spec channel_template;
+ unsigned int num_channels;
+ unsigned int channels_per_group;
+ unsigned int num_vrefs;
+};
+
+/**
+ * struct ad5360_state - driver instance specific data
+ * @spi: spi_device
+ * @chip_info: chip model specific constants, available modes etc
+ * @vref_reg: vref supply regulators
+ * @ctrl: control register cache
+ * @data: spi transfer buffers
+ */
+
+struct ad5360_state {
+ struct spi_device *spi;
+ const struct ad5360_chip_info *chip_info;
+ struct regulator_bulk_data vref_reg[3];
+ unsigned int ctrl;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+enum ad5360_type {
+ ID_AD5360,
+ ID_AD5361,
+ ID_AD5362,
+ ID_AD5363,
+ ID_AD5370,
+ ID_AD5371,
+ ID_AD5372,
+ ID_AD5373,
+};
+
+#define AD5360_CHANNEL(bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .scan_type = IIO_ST('u', (bits), 16, 16 - (bits)) \
+}
+
+static const struct ad5360_chip_info ad5360_chip_info_tbl[] = {
+ [ID_AD5360] = {
+ .channel_template = AD5360_CHANNEL(16),
+ .num_channels = 16,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+ [ID_AD5361] = {
+ .channel_template = AD5360_CHANNEL(14),
+ .num_channels = 16,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+ [ID_AD5362] = {
+ .channel_template = AD5360_CHANNEL(16),
+ .num_channels = 8,
+ .channels_per_group = 4,
+ .num_vrefs = 2,
+ },
+ [ID_AD5363] = {
+ .channel_template = AD5360_CHANNEL(14),
+ .num_channels = 8,
+ .channels_per_group = 4,
+ .num_vrefs = 2,
+ },
+ [ID_AD5370] = {
+ .channel_template = AD5360_CHANNEL(16),
+ .num_channels = 40,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+ [ID_AD5371] = {
+ .channel_template = AD5360_CHANNEL(14),
+ .num_channels = 40,
+ .channels_per_group = 8,
+ .num_vrefs = 3,
+ },
+ [ID_AD5372] = {
+ .channel_template = AD5360_CHANNEL(16),
+ .num_channels = 32,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+ [ID_AD5373] = {
+ .channel_template = AD5360_CHANNEL(14),
+ .num_channels = 32,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+};
+
+static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st,
+ unsigned int channel)
+{
+ unsigned int i;
+
+ /* The first groups have their own vref, while the remaining groups
+ * share the last vref */
+ i = channel / st->chip_info->channels_per_group;
+ if (i >= st->chip_info->num_vrefs)
+ i = st->chip_info->num_vrefs - 1;
+
+ return i;
+}
+
+static int ad5360_get_channel_vref(struct ad5360_state *st,
+ unsigned int channel)
+{
+ unsigned int i = ad5360_get_channel_vref_index(st, channel);
+
+ return regulator_get_voltage(st->vref_reg[i].consumer);
+}
+
+
+static int ad5360_write_unlocked(struct iio_dev *indio_dev,
+ unsigned int cmd, unsigned int addr, unsigned int val,
+ unsigned int shift)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+
+ val <<= shift;
+ val |= AD5360_CMD(cmd) | AD5360_ADDR(addr);
+ st->data[0].d32 = cpu_to_be32(val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd,
+ unsigned int addr, unsigned int val, unsigned int shift)
+{
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5360_read(struct iio_dev *indio_dev, unsigned int type,
+ unsigned int addr)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .rx_buf = &st->data[1].d8[1],
+ .len = 3,
+ },
+ };
+
+ mutex_lock(&indio_dev->mlock);
+
+ st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) |
+ AD5360_ADDR(AD5360_REG_SF_READBACK) |
+ AD5360_READBACK_TYPE(type) |
+ AD5360_READBACK_ADDR(addr));
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret >= 0)
+ ret = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static ssize_t ad5360_read_dac_powerdown(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad5360_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN));
+}
+
+static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set,
+ unsigned int clr)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ unsigned int ret;
+
+ mutex_lock(&indio_dev->mlock);
+
+ st->ctrl |= set;
+ st->ctrl &= ~clr;
+
+ ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION,
+ AD5360_REG_SF_CTRL, st->ctrl, 0);
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static ssize_t ad5360_write_dac_powerdown(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ if (pwr_down)
+ ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0);
+ else
+ ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN);
+
+ return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(out_voltage_powerdown,
+ S_IRUGO | S_IWUSR,
+ ad5360_read_dac_powerdown,
+ ad5360_write_dac_powerdown, 0);
+
+static struct attribute *ad5360_attributes[] = {
+ &iio_dev_attr_out_voltage_powerdown.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad5360_attribute_group = {
+ .attrs = ad5360_attributes,
+};
+
+static int ad5360_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ int max_val = (1 << chan->scan_type.realbits);
+ unsigned int ofs_index;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA,
+ chan->address, val, chan->scan_type.shift);
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET,
+ chan->address, val, chan->scan_type.shift);
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN,
+ chan->address, val, chan->scan_type.shift);
+
+ case IIO_CHAN_INFO_OFFSET:
+ if (val <= -max_val || val > 0)
+ return -EINVAL;
+
+ val = -val;
+
+ /* offset is supposed to have the same scale as raw, but it
+ * is always 14bits wide, so on a chip where the raw value has
+ * more bits, we need to shift offset. */
+ val >>= (chan->scan_type.realbits - 14);
+
+ /* There is one DAC offset register per vref. Changing one
+ * channels offset will also change the offset for all other
+ * channels which share the same vref supply. */
+ ofs_index = ad5360_get_channel_vref_index(st, chan->channel);
+ return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION,
+ AD5360_REG_SF_OFS(ofs_index), val, 0);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5360_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ unsigned int ofs_index;
+ int scale_uv;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5360_read(indio_dev, AD5360_READBACK_X1A,
+ chan->address);
+ if (ret < 0)
+ return ret;
+ *val = ret >> chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* vout = 4 * vref * dac_code */
+ scale_uv = ad5360_get_channel_vref(st, chan->channel) * 4 * 100;
+ if (scale_uv < 0)
+ return scale_uv;
+
+ scale_uv >>= (chan->scan_type.realbits);
+ *val = scale_uv / 100000;
+ *val2 = (scale_uv % 100000) * 10;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET,
+ chan->address);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN,
+ chan->address);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ ofs_index = ad5360_get_channel_vref_index(st, chan->channel);
+ ret = ad5360_read(indio_dev, AD5360_READBACK_SF,
+ AD5360_REG_SF_OFS(ofs_index));
+ if (ret < 0)
+ return ret;
+
+ ret <<= (chan->scan_type.realbits - 14);
+ *val = -ret;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad5360_info = {
+ .read_raw = ad5360_read_raw,
+ .write_raw = ad5360_write_raw,
+ .attrs = &ad5360_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const ad5360_vref_name[] = {
+ "vref0", "vref1", "vref2"
+};
+
+static int ad5360_alloc_channels(struct iio_dev *indio_dev)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ struct iio_chan_spec *channels;
+ unsigned int i;
+
+ channels = kcalloc(st->chip_info->num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < st->chip_info->num_channels; ++i) {
+ channels[i] = st->chip_info->channel_template;
+ channels[i].channel = i;
+ channels[i].address = AD5360_CHAN_ADDR(i);
+ }
+
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int ad5360_probe(struct spi_device *spi)
+{
+ enum ad5360_type type = spi_get_device_id(spi)->driver_data;
+ struct iio_dev *indio_dev;
+ struct ad5360_state *st;
+ unsigned int i;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->chip_info = &ad5360_chip_info_tbl[type];
+ st->spi = spi;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5360_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ ret = ad5360_alloc_channels(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret);
+ goto error_free;
+ }
+
+ for (i = 0; i < st->chip_info->num_vrefs; ++i)
+ st->vref_reg[i].supply = ad5360_vref_name[i];
+
+ ret = regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs,
+ st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret);
+ goto error_free_channels;
+ }
+
+ ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret);
+ goto error_free_reg;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+ goto error_disable_reg;
+ }
+
+ return 0;
+
+error_disable_reg:
+ regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg);
+error_free_reg:
+ regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg);
+error_free_channels:
+ kfree(indio_dev->channels);
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad5360_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5360_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ kfree(indio_dev->channels);
+
+ regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg);
+ regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5360_ids[] = {
+ { "ad5360", ID_AD5360 },
+ { "ad5361", ID_AD5361 },
+ { "ad5362", ID_AD5362 },
+ { "ad5363", ID_AD5363 },
+ { "ad5370", ID_AD5370 },
+ { "ad5371", ID_AD5371 },
+ { "ad5372", ID_AD5372 },
+ { "ad5373", ID_AD5373 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5360_ids);
+
+static struct spi_driver ad5360_driver = {
+ .driver = {
+ .name = "ad5360",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5360_probe,
+ .remove = ad5360_remove,
+ .id_table = ad5360_ids,
+};
+module_spi_driver(ad5360_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c
new file mode 100644
index 00000000..bf2db022
--- /dev/null
+++ b/drivers/iio/dac/ad5380.c
@@ -0,0 +1,659 @@
+/*
+ * Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392
+ * multi-channel Digital to Analog Converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AD5380_REG_DATA(x) (((x) << 2) | 3)
+#define AD5380_REG_OFFSET(x) (((x) << 2) | 2)
+#define AD5380_REG_GAIN(x) (((x) << 2) | 1)
+#define AD5380_REG_SF_PWR_DOWN (8 << 2)
+#define AD5380_REG_SF_PWR_UP (9 << 2)
+#define AD5380_REG_SF_CTRL (12 << 2)
+
+#define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13
+#define AD5380_CTRL_INT_VREF_2V5 BIT(12)
+#define AD5380_CTRL_INT_VREF_EN BIT(10)
+
+/**
+ * struct ad5380_chip_info - chip specific information
+ * @channel_template: channel specification template
+ * @num_channels: number of channels
+ * @int_vref: internal vref in uV
+*/
+
+struct ad5380_chip_info {
+ struct iio_chan_spec channel_template;
+ unsigned int num_channels;
+ unsigned int int_vref;
+};
+
+/**
+ * struct ad5380_state - driver instance specific data
+ * @regmap: regmap instance used by the device
+ * @chip_info: chip model specific constants, available modes etc
+ * @vref_reg: vref supply regulator
+ * @vref: actual reference voltage used in uA
+ * @pwr_down: whether the chip is currently in power down mode
+ */
+
+struct ad5380_state {
+ struct regmap *regmap;
+ const struct ad5380_chip_info *chip_info;
+ struct regulator *vref_reg;
+ int vref;
+ bool pwr_down;
+};
+
+enum ad5380_type {
+ ID_AD5380_3,
+ ID_AD5380_5,
+ ID_AD5381_3,
+ ID_AD5381_5,
+ ID_AD5382_3,
+ ID_AD5382_5,
+ ID_AD5383_3,
+ ID_AD5383_5,
+ ID_AD5390_3,
+ ID_AD5390_5,
+ ID_AD5391_3,
+ ID_AD5391_5,
+ ID_AD5392_3,
+ ID_AD5392_5,
+};
+
+static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->pwr_down);
+}
+
+static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (pwr_down)
+ ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0);
+ else
+ ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_UP, 0);
+
+ st->pwr_down = pwr_down;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static const char * const ad5380_powerdown_modes[] = {
+ "100kohm_to_gnd",
+ "three_state",
+};
+
+static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ unsigned int mode;
+ int ret;
+
+ ret = regmap_read(st->regmap, AD5380_REG_SF_CTRL, &mode);
+ if (ret)
+ return ret;
+
+ mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1;
+
+ return mode;
+}
+
+static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_update_bits(st->regmap, AD5380_REG_SF_CTRL,
+ 1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET,
+ mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET);
+
+ return ret;
+}
+
+static const struct iio_enum ad5380_powerdown_mode_enum = {
+ .items = ad5380_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5380_powerdown_modes),
+ .get = ad5380_get_powerdown_mode,
+ .set = ad5380_set_powerdown_mode,
+};
+
+static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan,
+ long info)
+{
+ switch (info) {
+ case 0:
+ return AD5380_REG_DATA(chan->address);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return AD5380_REG_OFFSET(chan->address);
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return AD5380_REG_GAIN(chan->address);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ad5380_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+ const unsigned int max_val = (1 << chan->scan_type.realbits);
+ struct ad5380_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return regmap_write(st->regmap,
+ ad5380_info_to_reg(chan, info),
+ val << chan->scan_type.shift);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ val += (1 << chan->scan_type.realbits) / 2;
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return regmap_write(st->regmap,
+ AD5380_REG_OFFSET(chan->address),
+ val << chan->scan_type.shift);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int ad5380_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ unsigned long scale_uv;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = regmap_read(st->regmap, ad5380_info_to_reg(chan, info),
+ val);
+ if (ret)
+ return ret;
+ *val >>= chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = regmap_read(st->regmap, AD5380_REG_OFFSET(chan->address),
+ val);
+ if (ret)
+ return ret;
+ *val >>= chan->scan_type.shift;
+ val -= (1 << chan->scan_type.realbits) / 2;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = ((2 * st->vref) >> chan->scan_type.realbits) * 100;
+ *val = scale_uv / 100000;
+ *val2 = (scale_uv % 100000) * 10;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad5380_info = {
+ .read_raw = ad5380_read_raw,
+ .write_raw = ad5380_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static struct iio_chan_spec_ext_info ad5380_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5380_read_dac_powerdown,
+ .write = ad5380_write_dac_powerdown,
+ },
+ IIO_ENUM("powerdown_mode", true, &ad5380_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum),
+ { },
+};
+
+#define AD5380_CHANNEL(_bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = IIO_ST('u', (_bits), 16, 14 - (_bits)), \
+ .ext_info = ad5380_ext_info, \
+}
+
+static const struct ad5380_chip_info ad5380_chip_info_tbl[] = {
+ [ID_AD5380_3] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 40,
+ .int_vref = 1250000,
+ },
+ [ID_AD5380_5] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 40,
+ .int_vref = 2500000,
+ },
+ [ID_AD5381_3] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 16,
+ .int_vref = 1250000,
+ },
+ [ID_AD5381_5] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 16,
+ .int_vref = 2500000,
+ },
+ [ID_AD5382_3] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 32,
+ .int_vref = 1250000,
+ },
+ [ID_AD5382_5] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 32,
+ .int_vref = 2500000,
+ },
+ [ID_AD5383_3] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 32,
+ .int_vref = 1250000,
+ },
+ [ID_AD5383_5] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 32,
+ .int_vref = 2500000,
+ },
+ [ID_AD5390_3] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 16,
+ .int_vref = 1250000,
+ },
+ [ID_AD5390_5] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 16,
+ .int_vref = 2500000,
+ },
+ [ID_AD5391_3] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 16,
+ .int_vref = 1250000,
+ },
+ [ID_AD5391_5] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 16,
+ .int_vref = 2500000,
+ },
+ [ID_AD5392_3] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 8,
+ .int_vref = 1250000,
+ },
+ [ID_AD5392_5] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 8,
+ .int_vref = 2500000,
+ },
+};
+
+static int ad5380_alloc_channels(struct iio_dev *indio_dev)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ struct iio_chan_spec *channels;
+ unsigned int i;
+
+ channels = kcalloc(st->chip_info->num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < st->chip_info->num_channels; ++i) {
+ channels[i] = st->chip_info->channel_template;
+ channels[i].channel = i;
+ channels[i].address = i;
+ }
+
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int ad5380_probe(struct device *dev, struct regmap *regmap,
+ enum ad5380_type type, const char *name)
+{
+ struct iio_dev *indio_dev;
+ struct ad5380_state *st;
+ unsigned int ctrl = 0;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(dev, "Failed to allocate iio device\n");
+ ret = -ENOMEM;
+ goto error_out;
+ }
+
+ st = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+
+ st->chip_info = &ad5380_chip_info_tbl[type];
+ st->regmap = regmap;
+
+ indio_dev->dev.parent = dev;
+ indio_dev->name = name;
+ indio_dev->info = &ad5380_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ ret = ad5380_alloc_channels(indio_dev);
+ if (ret) {
+ dev_err(dev, "Failed to allocate channel spec: %d\n", ret);
+ goto error_free;
+ }
+
+ if (st->chip_info->int_vref == 2500000)
+ ctrl |= AD5380_CTRL_INT_VREF_2V5;
+
+ st->vref_reg = regulator_get(dev, "vref");
+ if (!IS_ERR(st->vref_reg)) {
+ ret = regulator_enable(st->vref_reg);
+ if (ret) {
+ dev_err(dev, "Failed to enable vref regulators: %d\n",
+ ret);
+ goto error_free_reg;
+ }
+
+ ret = regulator_get_voltage(st->vref_reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ st->vref = ret;
+ } else {
+ st->vref = st->chip_info->int_vref;
+ ctrl |= AD5380_CTRL_INT_VREF_EN;
+ }
+
+ ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl);
+ if (ret) {
+ dev_err(dev, "Failed to write to device: %d\n", ret);
+ goto error_disable_reg;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register iio device: %d\n", ret);
+ goto error_disable_reg;
+ }
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->vref_reg))
+ regulator_disable(st->vref_reg);
+error_free_reg:
+ if (!IS_ERR(st->vref_reg))
+ regulator_put(st->vref_reg);
+
+ kfree(indio_dev->channels);
+error_free:
+ iio_device_free(indio_dev);
+error_out:
+
+ return ret;
+}
+
+static int ad5380_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad5380_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ kfree(indio_dev->channels);
+
+ if (!IS_ERR(st->vref_reg)) {
+ regulator_disable(st->vref_reg);
+ regulator_put(st->vref_reg);
+ }
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static bool ad5380_reg_false(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static const struct regmap_config ad5380_regmap_config = {
+ .reg_bits = 10,
+ .val_bits = 14,
+
+ .max_register = AD5380_REG_DATA(40),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = ad5380_reg_false,
+ .readable_reg = ad5380_reg_false,
+};
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static int ad5380_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &ad5380_regmap_config);
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name);
+}
+
+static int ad5380_spi_remove(struct spi_device *spi)
+{
+ return ad5380_remove(&spi->dev);
+}
+
+static const struct spi_device_id ad5380_spi_ids[] = {
+ { "ad5380-3", ID_AD5380_3 },
+ { "ad5380-5", ID_AD5380_5 },
+ { "ad5381-3", ID_AD5381_3 },
+ { "ad5381-5", ID_AD5381_5 },
+ { "ad5382-3", ID_AD5382_3 },
+ { "ad5382-5", ID_AD5382_5 },
+ { "ad5383-3", ID_AD5383_3 },
+ { "ad5383-5", ID_AD5383_5 },
+ { "ad5384-3", ID_AD5380_3 },
+ { "ad5384-5", ID_AD5380_5 },
+ { "ad5390-3", ID_AD5390_3 },
+ { "ad5390-5", ID_AD5390_5 },
+ { "ad5391-3", ID_AD5391_3 },
+ { "ad5391-5", ID_AD5391_5 },
+ { "ad5392-3", ID_AD5392_3 },
+ { "ad5392-5", ID_AD5392_5 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad5380_spi_ids);
+
+static struct spi_driver ad5380_spi_driver = {
+ .driver = {
+ .name = "ad5380",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5380_spi_probe,
+ .remove = ad5380_spi_remove,
+ .id_table = ad5380_spi_ids,
+};
+
+static inline int ad5380_spi_register_driver(void)
+{
+ return spi_register_driver(&ad5380_spi_driver);
+}
+
+static inline void ad5380_spi_unregister_driver(void)
+{
+ spi_unregister_driver(&ad5380_spi_driver);
+}
+
+#else
+
+static inline int ad5380_spi_register_driver(void)
+{
+ return 0;
+}
+
+static inline void ad5380_spi_unregister_driver(void)
+{
+}
+
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int ad5380_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &ad5380_regmap_config);
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name);
+}
+
+static int ad5380_i2c_remove(struct i2c_client *i2c)
+{
+ return ad5380_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5380_i2c_ids[] = {
+ { "ad5380-3", ID_AD5380_3 },
+ { "ad5380-5", ID_AD5380_5 },
+ { "ad5381-3", ID_AD5381_3 },
+ { "ad5381-5", ID_AD5381_5 },
+ { "ad5382-3", ID_AD5382_3 },
+ { "ad5382-5", ID_AD5382_5 },
+ { "ad5383-3", ID_AD5383_3 },
+ { "ad5383-5", ID_AD5383_5 },
+ { "ad5384-3", ID_AD5380_3 },
+ { "ad5384-5", ID_AD5380_5 },
+ { "ad5390-3", ID_AD5390_3 },
+ { "ad5390-5", ID_AD5390_5 },
+ { "ad5391-3", ID_AD5391_3 },
+ { "ad5391-5", ID_AD5391_5 },
+ { "ad5392-3", ID_AD5392_3 },
+ { "ad5392-5", ID_AD5392_5 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids);
+
+static struct i2c_driver ad5380_i2c_driver = {
+ .driver = {
+ .name = "ad5380",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5380_i2c_probe,
+ .remove = ad5380_i2c_remove,
+ .id_table = ad5380_i2c_ids,
+};
+
+static inline int ad5380_i2c_register_driver(void)
+{
+ return i2c_add_driver(&ad5380_i2c_driver);
+}
+
+static inline void ad5380_i2c_unregister_driver(void)
+{
+ i2c_del_driver(&ad5380_i2c_driver);
+}
+
+#else
+
+static inline int ad5380_i2c_register_driver(void)
+{
+ return 0;
+}
+
+static inline void ad5380_i2c_unregister_driver(void)
+{
+}
+
+#endif
+
+static int __init ad5380_spi_init(void)
+{
+ int ret;
+
+ ret = ad5380_spi_register_driver();
+ if (ret)
+ return ret;
+
+ ret = ad5380_i2c_register_driver();
+ if (ret) {
+ ad5380_spi_unregister_driver();
+ return ret;
+ }
+
+ return 0;
+}
+module_init(ad5380_spi_init);
+
+static void __exit ad5380_spi_exit(void)
+{
+ ad5380_i2c_unregister_driver();
+ ad5380_spi_unregister_driver();
+
+}
+module_exit(ad5380_spi_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c
new file mode 100644
index 00000000..98f24407
--- /dev/null
+++ b/drivers/iio/dac/ad5421.c
@@ -0,0 +1,539 @@
+/*
+ * AD5421 Digital to analog converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/dac/ad5421.h>
+
+
+#define AD5421_REG_DAC_DATA 0x1
+#define AD5421_REG_CTRL 0x2
+#define AD5421_REG_OFFSET 0x3
+#define AD5421_REG_GAIN 0x4
+/* load dac and fault shared the same register number. Writing to it will cause
+ * a dac load command, reading from it will return the fault status register */
+#define AD5421_REG_LOAD_DAC 0x5
+#define AD5421_REG_FAULT 0x5
+#define AD5421_REG_FORCE_ALARM_CURRENT 0x6
+#define AD5421_REG_RESET 0x7
+#define AD5421_REG_START_CONVERSION 0x8
+#define AD5421_REG_NOOP 0x9
+
+#define AD5421_CTRL_WATCHDOG_DISABLE BIT(12)
+#define AD5421_CTRL_AUTO_FAULT_READBACK BIT(11)
+#define AD5421_CTRL_MIN_CURRENT BIT(9)
+#define AD5421_CTRL_ADC_SOURCE_TEMP BIT(8)
+#define AD5421_CTRL_ADC_ENABLE BIT(7)
+#define AD5421_CTRL_PWR_DOWN_INT_VREF BIT(6)
+
+#define AD5421_FAULT_SPI BIT(15)
+#define AD5421_FAULT_PEC BIT(14)
+#define AD5421_FAULT_OVER_CURRENT BIT(13)
+#define AD5421_FAULT_UNDER_CURRENT BIT(12)
+#define AD5421_FAULT_TEMP_OVER_140 BIT(11)
+#define AD5421_FAULT_TEMP_OVER_100 BIT(10)
+#define AD5421_FAULT_UNDER_VOLTAGE_6V BIT(9)
+#define AD5421_FAULT_UNDER_VOLTAGE_12V BIT(8)
+
+/* These bits will cause the fault pin to go high */
+#define AD5421_FAULT_TRIGGER_IRQ \
+ (AD5421_FAULT_SPI | AD5421_FAULT_PEC | AD5421_FAULT_OVER_CURRENT | \
+ AD5421_FAULT_UNDER_CURRENT | AD5421_FAULT_TEMP_OVER_140)
+
+/**
+ * struct ad5421_state - driver instance specific data
+ * @spi: spi_device
+ * @ctrl: control register cache
+ * @current_range: current range which the device is configured for
+ * @data: spi transfer buffers
+ * @fault_mask: software masking of events
+ */
+struct ad5421_state {
+ struct spi_device *spi;
+ unsigned int ctrl;
+ enum ad5421_current_range current_range;
+ unsigned int fault_mask;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ u32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+static const struct iio_chan_spec ad5421_channels[] = {
+ {
+ .type = IIO_CURRENT,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_type = IIO_ST('u', 16, 16, 0),
+ .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) |
+ IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
+ },
+ {
+ .type = IIO_TEMP,
+ .channel = -1,
+ .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ },
+};
+
+static int ad5421_write_unlocked(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int val)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+
+ st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5421_write(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int val)
+{
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5421_write_unlocked(indio_dev, reg, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .rx_buf = &st->data[1].d8[1],
+ .len = 3,
+ },
+ };
+
+ mutex_lock(&indio_dev->mlock);
+
+ st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret >= 0)
+ ret = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5421_update_ctrl(struct iio_dev *indio_dev, unsigned int set,
+ unsigned int clr)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+ unsigned int ret;
+
+ mutex_lock(&indio_dev->mlock);
+
+ st->ctrl &= ~clr;
+ st->ctrl |= set;
+
+ ret = ad5421_write_unlocked(indio_dev, AD5421_REG_CTRL, st->ctrl);
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static irqreturn_t ad5421_fault_handler(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct ad5421_state *st = iio_priv(indio_dev);
+ unsigned int fault;
+ unsigned int old_fault = 0;
+ unsigned int events;
+
+ fault = ad5421_read(indio_dev, AD5421_REG_FAULT);
+ if (!fault)
+ return IRQ_NONE;
+
+ /* If we had a fault, this might mean that the DAC has lost its state
+ * and has been reset. Make sure that the control register actually
+ * contains what we expect it to contain. Otherwise the watchdog might
+ * be enabled and we get watchdog timeout faults, which will render the
+ * DAC unusable. */
+ ad5421_update_ctrl(indio_dev, 0, 0);
+
+
+ /* The fault pin stays high as long as a fault condition is present and
+ * it is not possible to mask fault conditions. For certain fault
+ * conditions for example like over-temperature it takes some time
+ * until the fault condition disappears. If we would exit the interrupt
+ * handler immediately after handling the event it would be entered
+ * again instantly. Thus we fall back to polling in case we detect that
+ * a interrupt condition is still present.
+ */
+ do {
+ /* 0xffff is a invalid value for the register and will only be
+ * read if there has been a communication error */
+ if (fault == 0xffff)
+ fault = 0;
+
+ /* we are only interested in new events */
+ events = (old_fault ^ fault) & fault;
+ events &= st->fault_mask;
+
+ if (events & AD5421_FAULT_OVER_CURRENT) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns());
+ }
+
+ if (events & AD5421_FAULT_UNDER_CURRENT) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns());
+ }
+
+ if (events & AD5421_FAULT_TEMP_OVER_140) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP,
+ 0,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns());
+ }
+
+ old_fault = fault;
+ fault = ad5421_read(indio_dev, AD5421_REG_FAULT);
+
+ /* still active? go to sleep for some time */
+ if (fault & AD5421_FAULT_TRIGGER_IRQ)
+ msleep(1000);
+
+ } while (fault & AD5421_FAULT_TRIGGER_IRQ);
+
+
+ return IRQ_HANDLED;
+}
+
+static void ad5421_get_current_min_max(struct ad5421_state *st,
+ unsigned int *min, unsigned int *max)
+{
+ /* The current range is configured using external pins, which are
+ * usually hard-wired and not run-time switchable. */
+ switch (st->current_range) {
+ case AD5421_CURRENT_RANGE_4mA_20mA:
+ *min = 4000;
+ *max = 20000;
+ break;
+ case AD5421_CURRENT_RANGE_3mA8_21mA:
+ *min = 3800;
+ *max = 21000;
+ break;
+ case AD5421_CURRENT_RANGE_3mA2_24mA:
+ *min = 3200;
+ *max = 24000;
+ break;
+ default:
+ *min = 0;
+ *max = 1;
+ break;
+ }
+}
+
+static inline unsigned int ad5421_get_offset(struct ad5421_state *st)
+{
+ unsigned int min, max;
+
+ ad5421_get_current_min_max(st, &min, &max);
+ return (min * (1 << 16)) / (max - min);
+}
+
+static inline unsigned int ad5421_get_scale(struct ad5421_state *st)
+{
+ unsigned int min, max;
+
+ ad5421_get_current_min_max(st, &min, &max);
+ return ((max - min) * 1000) / (1 << 16);
+}
+
+static int ad5421_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long m)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (chan->type != IIO_CURRENT)
+ return -EINVAL;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = ad5421_get_scale(st);
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = ad5421_get_offset(st);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = ad5421_read(indio_dev, AD5421_REG_OFFSET);
+ if (ret < 0)
+ return ret;
+ *val = ret - 32768;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = ad5421_read(indio_dev, AD5421_REG_GAIN);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5421_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ const unsigned int max_val = 1 << 16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5421_write(indio_dev, AD5421_REG_DAC_DATA, val);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ val += 32768;
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5421_write(indio_dev, AD5421_REG_OFFSET, val);
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5421_write(indio_dev, AD5421_REG_GAIN, val);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5421_write_event_config(struct iio_dev *indio_dev,
+ u64 event_code, int state)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+ unsigned int mask;
+
+ switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
+ case IIO_CURRENT:
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
+ IIO_EV_DIR_RISING)
+ mask = AD5421_FAULT_OVER_CURRENT;
+ else
+ mask = AD5421_FAULT_UNDER_CURRENT;
+ break;
+ case IIO_TEMP:
+ mask = AD5421_FAULT_TEMP_OVER_140;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&indio_dev->mlock);
+ if (state)
+ st->fault_mask |= mask;
+ else
+ st->fault_mask &= ~mask;
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+
+static int ad5421_read_event_config(struct iio_dev *indio_dev,
+ u64 event_code)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+ unsigned int mask;
+
+ switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
+ case IIO_CURRENT:
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
+ IIO_EV_DIR_RISING)
+ mask = AD5421_FAULT_OVER_CURRENT;
+ else
+ mask = AD5421_FAULT_UNDER_CURRENT;
+ break;
+ case IIO_TEMP:
+ mask = AD5421_FAULT_TEMP_OVER_140;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return (bool)(st->fault_mask & mask);
+}
+
+static int ad5421_read_event_value(struct iio_dev *indio_dev, u64 event_code,
+ int *val)
+{
+ int ret;
+
+ switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
+ case IIO_CURRENT:
+ ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ break;
+ case IIO_TEMP:
+ *val = 140000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct iio_info ad5421_info = {
+ .read_raw = ad5421_read_raw,
+ .write_raw = ad5421_write_raw,
+ .read_event_config = ad5421_read_event_config,
+ .write_event_config = ad5421_write_event_config,
+ .read_event_value = ad5421_read_event_value,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad5421_probe(struct spi_device *spi)
+{
+ struct ad5421_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct iio_dev *indio_dev;
+ struct ad5421_state *st;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = "ad5421";
+ indio_dev->info = &ad5421_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad5421_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad5421_channels);
+
+ st->ctrl = AD5421_CTRL_WATCHDOG_DISABLE |
+ AD5421_CTRL_AUTO_FAULT_READBACK;
+
+ if (pdata) {
+ st->current_range = pdata->current_range;
+ if (pdata->external_vref)
+ st->ctrl |= AD5421_CTRL_PWR_DOWN_INT_VREF;
+ } else {
+ st->current_range = AD5421_CURRENT_RANGE_4mA_20mA;
+ }
+
+ /* write initial ctrl register value */
+ ad5421_update_ctrl(indio_dev, 0, 0);
+
+ if (spi->irq) {
+ ret = request_threaded_irq(spi->irq,
+ NULL,
+ ad5421_fault_handler,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ad5421 fault",
+ indio_dev);
+ if (ret)
+ goto error_free;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+ goto error_free_irq;
+ }
+
+ return 0;
+
+error_free_irq:
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad5421_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ iio_device_unregister(indio_dev);
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver ad5421_driver = {
+ .driver = {
+ .name = "ad5421",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5421_probe,
+ .remove = ad5421_remove,
+};
+module_spi_driver(ad5421_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5421 DAC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:ad5421");
diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c
new file mode 100644
index 00000000..cae8f605
--- /dev/null
+++ b/drivers/iio/dac/ad5446.c
@@ -0,0 +1,623 @@
+/*
+ * AD5446 SPI DAC driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define MODE_PWRDWN_1k 0x1
+#define MODE_PWRDWN_100k 0x2
+#define MODE_PWRDWN_TRISTATE 0x3
+
+/**
+ * struct ad5446_state - driver instance specific data
+ * @spi: spi_device
+ * @chip_info: chip model specific constants, available modes etc
+ * @reg: supply regulator
+ * @vref_mv: actual reference voltage used
+ */
+
+struct ad5446_state {
+ struct device *dev;
+ const struct ad5446_chip_info *chip_info;
+ struct regulator *reg;
+ unsigned short vref_mv;
+ unsigned cached_val;
+ unsigned pwr_down_mode;
+ unsigned pwr_down;
+};
+
+/**
+ * struct ad5446_chip_info - chip specific information
+ * @channel: channel spec for the DAC
+ * @int_vref_mv: AD5620/40/60: the internal reference voltage
+ * @write: chip specific helper function to write to the register
+ */
+
+struct ad5446_chip_info {
+ struct iio_chan_spec channel;
+ u16 int_vref_mv;
+ int (*write)(struct ad5446_state *st, unsigned val);
+};
+
+static const char * const ad5446_powerdown_modes[] = {
+ "1kohm_to_gnd", "100kohm_to_gnd", "three_state"
+};
+
+static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode = mode + 1;
+
+ return 0;
+}
+
+static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode - 1;
+}
+
+static const struct iio_enum ad5446_powerdown_mode_enum = {
+ .items = ad5446_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5446_powerdown_modes),
+ .get = ad5446_get_powerdown_mode,
+ .set = ad5446_set_powerdown_mode,
+};
+
+static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->pwr_down);
+}
+
+static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+ unsigned int shift;
+ unsigned int val;
+ bool powerdown;
+ int ret;
+
+ ret = strtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+ st->pwr_down = powerdown;
+
+ if (st->pwr_down) {
+ shift = chan->scan_type.realbits + chan->scan_type.shift;
+ val = st->pwr_down_mode << shift;
+ } else {
+ val = st->cached_val;
+ }
+
+ ret = st->chip_info->write(st, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
+ {
+ .name = "powerdown",
+ .read = ad5446_read_dac_powerdown,
+ .write = ad5446_write_dac_powerdown,
+ },
+ IIO_ENUM("powerdown_mode", false, &ad5446_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum),
+ { },
+};
+
+#define _AD5446_CHANNEL(bits, storage, shift, ext) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = IIO_ST('u', (bits), (storage), (shift)), \
+ .ext_info = (ext), \
+}
+
+#define AD5446_CHANNEL(bits, storage, shift) \
+ _AD5446_CHANNEL(bits, storage, shift, NULL)
+
+#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \
+ _AD5446_CHANNEL(bits, storage, shift, ad5446_ext_info_powerdown)
+
+static int ad5446_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+ unsigned long scale_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ *val = st->cached_val;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+ *val = scale_uv / 1000;
+ *val2 = (scale_uv % 1000) * 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ }
+ return -EINVAL;
+}
+
+static int ad5446_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ val <<= chan->scan_type.shift;
+ mutex_lock(&indio_dev->mlock);
+ st->cached_val = val;
+ if (!st->pwr_down)
+ ret = st->chip_info->write(st, val);
+ mutex_unlock(&indio_dev->mlock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad5446_info = {
+ .read_raw = ad5446_read_raw,
+ .write_raw = ad5446_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad5446_probe(struct device *dev, const char *name,
+ const struct ad5446_chip_info *chip_info)
+{
+ struct ad5446_state *st;
+ struct iio_dev *indio_dev;
+ struct regulator *reg;
+ int ret, voltage_uv = 0;
+
+ reg = regulator_get(dev, "vcc");
+ if (!IS_ERR(reg)) {
+ ret = regulator_enable(reg);
+ if (ret)
+ goto error_put_reg;
+
+ ret = regulator_get_voltage(reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ voltage_uv = ret;
+ }
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_disable_reg;
+ }
+ st = iio_priv(indio_dev);
+ st->chip_info = chip_info;
+
+ dev_set_drvdata(dev, indio_dev);
+ st->reg = reg;
+ st->dev = dev;
+
+ /* Establish that the iio_dev is a child of the device */
+ indio_dev->dev.parent = dev;
+ indio_dev->name = name;
+ indio_dev->info = &ad5446_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &st->chip_info->channel;
+ indio_dev->num_channels = 1;
+
+ st->pwr_down_mode = MODE_PWRDWN_1k;
+
+ if (st->chip_info->int_vref_mv)
+ st->vref_mv = st->chip_info->int_vref_mv;
+ else if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else
+ dev_warn(dev, "reference voltage unspecified\n");
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_device;
+
+ return 0;
+
+error_free_device:
+ iio_device_free(indio_dev);
+error_disable_reg:
+ if (!IS_ERR(reg))
+ regulator_disable(reg);
+error_put_reg:
+ if (!IS_ERR(reg))
+ regulator_put(reg);
+
+ return ret;
+}
+
+static int ad5446_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad5446_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg)) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static int ad5446_write(struct ad5446_state *st, unsigned val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ __be16 data = cpu_to_be16(val);
+
+ return spi_write(spi, &data, sizeof(data));
+}
+
+static int ad5660_write(struct ad5446_state *st, unsigned val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ uint8_t data[3];
+
+ data[0] = (val >> 16) & 0xFF;
+ data[1] = (val >> 8) & 0xFF;
+ data[2] = val & 0xFF;
+
+ return spi_write(spi, data, sizeof(data));
+}
+
+/**
+ * ad5446_supported_spi_device_ids:
+ * The AD5620/40/60 parts are available in different fixed internal reference
+ * voltage options. The actual part numbers may look differently
+ * (and a bit cryptic), however this style is used to make clear which
+ * parts are supported here.
+ */
+enum ad5446_supported_spi_device_ids {
+ ID_AD5300,
+ ID_AD5310,
+ ID_AD5320,
+ ID_AD5444,
+ ID_AD5446,
+ ID_AD5450,
+ ID_AD5451,
+ ID_AD5541A,
+ ID_AD5512A,
+ ID_AD5553,
+ ID_AD5601,
+ ID_AD5611,
+ ID_AD5621,
+ ID_AD5620_2500,
+ ID_AD5620_1250,
+ ID_AD5640_2500,
+ ID_AD5640_1250,
+ ID_AD5660_2500,
+ ID_AD5660_1250,
+ ID_AD5662,
+};
+
+static const struct ad5446_chip_info ad5446_spi_chip_info[] = {
+ [ID_AD5300] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5310] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
+ .write = ad5446_write,
+ },
+ [ID_AD5320] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5444] = {
+ .channel = AD5446_CHANNEL(12, 16, 2),
+ .write = ad5446_write,
+ },
+ [ID_AD5446] = {
+ .channel = AD5446_CHANNEL(14, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5450] = {
+ .channel = AD5446_CHANNEL(8, 16, 6),
+ .write = ad5446_write,
+ },
+ [ID_AD5451] = {
+ .channel = AD5446_CHANNEL(10, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5541A] = {
+ .channel = AD5446_CHANNEL(16, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5512A] = {
+ .channel = AD5446_CHANNEL(12, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5553] = {
+ .channel = AD5446_CHANNEL(14, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5601] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6),
+ .write = ad5446_write,
+ },
+ [ID_AD5611] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5621] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+ .write = ad5446_write,
+ },
+ [ID_AD5620_2500] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+ .int_vref_mv = 2500,
+ .write = ad5446_write,
+ },
+ [ID_AD5620_1250] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+ .int_vref_mv = 1250,
+ .write = ad5446_write,
+ },
+ [ID_AD5640_2500] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
+ .int_vref_mv = 2500,
+ .write = ad5446_write,
+ },
+ [ID_AD5640_1250] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
+ .int_vref_mv = 1250,
+ .write = ad5446_write,
+ },
+ [ID_AD5660_2500] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+ .int_vref_mv = 2500,
+ .write = ad5660_write,
+ },
+ [ID_AD5660_1250] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+ .int_vref_mv = 1250,
+ .write = ad5660_write,
+ },
+ [ID_AD5662] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+ .write = ad5660_write,
+ },
+};
+
+static const struct spi_device_id ad5446_spi_ids[] = {
+ {"ad5300", ID_AD5300},
+ {"ad5310", ID_AD5310},
+ {"ad5320", ID_AD5320},
+ {"ad5444", ID_AD5444},
+ {"ad5446", ID_AD5446},
+ {"ad5450", ID_AD5450},
+ {"ad5451", ID_AD5451},
+ {"ad5452", ID_AD5444}, /* ad5452 is compatible to the ad5444 */
+ {"ad5453", ID_AD5446}, /* ad5453 is compatible to the ad5446 */
+ {"ad5512a", ID_AD5512A},
+ {"ad5541a", ID_AD5541A},
+ {"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */
+ {"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */
+ {"ad5553", ID_AD5553},
+ {"ad5601", ID_AD5601},
+ {"ad5611", ID_AD5611},
+ {"ad5621", ID_AD5621},
+ {"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */
+ {"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */
+ {"ad5640-2500", ID_AD5640_2500},
+ {"ad5640-1250", ID_AD5640_1250},
+ {"ad5660-2500", ID_AD5660_2500},
+ {"ad5660-1250", ID_AD5660_1250},
+ {"ad5662", ID_AD5662},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5446_spi_ids);
+
+static int ad5446_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ return ad5446_probe(&spi->dev, id->name,
+ &ad5446_spi_chip_info[id->driver_data]);
+}
+
+static int ad5446_spi_remove(struct spi_device *spi)
+{
+ return ad5446_remove(&spi->dev);
+}
+
+static struct spi_driver ad5446_spi_driver = {
+ .driver = {
+ .name = "ad5446",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5446_spi_probe,
+ .remove = ad5446_spi_remove,
+ .id_table = ad5446_spi_ids,
+};
+
+static int __init ad5446_spi_register_driver(void)
+{
+ return spi_register_driver(&ad5446_spi_driver);
+}
+
+static void ad5446_spi_unregister_driver(void)
+{
+ spi_unregister_driver(&ad5446_spi_driver);
+}
+
+#else
+
+static inline int ad5446_spi_register_driver(void) { return 0; }
+static inline void ad5446_spi_unregister_driver(void) { }
+
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int ad5622_write(struct ad5446_state *st, unsigned val)
+{
+ struct i2c_client *client = to_i2c_client(st->dev);
+ __be16 data = cpu_to_be16(val);
+
+ return i2c_master_send(client, (char *)&data, sizeof(data));
+}
+
+/**
+ * ad5446_supported_i2c_device_ids:
+ * The AD5620/40/60 parts are available in different fixed internal reference
+ * voltage options. The actual part numbers may look differently
+ * (and a bit cryptic), however this style is used to make clear which
+ * parts are supported here.
+ */
+enum ad5446_supported_i2c_device_ids {
+ ID_AD5602,
+ ID_AD5612,
+ ID_AD5622,
+};
+
+static const struct ad5446_chip_info ad5446_i2c_chip_info[] = {
+ [ID_AD5602] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
+ .write = ad5622_write,
+ },
+ [ID_AD5612] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
+ .write = ad5622_write,
+ },
+ [ID_AD5622] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
+ .write = ad5622_write,
+ },
+};
+
+static int ad5446_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return ad5446_probe(&i2c->dev, id->name,
+ &ad5446_i2c_chip_info[id->driver_data]);
+}
+
+static int ad5446_i2c_remove(struct i2c_client *i2c)
+{
+ return ad5446_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5446_i2c_ids[] = {
+ {"ad5301", ID_AD5602},
+ {"ad5311", ID_AD5612},
+ {"ad5321", ID_AD5622},
+ {"ad5602", ID_AD5602},
+ {"ad5612", ID_AD5612},
+ {"ad5622", ID_AD5622},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids);
+
+static struct i2c_driver ad5446_i2c_driver = {
+ .driver = {
+ .name = "ad5446",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5446_i2c_probe,
+ .remove = ad5446_i2c_remove,
+ .id_table = ad5446_i2c_ids,
+};
+
+static int __init ad5446_i2c_register_driver(void)
+{
+ return i2c_add_driver(&ad5446_i2c_driver);
+}
+
+static void __exit ad5446_i2c_unregister_driver(void)
+{
+ i2c_del_driver(&ad5446_i2c_driver);
+}
+
+#else
+
+static inline int ad5446_i2c_register_driver(void) { return 0; }
+static inline void ad5446_i2c_unregister_driver(void) { }
+
+#endif
+
+static int __init ad5446_init(void)
+{
+ int ret;
+
+ ret = ad5446_spi_register_driver();
+ if (ret)
+ return ret;
+
+ ret = ad5446_i2c_register_driver();
+ if (ret) {
+ ad5446_spi_unregister_driver();
+ return ret;
+ }
+
+ return 0;
+}
+module_init(ad5446_init);
+
+static void __exit ad5446_exit(void)
+{
+ ad5446_i2c_unregister_driver();
+ ad5446_spi_unregister_driver();
+}
+module_exit(ad5446_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c
new file mode 100644
index 00000000..ba1c914b
--- /dev/null
+++ b/drivers/iio/dac/ad5449.c
@@ -0,0 +1,376 @@
+/*
+ * AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog
+ * Converter driver.
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/platform_data/ad5449.h>
+
+#define AD5449_MAX_CHANNELS 2
+#define AD5449_MAX_VREFS 2
+
+#define AD5449_CMD_NOOP 0x0
+#define AD5449_CMD_LOAD_AND_UPDATE(x) (0x1 + (x) * 3)
+#define AD5449_CMD_READ(x) (0x2 + (x) * 3)
+#define AD5449_CMD_LOAD(x) (0x3 + (x) * 3)
+#define AD5449_CMD_CTRL 13
+
+#define AD5449_CTRL_SDO_OFFSET 10
+#define AD5449_CTRL_DAISY_CHAIN BIT(9)
+#define AD5449_CTRL_HCLR_TO_MIDSCALE BIT(8)
+#define AD5449_CTRL_SAMPLE_RISING BIT(7)
+
+/**
+ * struct ad5449_chip_info - chip specific information
+ * @channels: Channel specification
+ * @num_channels: Number of channels
+ * @has_ctrl: Chip has a control register
+ */
+struct ad5449_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ bool has_ctrl;
+};
+
+/**
+ * struct ad5449 - driver instance specific data
+ * @spi: the SPI device for this driver instance
+ * @chip_info: chip model specific constants, available modes etc
+ * @vref_reg: vref supply regulators
+ * @has_sdo: whether the SDO line is connected
+ * @dac_cache: Cache for the DAC values
+ * @data: spi transfer buffers
+ */
+struct ad5449 {
+ struct spi_device *spi;
+ const struct ad5449_chip_info *chip_info;
+ struct regulator_bulk_data vref_reg[AD5449_MAX_VREFS];
+
+ bool has_sdo;
+ uint16_t dac_cache[AD5449_MAX_CHANNELS];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 data[2] ____cacheline_aligned;
+};
+
+enum ad5449_type {
+ ID_AD5426,
+ ID_AD5429,
+ ID_AD5432,
+ ID_AD5439,
+ ID_AD5443,
+ ID_AD5449,
+};
+
+static int ad5449_write(struct iio_dev *indio_dev, unsigned int addr,
+ unsigned int val)
+{
+ struct ad5449 *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ st->data[0] = cpu_to_be16((addr << 12) | val);
+ ret = spi_write(st->spi, st->data, 2);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr,
+ unsigned int *val)
+{
+ struct ad5449 *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_message msg;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0],
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1],
+ .rx_buf = &st->data[1],
+ .len = 2,
+ },
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&t[0], &msg);
+ spi_message_add_tail(&t[1], &msg);
+
+ mutex_lock(&indio_dev->mlock);
+ st->data[0] = cpu_to_be16(addr << 12);
+ st->data[1] = cpu_to_be16(AD5449_CMD_NOOP);
+
+ ret = spi_sync(st->spi, &msg);
+ if (ret < 0)
+ goto out_unlock;
+
+ *val = be16_to_cpu(st->data[1]);
+
+out_unlock:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static int ad5449_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad5449 *st = iio_priv(indio_dev);
+ struct regulator_bulk_data *reg;
+ int scale_uv;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (st->has_sdo) {
+ ret = ad5449_read(indio_dev,
+ AD5449_CMD_READ(chan->address), val);
+ if (ret)
+ return ret;
+ *val &= 0xfff;
+ } else {
+ *val = st->dac_cache[chan->address];
+ }
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ reg = &st->vref_reg[chan->channel];
+ scale_uv = regulator_get_voltage(reg->consumer);
+ if (scale_uv < 0)
+ return scale_uv;
+
+ *val = scale_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5449_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+ struct ad5449 *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (val < 0 || val >= (1 << chan->scan_type.realbits))
+ return -EINVAL;
+
+ ret = ad5449_write(indio_dev,
+ AD5449_CMD_LOAD_AND_UPDATE(chan->address),
+ val << chan->scan_type.shift);
+ if (ret == 0)
+ st->dac_cache[chan->address] = val;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad5449_info = {
+ .read_raw = ad5449_read_raw,
+ .write_raw = ad5449_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define AD5449_CHANNEL(chan, bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (chan), \
+ .scan_type = IIO_ST('u', (bits), 16, 12 - (bits)), \
+}
+
+#define DECLARE_AD5449_CHANNELS(name, bits) \
+const struct iio_chan_spec name[] = { \
+ AD5449_CHANNEL(0, bits), \
+ AD5449_CHANNEL(1, bits), \
+}
+
+static DECLARE_AD5449_CHANNELS(ad5429_channels, 8);
+static DECLARE_AD5449_CHANNELS(ad5439_channels, 10);
+static DECLARE_AD5449_CHANNELS(ad5449_channels, 12);
+
+static const struct ad5449_chip_info ad5449_chip_info[] = {
+ [ID_AD5426] = {
+ .channels = ad5429_channels,
+ .num_channels = 1,
+ .has_ctrl = false,
+ },
+ [ID_AD5429] = {
+ .channels = ad5429_channels,
+ .num_channels = 2,
+ .has_ctrl = true,
+ },
+ [ID_AD5432] = {
+ .channels = ad5439_channels,
+ .num_channels = 1,
+ .has_ctrl = false,
+ },
+ [ID_AD5439] = {
+ .channels = ad5439_channels,
+ .num_channels = 2,
+ .has_ctrl = true,
+ },
+ [ID_AD5443] = {
+ .channels = ad5449_channels,
+ .num_channels = 1,
+ .has_ctrl = false,
+ },
+ [ID_AD5449] = {
+ .channels = ad5449_channels,
+ .num_channels = 2,
+ .has_ctrl = true,
+ },
+};
+
+static const char *ad5449_vref_name(struct ad5449 *st, int n)
+{
+ if (st->chip_info->num_channels == 1)
+ return "VREF";
+
+ if (n == 0)
+ return "VREFA";
+ else
+ return "VREFB";
+}
+
+static int ad5449_spi_probe(struct spi_device *spi)
+{
+ struct ad5449_platform_data *pdata = spi->dev.platform_data;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct ad5449 *st;
+ unsigned int i;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->chip_info = &ad5449_chip_info[id->driver_data];
+ st->spi = spi;
+
+ for (i = 0; i < st->chip_info->num_channels; ++i)
+ st->vref_reg[i].supply = ad5449_vref_name(st, i);
+
+ ret = regulator_bulk_get(&spi->dev, st->chip_info->num_channels,
+ st->vref_reg);
+ if (ret)
+ goto error_free;
+
+ ret = regulator_bulk_enable(st->chip_info->num_channels, st->vref_reg);
+ if (ret)
+ goto error_free_reg;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = id->name;
+ indio_dev->info = &ad5449_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ if (st->chip_info->has_ctrl) {
+ unsigned int ctrl = 0x00;
+ if (pdata) {
+ if (pdata->hardware_clear_to_midscale)
+ ctrl |= AD5449_CTRL_HCLR_TO_MIDSCALE;
+ ctrl |= pdata->sdo_mode << AD5449_CTRL_SDO_OFFSET;
+ st->has_sdo = pdata->sdo_mode != AD5449_SDO_DISABLED;
+ } else {
+ st->has_sdo = true;
+ }
+ ad5449_write(indio_dev, AD5449_CMD_CTRL, ctrl);
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg);
+error_free_reg:
+ regulator_bulk_free(st->chip_info->num_channels, st->vref_reg);
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad5449_spi_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5449 *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg);
+ regulator_bulk_free(st->chip_info->num_channels, st->vref_reg);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5449_spi_ids[] = {
+ { "ad5415", ID_AD5449 },
+ { "ad5426", ID_AD5426 },
+ { "ad5429", ID_AD5429 },
+ { "ad5432", ID_AD5432 },
+ { "ad5439", ID_AD5439 },
+ { "ad5443", ID_AD5443 },
+ { "ad5449", ID_AD5449 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5449_spi_ids);
+
+static struct spi_driver ad5449_spi_driver = {
+ .driver = {
+ .name = "ad5449",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5449_spi_probe,
+ .remove = ad5449_spi_remove,
+ .id_table = ad5449_spi_ids,
+};
+module_spi_driver(ad5449_spi_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5449 and similar DACs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c
new file mode 100644
index 00000000..139206e8
--- /dev/null
+++ b/drivers/iio/dac/ad5504.c
@@ -0,0 +1,393 @@
+/*
+ * AD5504, AD5501 High Voltage Digital to Analog Converter
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/dac/ad5504.h>
+
+#define AD5505_BITS 12
+#define AD5504_RES_MASK ((1 << (AD5505_BITS)) - 1)
+
+#define AD5504_CMD_READ (1 << 15)
+#define AD5504_CMD_WRITE (0 << 15)
+#define AD5504_ADDR(addr) ((addr) << 12)
+
+/* Registers */
+#define AD5504_ADDR_NOOP 0
+#define AD5504_ADDR_DAC(x) ((x) + 1)
+#define AD5504_ADDR_ALL_DAC 5
+#define AD5504_ADDR_CTRL 7
+
+/* Control Register */
+#define AD5504_DAC_PWR(ch) ((ch) << 2)
+#define AD5504_DAC_PWRDWN_MODE(mode) ((mode) << 6)
+#define AD5504_DAC_PWRDN_20K 0
+#define AD5504_DAC_PWRDN_3STATE 1
+
+/**
+ * struct ad5446_state - driver instance specific data
+ * @us: spi_device
+ * @reg: supply regulator
+ * @vref_mv: actual reference voltage used
+ * @pwr_down_mask power down mask
+ * @pwr_down_mode current power down mode
+ */
+
+struct ad5504_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned short vref_mv;
+ unsigned pwr_down_mask;
+ unsigned pwr_down_mode;
+};
+
+/**
+ * ad5504_supported_device_ids:
+ */
+
+enum ad5504_supported_device_ids {
+ ID_AD5504,
+ ID_AD5501,
+};
+
+static int ad5504_spi_write(struct spi_device *spi, u8 addr, u16 val)
+{
+ u16 tmp = cpu_to_be16(AD5504_CMD_WRITE |
+ AD5504_ADDR(addr) |
+ (val & AD5504_RES_MASK));
+
+ return spi_write(spi, (u8 *)&tmp, 2);
+}
+
+static int ad5504_spi_read(struct spi_device *spi, u8 addr)
+{
+ u16 tmp = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr));
+ u16 val;
+ int ret;
+ struct spi_transfer t = {
+ .tx_buf = &tmp,
+ .rx_buf = &val,
+ .len = 2,
+ };
+ ret = spi_sync_transfer(spi, &t, 1);
+
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu(val) & AD5504_RES_MASK;
+}
+
+static int ad5504_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+ unsigned long scale_uv;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5504_spi_read(st->spi, chan->address);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+ *val = scale_uv / 1000;
+ *val2 = (scale_uv % 1000) * 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ }
+ return -EINVAL;
+}
+
+static int ad5504_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ return ad5504_spi_write(st->spi, chan->address, val);
+ default:
+ ret = -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static const char * const ad5504_powerdown_modes[] = {
+ "20kohm_to_gnd",
+ "three_state",
+};
+
+static int ad5504_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode;
+}
+
+static int ad5504_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode = mode;
+
+ return 0;
+}
+
+static const struct iio_enum ad5504_powerdown_mode_enum = {
+ .items = ad5504_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5504_powerdown_modes),
+ .get = ad5504_get_powerdown_mode,
+ .set = ad5504_set_powerdown_mode,
+};
+
+static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ !(st->pwr_down_mask & (1 << chan->channel)));
+}
+
+static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ bool pwr_down;
+ int ret;
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ if (pwr_down)
+ st->pwr_down_mask |= (1 << chan->channel);
+ else
+ st->pwr_down_mask &= ~(1 << chan->channel);
+
+ ret = ad5504_spi_write(st->spi, AD5504_ADDR_CTRL,
+ AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) |
+ AD5504_DAC_PWR(st->pwr_down_mask));
+
+ /* writes to the CTRL register must be followed by a NOOP */
+ ad5504_spi_write(st->spi, AD5504_ADDR_NOOP, 0);
+
+ return ret ? ret : len;
+}
+
+static IIO_CONST_ATTR(temp0_thresh_rising_value, "110000");
+static IIO_CONST_ATTR(temp0_thresh_rising_en, "1");
+
+static struct attribute *ad5504_ev_attributes[] = {
+ &iio_const_attr_temp0_thresh_rising_value.dev_attr.attr,
+ &iio_const_attr_temp0_thresh_rising_en.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ad5504_ev_attribute_group = {
+ .attrs = ad5504_ev_attributes,
+ .name = "events",
+};
+
+static irqreturn_t ad5504_event_handler(int irq, void *private)
+{
+ iio_push_event(private,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns());
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info ad5504_info = {
+ .write_raw = ad5504_write_raw,
+ .read_raw = ad5504_read_raw,
+ .event_attrs = &ad5504_ev_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec_ext_info ad5504_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5504_read_dac_powerdown,
+ .write = ad5504_write_dac_powerdown,
+ },
+ IIO_ENUM("powerdown_mode", true, &ad5504_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum),
+ { },
+};
+
+#define AD5504_CHANNEL(_chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = AD5504_ADDR_DAC(_chan), \
+ .scan_type = IIO_ST('u', 12, 16, 0), \
+ .ext_info = ad5504_ext_info, \
+}
+
+static const struct iio_chan_spec ad5504_channels[] = {
+ AD5504_CHANNEL(0),
+ AD5504_CHANNEL(1),
+ AD5504_CHANNEL(2),
+ AD5504_CHANNEL(3),
+};
+
+static int ad5504_probe(struct spi_device *spi)
+{
+ struct ad5504_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad5504_state *st;
+ struct regulator *reg;
+ int ret, voltage_uv = 0;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ reg = regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(reg)) {
+ ret = regulator_enable(reg);
+ if (ret)
+ goto error_put_reg;
+
+ ret = regulator_get_voltage(reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ voltage_uv = ret;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ st = iio_priv(indio_dev);
+ if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else if (pdata)
+ st->vref_mv = pdata->vref_mv;
+ else
+ dev_warn(&spi->dev, "reference voltage unspecified\n");
+
+ st->reg = reg;
+ st->spi = spi;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(st->spi)->name;
+ indio_dev->info = &ad5504_info;
+ if (spi_get_device_id(st->spi)->driver_data == ID_AD5501)
+ indio_dev->num_channels = 1;
+ else
+ indio_dev->num_channels = 4;
+ indio_dev->channels = ad5504_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (spi->irq) {
+ ret = request_threaded_irq(spi->irq,
+ NULL,
+ &ad5504_event_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ spi_get_device_id(st->spi)->name,
+ indio_dev);
+ if (ret)
+ goto error_disable_reg;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_irq;
+
+ return 0;
+
+error_free_irq:
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+error_disable_reg:
+ if (!IS_ERR(reg))
+ regulator_disable(reg);
+error_put_reg:
+ if (!IS_ERR(reg))
+ regulator_put(reg);
+
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+static int ad5504_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+
+ if (!IS_ERR(st->reg)) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5504_id[] = {
+ {"ad5504", ID_AD5504},
+ {"ad5501", ID_AD5501},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5504_id);
+
+static struct spi_driver ad5504_driver = {
+ .driver = {
+ .name = "ad5504",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5504_probe,
+ .remove = ad5504_remove,
+ .id_table = ad5504_id,
+};
+module_spi_driver(ad5504_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5624r.h b/drivers/iio/dac/ad5624r.h
new file mode 100644
index 00000000..5dca3028
--- /dev/null
+++ b/drivers/iio/dac/ad5624r.h
@@ -0,0 +1,79 @@
+/*
+ * AD5624R SPI DAC driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+#ifndef SPI_AD5624R_H_
+#define SPI_AD5624R_H_
+
+#define AD5624R_DAC_CHANNELS 4
+
+#define AD5624R_ADDR_DAC0 0x0
+#define AD5624R_ADDR_DAC1 0x1
+#define AD5624R_ADDR_DAC2 0x2
+#define AD5624R_ADDR_DAC3 0x3
+#define AD5624R_ADDR_ALL_DAC 0x7
+
+#define AD5624R_CMD_WRITE_INPUT_N 0x0
+#define AD5624R_CMD_UPDATE_DAC_N 0x1
+#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
+#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_N 0x3
+#define AD5624R_CMD_POWERDOWN_DAC 0x4
+#define AD5624R_CMD_RESET 0x5
+#define AD5624R_CMD_LDAC_SETUP 0x6
+#define AD5624R_CMD_INTERNAL_REFER_SETUP 0x7
+
+#define AD5624R_LDAC_PWRDN_NONE 0x0
+#define AD5624R_LDAC_PWRDN_1K 0x1
+#define AD5624R_LDAC_PWRDN_100K 0x2
+#define AD5624R_LDAC_PWRDN_3STATE 0x3
+
+/**
+ * struct ad5624r_chip_info - chip specific information
+ * @channels: channel spec for the DAC
+ * @int_vref_mv: AD5620/40/60: the internal reference voltage
+ */
+
+struct ad5624r_chip_info {
+ const struct iio_chan_spec *channels;
+ u16 int_vref_mv;
+};
+
+/**
+ * struct ad5446_state - driver instance specific data
+ * @indio_dev: the industrial I/O device
+ * @us: spi_device
+ * @chip_info: chip model specific constants, available modes etc
+ * @reg: supply regulator
+ * @vref_mv: actual reference voltage used
+ * @pwr_down_mask power down mask
+ * @pwr_down_mode current power down mode
+ */
+
+struct ad5624r_state {
+ struct spi_device *us;
+ const struct ad5624r_chip_info *chip_info;
+ struct regulator *reg;
+ unsigned short vref_mv;
+ unsigned pwr_down_mask;
+ unsigned pwr_down_mode;
+};
+
+/**
+ * ad5624r_supported_device_ids:
+ * The AD5624/44/64 parts are available in different
+ * fixed internal reference voltage options.
+ */
+
+enum ad5624r_supported_device_ids {
+ ID_AD5624R3,
+ ID_AD5644R3,
+ ID_AD5664R3,
+ ID_AD5624R5,
+ ID_AD5644R5,
+ ID_AD5664R5,
+};
+
+#endif /* SPI_AD5624R_H_ */
diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c
new file mode 100644
index 00000000..bb298aaf
--- /dev/null
+++ b/drivers/iio/dac/ad5624r_spi.c
@@ -0,0 +1,328 @@
+/*
+ * AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "ad5624r.h"
+
+static int ad5624r_spi_write(struct spi_device *spi,
+ u8 cmd, u8 addr, u16 val, u8 len)
+{
+ u32 data;
+ u8 msg[3];
+
+ /*
+ * The input shift register is 24 bits wide. The first two bits are
+ * don't care bits. The next three are the command bits, C2 to C0,
+ * followed by the 3-bit DAC address, A2 to A0, and then the
+ * 16-, 14-, 12-bit data-word. The data-word comprises the 16-,
+ * 14-, 12-bit input code followed by 0, 2, or 4 don't care bits,
+ * for the AD5664R, AD5644R, and AD5624R, respectively.
+ */
+ data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << (16 - len));
+ msg[0] = data >> 16;
+ msg[1] = data >> 8;
+ msg[2] = data;
+
+ return spi_write(spi, msg, 3);
+}
+
+static int ad5624r_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+ unsigned long scale_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+ *val = scale_uv / 1000;
+ *val2 = (scale_uv % 1000) * 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ }
+ return -EINVAL;
+}
+
+static int ad5624r_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ return ad5624r_spi_write(st->us,
+ AD5624R_CMD_WRITE_INPUT_N_UPDATE_N,
+ chan->address, val,
+ chan->scan_type.shift);
+ default:
+ ret = -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static const char * const ad5624r_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "three_state"
+};
+
+static int ad5624r_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode;
+}
+
+static int ad5624r_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode = mode;
+
+ return 0;
+}
+
+static const struct iio_enum ad5624r_powerdown_mode_enum = {
+ .items = ad5624r_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5624r_powerdown_modes),
+ .get = ad5624r_get_powerdown_mode,
+ .set = ad5624r_set_powerdown_mode,
+};
+
+static ssize_t ad5624r_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ !!(st->pwr_down_mask & (1 << chan->channel)));
+}
+
+static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ bool pwr_down;
+ int ret;
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ if (pwr_down)
+ st->pwr_down_mask |= (1 << chan->channel);
+ else
+ st->pwr_down_mask &= ~(1 << chan->channel);
+
+ ret = ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0,
+ (st->pwr_down_mode << 4) |
+ st->pwr_down_mask, 16);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_info ad5624r_info = {
+ .write_raw = ad5624r_write_raw,
+ .read_raw = ad5624r_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5624r_read_dac_powerdown,
+ .write = ad5624r_write_dac_powerdown,
+ },
+ IIO_ENUM("powerdown_mode", true, &ad5624r_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum),
+ { },
+};
+
+#define AD5624R_CHANNEL(_chan, _bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (_chan), \
+ .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \
+ .ext_info = ad5624r_ext_info, \
+}
+
+#define DECLARE_AD5624R_CHANNELS(_name, _bits) \
+ const struct iio_chan_spec _name##_channels[] = { \
+ AD5624R_CHANNEL(0, _bits), \
+ AD5624R_CHANNEL(1, _bits), \
+ AD5624R_CHANNEL(2, _bits), \
+ AD5624R_CHANNEL(3, _bits), \
+}
+
+static DECLARE_AD5624R_CHANNELS(ad5624r, 12);
+static DECLARE_AD5624R_CHANNELS(ad5644r, 14);
+static DECLARE_AD5624R_CHANNELS(ad5664r, 16);
+
+static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = {
+ [ID_AD5624R3] = {
+ .channels = ad5624r_channels,
+ .int_vref_mv = 1250,
+ },
+ [ID_AD5624R5] = {
+ .channels = ad5624r_channels,
+ .int_vref_mv = 2500,
+ },
+ [ID_AD5644R3] = {
+ .channels = ad5644r_channels,
+ .int_vref_mv = 1250,
+ },
+ [ID_AD5644R5] = {
+ .channels = ad5644r_channels,
+ .int_vref_mv = 2500,
+ },
+ [ID_AD5664R3] = {
+ .channels = ad5664r_channels,
+ .int_vref_mv = 1250,
+ },
+ [ID_AD5664R5] = {
+ .channels = ad5664r_channels,
+ .int_vref_mv = 2500,
+ },
+};
+
+static int ad5624r_probe(struct spi_device *spi)
+{
+ struct ad5624r_state *st;
+ struct iio_dev *indio_dev;
+ int ret, voltage_uv = 0;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ st = iio_priv(indio_dev);
+ st->reg = regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ voltage_uv = ret;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ st->chip_info =
+ &ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else
+ st->vref_mv = st->chip_info->int_vref_mv;
+
+ st->us = spi;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5624r_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = AD5624R_DAC_CHANNELS;
+
+ ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0,
+ !!voltage_uv, 16);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+error_put_reg:
+ if (!IS_ERR(st->reg))
+ regulator_put(st->reg);
+ iio_device_free(indio_dev);
+error_ret:
+
+ return ret;
+}
+
+static int ad5624r_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg)) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5624r_id[] = {
+ {"ad5624r3", ID_AD5624R3},
+ {"ad5644r3", ID_AD5644R3},
+ {"ad5664r3", ID_AD5664R3},
+ {"ad5624r5", ID_AD5624R5},
+ {"ad5644r5", ID_AD5644R5},
+ {"ad5664r5", ID_AD5664R5},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5624r_id);
+
+static struct spi_driver ad5624r_driver = {
+ .driver = {
+ .name = "ad5624r",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5624r_probe,
+ .remove = ad5624r_remove,
+ .id_table = ad5624r_id,
+};
+module_spi_driver(ad5624r_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
new file mode 100644
index 00000000..06439b1a
--- /dev/null
+++ b/drivers/iio/dac/ad5686.c
@@ -0,0 +1,417 @@
+/*
+ * AD5686R, AD5685R, AD5684R Digital to analog converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AD5686_DAC_CHANNELS 4
+
+#define AD5686_ADDR(x) ((x) << 16)
+#define AD5686_CMD(x) ((x) << 20)
+
+#define AD5686_ADDR_DAC(chan) (0x1 << (chan))
+#define AD5686_ADDR_ALL_DAC 0xF
+
+#define AD5686_CMD_NOOP 0x0
+#define AD5686_CMD_WRITE_INPUT_N 0x1
+#define AD5686_CMD_UPDATE_DAC_N 0x2
+#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3
+#define AD5686_CMD_POWERDOWN_DAC 0x4
+#define AD5686_CMD_LDAC_MASK 0x5
+#define AD5686_CMD_RESET 0x6
+#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7
+#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8
+#define AD5686_CMD_READBACK_ENABLE 0x9
+
+#define AD5686_LDAC_PWRDN_NONE 0x0
+#define AD5686_LDAC_PWRDN_1K 0x1
+#define AD5686_LDAC_PWRDN_100K 0x2
+#define AD5686_LDAC_PWRDN_3STATE 0x3
+
+/**
+ * struct ad5686_chip_info - chip specific information
+ * @int_vref_mv: AD5620/40/60: the internal reference voltage
+ * @channel: channel specification
+*/
+
+struct ad5686_chip_info {
+ u16 int_vref_mv;
+ struct iio_chan_spec channel[AD5686_DAC_CHANNELS];
+};
+
+/**
+ * struct ad5446_state - driver instance specific data
+ * @spi: spi_device
+ * @chip_info: chip model specific constants, available modes etc
+ * @reg: supply regulator
+ * @vref_mv: actual reference voltage used
+ * @pwr_down_mask: power down mask
+ * @pwr_down_mode: current power down mode
+ * @data: spi transfer buffers
+ */
+
+struct ad5686_state {
+ struct spi_device *spi;
+ const struct ad5686_chip_info *chip_info;
+ struct regulator *reg;
+ unsigned short vref_mv;
+ unsigned pwr_down_mask;
+ unsigned pwr_down_mode;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+
+ union {
+ u32 d32;
+ u8 d8[4];
+ } data[3] ____cacheline_aligned;
+};
+
+/**
+ * ad5686_supported_device_ids:
+ */
+
+enum ad5686_supported_device_ids {
+ ID_AD5684,
+ ID_AD5685,
+ ID_AD5686,
+};
+static int ad5686_spi_write(struct ad5686_state *st,
+ u8 cmd, u8 addr, u16 val, u8 shift)
+{
+ val <<= shift;
+
+ st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
+ AD5686_ADDR(addr) |
+ val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
+{
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1].d8[1],
+ .rx_buf = &st->data[2].d8[1],
+ .len = 3,
+ },
+ };
+ int ret;
+
+ st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) |
+ AD5686_ADDR(addr));
+ st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ return be32_to_cpu(st->data[2].d32);
+}
+
+static const char * const ad5686_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "three_state"
+};
+
+static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1;
+}
+
+static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode &= ~(0x3 << (chan->channel * 2));
+ st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2));
+
+ return 0;
+}
+
+static const struct iio_enum ad5686_powerdown_mode_enum = {
+ .items = ad5686_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5686_powerdown_modes),
+ .get = ad5686_get_powerdown_mode,
+ .set = ad5686_set_powerdown_mode,
+};
+
+static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", !!(st->pwr_down_mask &
+ (0x3 << (chan->channel * 2))));
+}
+
+static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ bool readin;
+ int ret;
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ ret = strtobool(buf, &readin);
+ if (ret)
+ return ret;
+
+ if (readin)
+ st->pwr_down_mask |= (0x3 << (chan->channel * 2));
+ else
+ st->pwr_down_mask &= ~(0x3 << (chan->channel * 2));
+
+ ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0,
+ st->pwr_down_mask & st->pwr_down_mode, 0);
+
+ return ret ? ret : len;
+}
+
+static int ad5686_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+ unsigned long scale_uv;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5686_spi_read(st, chan->address);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = (st->vref_mv * 100000)
+ >> (chan->scan_type.realbits);
+ *val = scale_uv / 100000;
+ *val2 = (scale_uv % 100000) * 10;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ }
+ return -EINVAL;
+}
+
+static int ad5686_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val > (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5686_spi_write(st,
+ AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
+ chan->address,
+ val,
+ chan->scan_type.shift);
+ mutex_unlock(&indio_dev->mlock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad5686_info = {
+ .read_raw = ad5686_read_raw,
+ .write_raw = ad5686_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5686_read_dac_powerdown,
+ .write = ad5686_write_dac_powerdown,
+ },
+ IIO_ENUM("powerdown_mode", false, &ad5686_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum),
+ { },
+};
+
+#define AD5868_CHANNEL(chan, bits, shift) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+ .address = AD5686_ADDR_DAC(chan), \
+ .scan_type = IIO_ST('u', bits, 16, shift), \
+ .ext_info = ad5686_ext_info, \
+}
+
+static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
+ [ID_AD5684] = {
+ .channel[0] = AD5868_CHANNEL(0, 12, 4),
+ .channel[1] = AD5868_CHANNEL(1, 12, 4),
+ .channel[2] = AD5868_CHANNEL(2, 12, 4),
+ .channel[3] = AD5868_CHANNEL(3, 12, 4),
+ .int_vref_mv = 2500,
+ },
+ [ID_AD5685] = {
+ .channel[0] = AD5868_CHANNEL(0, 14, 2),
+ .channel[1] = AD5868_CHANNEL(1, 14, 2),
+ .channel[2] = AD5868_CHANNEL(2, 14, 2),
+ .channel[3] = AD5868_CHANNEL(3, 14, 2),
+ .int_vref_mv = 2500,
+ },
+ [ID_AD5686] = {
+ .channel[0] = AD5868_CHANNEL(0, 16, 0),
+ .channel[1] = AD5868_CHANNEL(1, 16, 0),
+ .channel[2] = AD5868_CHANNEL(2, 16, 0),
+ .channel[3] = AD5868_CHANNEL(3, 16, 0),
+ .int_vref_mv = 2500,
+ },
+};
+
+
+static int ad5686_probe(struct spi_device *spi)
+{
+ struct ad5686_state *st;
+ struct iio_dev *indio_dev;
+ int ret, regdone = 0, voltage_uv = 0;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->reg = regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ voltage_uv = ret;
+ }
+
+ st->chip_info =
+ &ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else
+ st->vref_mv = st->chip_info->int_vref_mv;
+
+ st->spi = spi;
+
+ /* Set all the power down mode for all channels to 1K pulldown */
+ st->pwr_down_mode = 0x55;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5686_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channel;
+ indio_dev->num_channels = AD5686_DAC_CHANNELS;
+
+ regdone = 1;
+ ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0,
+ !!voltage_uv, 0);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+error_put_reg:
+ if (!IS_ERR(st->reg))
+ regulator_put(st->reg);
+
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad5686_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg)) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5686_id[] = {
+ {"ad5684", ID_AD5684},
+ {"ad5685", ID_AD5685},
+ {"ad5686", ID_AD5686},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5686_id);
+
+static struct spi_driver ad5686_driver = {
+ .driver = {
+ .name = "ad5686",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5686_probe,
+ .remove = ad5686_remove,
+ .id_table = ad5686_id,
+};
+module_spi_driver(ad5686_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
new file mode 100644
index 00000000..12bb315e
--- /dev/null
+++ b/drivers/iio/dac/ad5755.c
@@ -0,0 +1,645 @@
+/*
+ * AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/platform_data/ad5755.h>
+
+#define AD5755_NUM_CHANNELS 4
+
+#define AD5755_ADDR(x) ((x) << 16)
+
+#define AD5755_WRITE_REG_DATA(chan) (chan)
+#define AD5755_WRITE_REG_GAIN(chan) (0x08 | (chan))
+#define AD5755_WRITE_REG_OFFSET(chan) (0x10 | (chan))
+#define AD5755_WRITE_REG_CTRL(chan) (0x1c | (chan))
+
+#define AD5755_READ_REG_DATA(chan) (chan)
+#define AD5755_READ_REG_CTRL(chan) (0x4 | (chan))
+#define AD5755_READ_REG_GAIN(chan) (0x8 | (chan))
+#define AD5755_READ_REG_OFFSET(chan) (0xc | (chan))
+#define AD5755_READ_REG_CLEAR(chan) (0x10 | (chan))
+#define AD5755_READ_REG_SLEW(chan) (0x14 | (chan))
+#define AD5755_READ_REG_STATUS 0x18
+#define AD5755_READ_REG_MAIN 0x19
+#define AD5755_READ_REG_DC_DC 0x1a
+
+#define AD5755_CTRL_REG_SLEW 0x0
+#define AD5755_CTRL_REG_MAIN 0x1
+#define AD5755_CTRL_REG_DAC 0x2
+#define AD5755_CTRL_REG_DC_DC 0x3
+#define AD5755_CTRL_REG_SW 0x4
+
+#define AD5755_READ_FLAG 0x800000
+
+#define AD5755_NOOP 0x1CE000
+
+#define AD5755_DAC_INT_EN BIT(8)
+#define AD5755_DAC_CLR_EN BIT(7)
+#define AD5755_DAC_OUT_EN BIT(6)
+#define AD5755_DAC_INT_CURRENT_SENSE_RESISTOR BIT(5)
+#define AD5755_DAC_DC_DC_EN BIT(4)
+#define AD5755_DAC_VOLTAGE_OVERRANGE_EN BIT(3)
+
+#define AD5755_DC_DC_MAXV 0
+#define AD5755_DC_DC_FREQ_SHIFT 2
+#define AD5755_DC_DC_PHASE_SHIFT 4
+#define AD5755_EXT_DC_DC_COMP_RES BIT(6)
+
+#define AD5755_SLEW_STEP_SIZE_SHIFT 0
+#define AD5755_SLEW_RATE_SHIFT 3
+#define AD5755_SLEW_ENABLE BIT(12)
+
+/**
+ * struct ad5755_chip_info - chip specific information
+ * @channel_template: channel specification
+ * @calib_shift: shift for the calibration data registers
+ * @has_voltage_out: whether the chip has voltage outputs
+ */
+struct ad5755_chip_info {
+ const struct iio_chan_spec channel_template;
+ unsigned int calib_shift;
+ bool has_voltage_out;
+};
+
+/**
+ * struct ad5755_state - driver instance specific data
+ * @spi: spi device the driver is attached to
+ * @chip_info: chip model specific constants, available modes etc
+ * @pwr_down: bitmask which contains hether a channel is powered down or not
+ * @ctrl: software shadow of the channel ctrl registers
+ * @channels: iio channel spec for the device
+ * @data: spi transfer buffers
+ */
+struct ad5755_state {
+ struct spi_device *spi;
+ const struct ad5755_chip_info *chip_info;
+ unsigned int pwr_down;
+ unsigned int ctrl[AD5755_NUM_CHANNELS];
+ struct iio_chan_spec channels[AD5755_NUM_CHANNELS];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+
+ union {
+ u32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+enum ad5755_type {
+ ID_AD5755,
+ ID_AD5757,
+ ID_AD5735,
+ ID_AD5737,
+};
+
+static int ad5755_write_unlocked(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int val)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+
+ st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5755_write_ctrl_unlocked(struct iio_dev *indio_dev,
+ unsigned int channel, unsigned int reg, unsigned int val)
+{
+ return ad5755_write_unlocked(indio_dev,
+ AD5755_WRITE_REG_CTRL(channel), (reg << 13) | val);
+}
+
+static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int val)
+{
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5755_write_unlocked(indio_dev, reg, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel,
+ unsigned int reg, unsigned int val)
+{
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1].d8[1],
+ .rx_buf = &st->data[1].d8[1],
+ .len = 3,
+ },
+ };
+
+ mutex_lock(&indio_dev->mlock);
+
+ st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16));
+ st->data[1].d32 = cpu_to_be32(AD5755_NOOP);
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret >= 0)
+ ret = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5755_update_dac_ctrl(struct iio_dev *indio_dev,
+ unsigned int channel, unsigned int set, unsigned int clr)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ int ret;
+
+ st->ctrl[channel] |= set;
+ st->ctrl[channel] &= ~clr;
+
+ ret = ad5755_write_ctrl_unlocked(indio_dev, channel,
+ AD5755_CTRL_REG_DAC, st->ctrl[channel]);
+
+ return ret;
+}
+
+static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev,
+ unsigned int channel, bool pwr_down)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int mask = BIT(channel);
+
+ mutex_lock(&indio_dev->mlock);
+
+ if ((bool)(st->pwr_down & mask) == pwr_down)
+ goto out_unlock;
+
+ if (!pwr_down) {
+ st->pwr_down &= ~mask;
+ ad5755_update_dac_ctrl(indio_dev, channel,
+ AD5755_DAC_INT_EN | AD5755_DAC_DC_DC_EN, 0);
+ udelay(200);
+ ad5755_update_dac_ctrl(indio_dev, channel,
+ AD5755_DAC_OUT_EN, 0);
+ } else {
+ st->pwr_down |= mask;
+ ad5755_update_dac_ctrl(indio_dev, channel,
+ 0, AD5755_DAC_INT_EN | AD5755_DAC_OUT_EN |
+ AD5755_DAC_DC_DC_EN);
+ }
+
+out_unlock:
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+
+static const int ad5755_min_max_table[][2] = {
+ [AD5755_MODE_VOLTAGE_0V_5V] = { 0, 5000 },
+ [AD5755_MODE_VOLTAGE_0V_10V] = { 0, 10000 },
+ [AD5755_MODE_VOLTAGE_PLUSMINUS_5V] = { -5000, 5000 },
+ [AD5755_MODE_VOLTAGE_PLUSMINUS_10V] = { -10000, 10000 },
+ [AD5755_MODE_CURRENT_4mA_20mA] = { 4, 20 },
+ [AD5755_MODE_CURRENT_0mA_20mA] = { 0, 20 },
+ [AD5755_MODE_CURRENT_0mA_24mA] = { 0, 24 },
+};
+
+static void ad5755_get_min_max(struct ad5755_state *st,
+ struct iio_chan_spec const *chan, int *min, int *max)
+{
+ enum ad5755_mode mode = st->ctrl[chan->channel] & 7;
+ *min = ad5755_min_max_table[mode][0];
+ *max = ad5755_min_max_table[mode][1];
+}
+
+static inline int ad5755_get_offset(struct ad5755_state *st,
+ struct iio_chan_spec const *chan)
+{
+ int min, max;
+
+ ad5755_get_min_max(st, chan, &min, &max);
+ return (min * (1 << chan->scan_type.realbits)) / (max - min);
+}
+
+static inline int ad5755_get_scale(struct ad5755_state *st,
+ struct iio_chan_spec const *chan)
+{
+ int min, max;
+
+ ad5755_get_min_max(st, chan, &min, &max);
+ return ((max - min) * 1000000000ULL) >> chan->scan_type.realbits;
+}
+
+static int ad5755_chan_reg_info(struct ad5755_state *st,
+ struct iio_chan_spec const *chan, long info, bool write,
+ unsigned int *reg, unsigned int *shift, unsigned int *offset)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (write)
+ *reg = AD5755_WRITE_REG_DATA(chan->address);
+ else
+ *reg = AD5755_READ_REG_DATA(chan->address);
+ *shift = chan->scan_type.shift;
+ *offset = 0;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (write)
+ *reg = AD5755_WRITE_REG_OFFSET(chan->address);
+ else
+ *reg = AD5755_READ_REG_OFFSET(chan->address);
+ *shift = st->chip_info->calib_shift;
+ *offset = 32768;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (write)
+ *reg = AD5755_WRITE_REG_GAIN(chan->address);
+ else
+ *reg = AD5755_READ_REG_GAIN(chan->address);
+ *shift = st->chip_info->calib_shift;
+ *offset = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad5755_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int reg, shift, offset;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = ad5755_get_scale(st, chan);
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = ad5755_get_offset(st, chan);
+ return IIO_VAL_INT;
+ default:
+ ret = ad5755_chan_reg_info(st, chan, info, false,
+ &reg, &shift, &offset);
+ if (ret)
+ return ret;
+
+ ret = ad5755_read(indio_dev, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = (ret - offset) >> shift;
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5755_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int val, int val2, long info)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int shift, reg, offset;
+ int ret;
+
+ ret = ad5755_chan_reg_info(st, chan, info, true,
+ &reg, &shift, &offset);
+ if (ret)
+ return ret;
+
+ val <<= shift;
+ val += offset;
+
+ if (val < 0 || val > 0xffff)
+ return -EINVAL;
+
+ return ad5755_write(indio_dev, reg, val);
+}
+
+static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ (bool)(st->pwr_down & (1 << chan->channel)));
+}
+
+static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+ struct iio_chan_spec const *chan, const char *buf, size_t len)
+{
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ ret = ad5755_set_channel_pwr_down(indio_dev, chan->channel, pwr_down);
+ return ret ? ret : len;
+}
+
+static const struct iio_info ad5755_info = {
+ .read_raw = ad5755_read_raw,
+ .write_raw = ad5755_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec_ext_info ad5755_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5755_read_powerdown,
+ .write = ad5755_write_powerdown,
+ },
+ { },
+};
+
+#define AD5755_CHANNEL(_bits) { \
+ .indexed = 1, \
+ .output = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \
+ .ext_info = ad5755_ext_info, \
+}
+
+static const struct ad5755_chip_info ad5755_chip_info_tbl[] = {
+ [ID_AD5735] = {
+ .channel_template = AD5755_CHANNEL(14),
+ .has_voltage_out = true,
+ .calib_shift = 4,
+ },
+ [ID_AD5737] = {
+ .channel_template = AD5755_CHANNEL(14),
+ .has_voltage_out = false,
+ .calib_shift = 4,
+ },
+ [ID_AD5755] = {
+ .channel_template = AD5755_CHANNEL(16),
+ .has_voltage_out = true,
+ .calib_shift = 0,
+ },
+ [ID_AD5757] = {
+ .channel_template = AD5755_CHANNEL(16),
+ .has_voltage_out = false,
+ .calib_shift = 0,
+ },
+};
+
+static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode)
+{
+ switch (mode) {
+ case AD5755_MODE_VOLTAGE_0V_5V:
+ case AD5755_MODE_VOLTAGE_0V_10V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
+ return st->chip_info->has_voltage_out;
+ case AD5755_MODE_CURRENT_4mA_20mA:
+ case AD5755_MODE_CURRENT_0mA_20mA:
+ case AD5755_MODE_CURRENT_0mA_24mA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int ad5755_setup_pdata(struct iio_dev *indio_dev,
+ const struct ad5755_platform_data *pdata)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int val;
+ unsigned int i;
+ int ret;
+
+ if (pdata->dc_dc_phase > AD5755_DC_DC_PHASE_90_DEGREE ||
+ pdata->dc_dc_freq > AD5755_DC_DC_FREQ_650kHZ ||
+ pdata->dc_dc_maxv > AD5755_DC_DC_MAXV_29V5)
+ return -EINVAL;
+
+ val = pdata->dc_dc_maxv << AD5755_DC_DC_MAXV;
+ val |= pdata->dc_dc_freq << AD5755_DC_DC_FREQ_SHIFT;
+ val |= pdata->dc_dc_phase << AD5755_DC_DC_PHASE_SHIFT;
+ if (pdata->ext_dc_dc_compenstation_resistor)
+ val |= AD5755_EXT_DC_DC_COMP_RES;
+
+ ret = ad5755_write_ctrl(indio_dev, 0, AD5755_CTRL_REG_DC_DC, val);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
+ val = pdata->dac[i].slew.step_size <<
+ AD5755_SLEW_STEP_SIZE_SHIFT;
+ val |= pdata->dac[i].slew.rate <<
+ AD5755_SLEW_RATE_SHIFT;
+ if (pdata->dac[i].slew.enable)
+ val |= AD5755_SLEW_ENABLE;
+
+ ret = ad5755_write_ctrl(indio_dev, i,
+ AD5755_CTRL_REG_SLEW, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
+ if (!ad5755_is_valid_mode(st, pdata->dac[i].mode))
+ return -EINVAL;
+
+ val = 0;
+ if (!pdata->dac[i].ext_current_sense_resistor)
+ val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR;
+ if (pdata->dac[i].enable_voltage_overrange)
+ val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN;
+ val |= pdata->dac[i].mode;
+
+ ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool ad5755_is_voltage_mode(enum ad5755_mode mode)
+{
+ switch (mode) {
+ case AD5755_MODE_VOLTAGE_0V_5V:
+ case AD5755_MODE_VOLTAGE_0V_10V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int ad5755_init_channels(struct iio_dev *indio_dev,
+ const struct ad5755_platform_data *pdata)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ struct iio_chan_spec *channels = st->channels;
+ unsigned int i;
+
+ for (i = 0; i < AD5755_NUM_CHANNELS; ++i) {
+ channels[i] = st->chip_info->channel_template;
+ channels[i].channel = i;
+ channels[i].address = i;
+ if (pdata && ad5755_is_voltage_mode(pdata->dac[i].mode))
+ channels[i].type = IIO_VOLTAGE;
+ else
+ channels[i].type = IIO_CURRENT;
+ }
+
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+#define AD5755_DEFAULT_DAC_PDATA { \
+ .mode = AD5755_MODE_CURRENT_4mA_20mA, \
+ .ext_current_sense_resistor = true, \
+ .enable_voltage_overrange = false, \
+ .slew = { \
+ .enable = false, \
+ .rate = AD5755_SLEW_RATE_64k, \
+ .step_size = AD5755_SLEW_STEP_SIZE_1, \
+ }, \
+ }
+
+static const struct ad5755_platform_data ad5755_default_pdata = {
+ .ext_dc_dc_compenstation_resistor = false,
+ .dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE,
+ .dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ,
+ .dc_dc_maxv = AD5755_DC_DC_MAXV_23V,
+ .dac = {
+ [0] = AD5755_DEFAULT_DAC_PDATA,
+ [1] = AD5755_DEFAULT_DAC_PDATA,
+ [2] = AD5755_DEFAULT_DAC_PDATA,
+ [3] = AD5755_DEFAULT_DAC_PDATA,
+ },
+};
+
+static int ad5755_probe(struct spi_device *spi)
+{
+ enum ad5755_type type = spi_get_device_id(spi)->driver_data;
+ const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct iio_dev *indio_dev;
+ struct ad5755_state *st;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->chip_info = &ad5755_chip_info_tbl[type];
+ st->spi = spi;
+ st->pwr_down = 0xf;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5755_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = AD5755_NUM_CHANNELS;
+
+ if (!pdata)
+ pdata = &ad5755_default_pdata;
+
+ ret = ad5755_init_channels(indio_dev, pdata);
+ if (ret)
+ goto error_free;
+
+ ret = ad5755_setup_pdata(indio_dev, pdata);
+ if (ret)
+ goto error_free;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+ goto error_free;
+ }
+
+ return 0;
+
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad5755_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ iio_device_unregister(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5755_id[] = {
+ { "ad5755", ID_AD5755 },
+ { "ad5755-1", ID_AD5755 },
+ { "ad5757", ID_AD5757 },
+ { "ad5735", ID_AD5735 },
+ { "ad5737", ID_AD5737 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5755_id);
+
+static struct spi_driver ad5755_driver = {
+ .driver = {
+ .name = "ad5755",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5755_probe,
+ .remove = ad5755_remove,
+ .id_table = ad5755_id,
+};
+module_spi_driver(ad5755_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5755/55-1/57/35/37 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c
new file mode 100644
index 00000000..7a53f7d7
--- /dev/null
+++ b/drivers/iio/dac/ad5764.c
@@ -0,0 +1,377 @@
+/*
+ * Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel
+ * Digital to Analog Converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AD5764_REG_SF_NOP 0x0
+#define AD5764_REG_SF_CONFIG 0x1
+#define AD5764_REG_SF_CLEAR 0x4
+#define AD5764_REG_SF_LOAD 0x5
+#define AD5764_REG_DATA(x) ((2 << 3) | (x))
+#define AD5764_REG_COARSE_GAIN(x) ((3 << 3) | (x))
+#define AD5764_REG_FINE_GAIN(x) ((4 << 3) | (x))
+#define AD5764_REG_OFFSET(x) ((5 << 3) | (x))
+
+#define AD5764_NUM_CHANNELS 4
+
+/**
+ * struct ad5764_chip_info - chip specific information
+ * @int_vref: Value of the internal reference voltage in uV - 0 if external
+ * reference voltage is used
+ * @channel channel specification
+*/
+
+struct ad5764_chip_info {
+ unsigned long int_vref;
+ const struct iio_chan_spec *channels;
+};
+
+/**
+ * struct ad5764_state - driver instance specific data
+ * @spi: spi_device
+ * @chip_info: chip info
+ * @vref_reg: vref supply regulators
+ * @data: spi transfer buffers
+ */
+
+struct ad5764_state {
+ struct spi_device *spi;
+ const struct ad5764_chip_info *chip_info;
+ struct regulator_bulk_data vref_reg[2];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+enum ad5764_type {
+ ID_AD5744,
+ ID_AD5744R,
+ ID_AD5764,
+ ID_AD5764R,
+};
+
+#define AD5764_CHANNEL(_chan, _bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (_chan), \
+ .address = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)) \
+}
+
+#define DECLARE_AD5764_CHANNELS(_name, _bits) \
+const struct iio_chan_spec _name##_channels[] = { \
+ AD5764_CHANNEL(0, (_bits)), \
+ AD5764_CHANNEL(1, (_bits)), \
+ AD5764_CHANNEL(2, (_bits)), \
+ AD5764_CHANNEL(3, (_bits)), \
+};
+
+static DECLARE_AD5764_CHANNELS(ad5764, 16);
+static DECLARE_AD5764_CHANNELS(ad5744, 14);
+
+static const struct ad5764_chip_info ad5764_chip_infos[] = {
+ [ID_AD5744] = {
+ .int_vref = 0,
+ .channels = ad5744_channels,
+ },
+ [ID_AD5744R] = {
+ .int_vref = 5000000,
+ .channels = ad5744_channels,
+ },
+ [ID_AD5764] = {
+ .int_vref = 0,
+ .channels = ad5764_channels,
+ },
+ [ID_AD5764R] = {
+ .int_vref = 5000000,
+ .channels = ad5764_channels,
+ },
+};
+
+static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int val)
+{
+ struct ad5764_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+ ret = spi_write(st->spi, &st->data[0].d8[1], 3);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int *val)
+{
+ struct ad5764_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .rx_buf = &st->data[1].d8[1],
+ .len = 3,
+ },
+ };
+
+ mutex_lock(&indio_dev->mlock);
+
+ st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret >= 0)
+ *val = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info)
+{
+ switch (info) {
+ case 0:
+ return AD5764_REG_DATA(chan->address);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return AD5764_REG_OFFSET(chan->address);
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return AD5764_REG_FINE_GAIN(chan->address);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ad5764_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+ const int max_val = (1 << chan->scan_type.realbits);
+ unsigned int reg;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+ val <<= chan->scan_type.shift;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val >= 128 || val < -128)
+ return -EINVAL;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val >= 32 || val < -32)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg = ad5764_chan_info_to_reg(chan, info);
+ return ad5764_write(indio_dev, reg, (u16)val);
+}
+
+static int ad5764_get_channel_vref(struct ad5764_state *st,
+ unsigned int channel)
+{
+ if (st->chip_info->int_vref)
+ return st->chip_info->int_vref;
+ else
+ return regulator_get_voltage(st->vref_reg[channel / 2].consumer);
+}
+
+static int ad5764_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad5764_state *st = iio_priv(indio_dev);
+ unsigned long scale_uv;
+ unsigned int reg;
+ int vref;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ reg = AD5764_REG_DATA(chan->address);
+ ret = ad5764_read(indio_dev, reg, val);
+ if (ret < 0)
+ return ret;
+ *val >>= chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ reg = AD5764_REG_OFFSET(chan->address);
+ ret = ad5764_read(indio_dev, reg, val);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(*val, 7);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ reg = AD5764_REG_FINE_GAIN(chan->address);
+ ret = ad5764_read(indio_dev, reg, val);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(*val, 5);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* vout = 4 * vref + ((dac_code / 65535) - 0.5) */
+ vref = ad5764_get_channel_vref(st, chan->channel);
+ if (vref < 0)
+ return vref;
+
+ scale_uv = (vref * 4 * 100) >> chan->scan_type.realbits;
+ *val = scale_uv / 100000;
+ *val2 = (scale_uv % 100000) * 10;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -(1 << chan->scan_type.realbits) / 2;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad5764_info = {
+ .read_raw = ad5764_read_raw,
+ .write_raw = ad5764_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad5764_probe(struct spi_device *spi)
+{
+ enum ad5764_type type = spi_get_device_id(spi)->driver_data;
+ struct iio_dev *indio_dev;
+ struct ad5764_state *st;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+ st->chip_info = &ad5764_chip_infos[type];
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5764_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = AD5764_NUM_CHANNELS;
+ indio_dev->channels = st->chip_info->channels;
+
+ if (st->chip_info->int_vref == 0) {
+ st->vref_reg[0].supply = "vrefAB";
+ st->vref_reg[1].supply = "vrefCD";
+
+ ret = regulator_bulk_get(&st->spi->dev,
+ ARRAY_SIZE(st->vref_reg), st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to request vref regulators: %d\n",
+ ret);
+ goto error_free;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg),
+ st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vref regulators: %d\n",
+ ret);
+ goto error_free_reg;
+ }
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+ goto error_disable_reg;
+ }
+
+ return 0;
+
+error_disable_reg:
+ if (st->chip_info->int_vref == 0)
+ regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+error_free_reg:
+ if (st->chip_info->int_vref == 0)
+ regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad5764_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5764_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (st->chip_info->int_vref == 0) {
+ regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+ regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+ }
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5764_ids[] = {
+ { "ad5744", ID_AD5744 },
+ { "ad5744r", ID_AD5744R },
+ { "ad5764", ID_AD5764 },
+ { "ad5764r", ID_AD5764R },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad5764_ids);
+
+static struct spi_driver ad5764_driver = {
+ .driver = {
+ .name = "ad5764",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5764_probe,
+ .remove = ad5764_remove,
+ .id_table = ad5764_ids,
+};
+module_spi_driver(ad5764_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c
new file mode 100644
index 00000000..97c1e5d7
--- /dev/null
+++ b/drivers/iio/dac/ad5791.c
@@ -0,0 +1,490 @@
+/*
+ * AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog
+ * Converter
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/dac/ad5791.h>
+
+#define AD5791_RES_MASK(x) ((1 << (x)) - 1)
+#define AD5791_DAC_MASK AD5791_RES_MASK(20)
+#define AD5791_DAC_MSB (1 << 19)
+
+#define AD5791_CMD_READ (1 << 23)
+#define AD5791_CMD_WRITE (0 << 23)
+#define AD5791_ADDR(addr) ((addr) << 20)
+
+/* Registers */
+#define AD5791_ADDR_NOOP 0
+#define AD5791_ADDR_DAC0 1
+#define AD5791_ADDR_CTRL 2
+#define AD5791_ADDR_CLRCODE 3
+#define AD5791_ADDR_SW_CTRL 4
+
+/* Control Register */
+#define AD5791_CTRL_RBUF (1 << 1)
+#define AD5791_CTRL_OPGND (1 << 2)
+#define AD5791_CTRL_DACTRI (1 << 3)
+#define AD5791_CTRL_BIN2SC (1 << 4)
+#define AD5791_CTRL_SDODIS (1 << 5)
+#define AD5761_CTRL_LINCOMP(x) ((x) << 6)
+
+#define AD5791_LINCOMP_0_10 0
+#define AD5791_LINCOMP_10_12 1
+#define AD5791_LINCOMP_12_16 2
+#define AD5791_LINCOMP_16_19 3
+#define AD5791_LINCOMP_19_20 12
+
+#define AD5780_LINCOMP_0_10 0
+#define AD5780_LINCOMP_10_20 12
+
+/* Software Control Register */
+#define AD5791_SWCTRL_LDAC (1 << 0)
+#define AD5791_SWCTRL_CLR (1 << 1)
+#define AD5791_SWCTRL_RESET (1 << 2)
+
+#define AD5791_DAC_PWRDN_6K 0
+#define AD5791_DAC_PWRDN_3STATE 1
+
+/**
+ * struct ad5791_chip_info - chip specific information
+ * @get_lin_comp: function pointer to the device specific function
+ */
+
+struct ad5791_chip_info {
+ int (*get_lin_comp) (unsigned int span);
+};
+
+/**
+ * struct ad5791_state - driver instance specific data
+ * @us: spi_device
+ * @reg_vdd: positive supply regulator
+ * @reg_vss: negative supply regulator
+ * @chip_info: chip model specific constants
+ * @vref_mv: actual reference voltage used
+ * @vref_neg_mv: voltage of the negative supply
+ * @pwr_down_mode current power down mode
+ */
+
+struct ad5791_state {
+ struct spi_device *spi;
+ struct regulator *reg_vdd;
+ struct regulator *reg_vss;
+ const struct ad5791_chip_info *chip_info;
+ unsigned short vref_mv;
+ unsigned int vref_neg_mv;
+ unsigned ctrl;
+ unsigned pwr_down_mode;
+ bool pwr_down;
+};
+
+/**
+ * ad5791_supported_device_ids:
+ */
+
+enum ad5791_supported_device_ids {
+ ID_AD5760,
+ ID_AD5780,
+ ID_AD5781,
+ ID_AD5791,
+};
+
+static int ad5791_spi_write(struct spi_device *spi, u8 addr, u32 val)
+{
+ union {
+ u32 d32;
+ u8 d8[4];
+ } data;
+
+ data.d32 = cpu_to_be32(AD5791_CMD_WRITE |
+ AD5791_ADDR(addr) |
+ (val & AD5791_DAC_MASK));
+
+ return spi_write(spi, &data.d8[1], 3);
+}
+
+static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val)
+{
+ union {
+ u32 d32;
+ u8 d8[4];
+ } data[3];
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = &data[0].d8[1],
+ .bits_per_word = 8,
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &data[1].d8[1],
+ .rx_buf = &data[2].d8[1],
+ .bits_per_word = 8,
+ .len = 3,
+ },
+ };
+
+ data[0].d32 = cpu_to_be32(AD5791_CMD_READ |
+ AD5791_ADDR(addr));
+ data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP));
+
+ ret = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
+
+ *val = be32_to_cpu(data[2].d32);
+
+ return ret;
+}
+
+static const char * const ad5791_powerdown_modes[] = {
+ "6kohm_to_gnd",
+ "three_state",
+};
+
+static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode;
+}
+
+static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode = mode;
+
+ return 0;
+}
+
+static const struct iio_enum ad5791_powerdown_mode_enum = {
+ .items = ad5791_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5791_powerdown_modes),
+ .get = ad5791_get_powerdown_mode,
+ .set = ad5791_set_powerdown_mode,
+};
+
+static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->pwr_down);
+}
+
+static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ bool pwr_down;
+ int ret;
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ if (!pwr_down) {
+ st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI);
+ } else {
+ if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K)
+ st->ctrl |= AD5791_CTRL_OPGND;
+ else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE)
+ st->ctrl |= AD5791_CTRL_DACTRI;
+ }
+ st->pwr_down = pwr_down;
+
+ ret = ad5791_spi_write(st->spi, AD5791_ADDR_CTRL, st->ctrl);
+
+ return ret ? ret : len;
+}
+
+static int ad5791_get_lin_comp(unsigned int span)
+{
+ if (span <= 10000)
+ return AD5791_LINCOMP_0_10;
+ else if (span <= 12000)
+ return AD5791_LINCOMP_10_12;
+ else if (span <= 16000)
+ return AD5791_LINCOMP_12_16;
+ else if (span <= 19000)
+ return AD5791_LINCOMP_16_19;
+ else
+ return AD5791_LINCOMP_19_20;
+}
+
+static int ad5780_get_lin_comp(unsigned int span)
+{
+ if (span <= 10000)
+ return AD5780_LINCOMP_0_10;
+ else
+ return AD5780_LINCOMP_10_20;
+}
+static const struct ad5791_chip_info ad5791_chip_info_tbl[] = {
+ [ID_AD5760] = {
+ .get_lin_comp = ad5780_get_lin_comp,
+ },
+ [ID_AD5780] = {
+ .get_lin_comp = ad5780_get_lin_comp,
+ },
+ [ID_AD5781] = {
+ .get_lin_comp = ad5791_get_lin_comp,
+ },
+ [ID_AD5791] = {
+ .get_lin_comp = ad5791_get_lin_comp,
+ },
+};
+
+static int ad5791_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+ u64 val64;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5791_spi_read(st->spi, chan->address, val);
+ if (ret)
+ return ret;
+ *val &= AD5791_DAC_MASK;
+ *val >>= chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = (((u64)st->vref_mv) * 1000000ULL) >> chan->scan_type.realbits;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits);
+ do_div(val64, st->vref_mv);
+ *val = -val64;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+};
+
+static const struct iio_chan_spec_ext_info ad5791_ext_info[] = {
+ {
+ .name = "powerdown",
+ .shared = true,
+ .read = ad5791_read_dac_powerdown,
+ .write = ad5791_write_dac_powerdown,
+ },
+ IIO_ENUM("powerdown_mode", true, &ad5791_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum),
+ { },
+};
+
+#define AD5791_CHAN(bits, shift) { \
+ .type = IIO_VOLTAGE, \
+ .output = 1, \
+ .indexed = 1, \
+ .address = AD5791_ADDR_DAC0, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_type = IIO_ST('u', bits, 24, shift), \
+ .ext_info = ad5791_ext_info, \
+}
+
+static const struct iio_chan_spec ad5791_channels[] = {
+ [ID_AD5760] = AD5791_CHAN(16, 4),
+ [ID_AD5780] = AD5791_CHAN(18, 2),
+ [ID_AD5781] = AD5791_CHAN(18, 2),
+ [ID_AD5791] = AD5791_CHAN(20, 0)
+};
+
+static int ad5791_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ val &= AD5791_RES_MASK(chan->scan_type.realbits);
+ val <<= chan->scan_type.shift;
+
+ return ad5791_spi_write(st->spi, chan->address, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad5791_info = {
+ .read_raw = &ad5791_read_raw,
+ .write_raw = &ad5791_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad5791_probe(struct spi_device *spi)
+{
+ struct ad5791_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad5791_state *st;
+ int ret, pos_voltage_uv = 0, neg_voltage_uv = 0;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ st = iio_priv(indio_dev);
+ st->reg_vdd = regulator_get(&spi->dev, "vdd");
+ if (!IS_ERR(st->reg_vdd)) {
+ ret = regulator_enable(st->reg_vdd);
+ if (ret)
+ goto error_put_reg_pos;
+
+ ret = regulator_get_voltage(st->reg_vdd);
+ if (ret < 0)
+ goto error_disable_reg_pos;
+
+ pos_voltage_uv = ret;
+ }
+
+ st->reg_vss = regulator_get(&spi->dev, "vss");
+ if (!IS_ERR(st->reg_vss)) {
+ ret = regulator_enable(st->reg_vss);
+ if (ret)
+ goto error_put_reg_neg;
+
+ ret = regulator_get_voltage(st->reg_vss);
+ if (ret < 0)
+ goto error_disable_reg_neg;
+
+ neg_voltage_uv = ret;
+ }
+
+ st->pwr_down = true;
+ st->spi = spi;
+
+ if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) {
+ st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000;
+ st->vref_neg_mv = neg_voltage_uv / 1000;
+ } else if (pdata) {
+ st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv;
+ st->vref_neg_mv = pdata->vref_neg_mv;
+ } else {
+ dev_warn(&spi->dev, "reference voltage unspecified\n");
+ }
+
+ ret = ad5791_spi_write(spi, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET);
+ if (ret)
+ goto error_disable_reg_neg;
+
+ st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi)
+ ->driver_data];
+
+
+ st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv))
+ | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) |
+ AD5791_CTRL_BIN2SC;
+
+ ret = ad5791_spi_write(spi, AD5791_ADDR_CTRL, st->ctrl |
+ AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI);
+ if (ret)
+ goto error_disable_reg_neg;
+
+ spi_set_drvdata(spi, indio_dev);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ad5791_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels
+ = &ad5791_channels[spi_get_device_id(spi)->driver_data];
+ indio_dev->num_channels = 1;
+ indio_dev->name = spi_get_device_id(st->spi)->name;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg_neg;
+
+ return 0;
+
+error_disable_reg_neg:
+ if (!IS_ERR(st->reg_vss))
+ regulator_disable(st->reg_vss);
+error_put_reg_neg:
+ if (!IS_ERR(st->reg_vss))
+ regulator_put(st->reg_vss);
+
+error_disable_reg_pos:
+ if (!IS_ERR(st->reg_vdd))
+ regulator_disable(st->reg_vdd);
+error_put_reg_pos:
+ if (!IS_ERR(st->reg_vdd))
+ regulator_put(st->reg_vdd);
+ iio_device_free(indio_dev);
+error_ret:
+
+ return ret;
+}
+
+static int ad5791_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg_vdd)) {
+ regulator_disable(st->reg_vdd);
+ regulator_put(st->reg_vdd);
+ }
+
+ if (!IS_ERR(st->reg_vss)) {
+ regulator_disable(st->reg_vss);
+ regulator_put(st->reg_vss);
+ }
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5791_id[] = {
+ {"ad5760", ID_AD5760},
+ {"ad5780", ID_AD5780},
+ {"ad5781", ID_AD5781},
+ {"ad5790", ID_AD5791},
+ {"ad5791", ID_AD5791},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5791_id);
+
+static struct spi_driver ad5791_driver = {
+ .driver = {
+ .name = "ad5791",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5791_probe,
+ .remove = ad5791_remove,
+ .id_table = ad5791_id,
+};
+module_spi_driver(ad5791_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c
new file mode 100644
index 00000000..ebfaa415
--- /dev/null
+++ b/drivers/iio/dac/max517.c
@@ -0,0 +1,242 @@
+/*
+ * max517.c - Support for Maxim MAX517, MAX518 and MAX519
+ *
+ * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/dac/max517.h>
+
+#define MAX517_DRV_NAME "max517"
+
+/* Commands */
+#define COMMAND_CHANNEL0 0x00
+#define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */
+#define COMMAND_PD 0x08 /* Power Down */
+
+enum max517_device_ids {
+ ID_MAX517,
+ ID_MAX518,
+ ID_MAX519,
+};
+
+struct max517_data {
+ struct i2c_client *client;
+ unsigned short vref_mv[2];
+};
+
+/*
+ * channel: bit 0: channel 1
+ * bit 1: channel 2
+ * (this way, it's possible to set both channels at once)
+ */
+static int max517_set_value(struct iio_dev *indio_dev,
+ long val, int channel)
+{
+ struct max517_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ u8 outbuf[2];
+ int res;
+
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ outbuf[0] = channel;
+ outbuf[1] = val;
+
+ res = i2c_master_send(client, outbuf, 2);
+ if (res < 0)
+ return res;
+ else if (res != 2)
+ return -EIO;
+ else
+ return 0;
+}
+
+static int max517_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct max517_data *data = iio_priv(indio_dev);
+ unsigned int scale_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ /* Corresponds to Vref / 2^(bits) */
+ scale_uv = (data->vref_mv[chan->channel] * 1000) >> 8;
+ *val = scale_uv / 1000000;
+ *val2 = scale_uv % 1000000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int max517_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = max517_set_value(indio_dev, val, chan->channel);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max517_suspend(struct device *dev)
+{
+ u8 outbuf = COMMAND_PD;
+
+ return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
+}
+
+static int max517_resume(struct device *dev)
+{
+ u8 outbuf = 0;
+
+ return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
+}
+
+static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume);
+#define MAX517_PM_OPS (&max517_pm_ops)
+#else
+#define MAX517_PM_OPS NULL
+#endif
+
+static const struct iio_info max517_info = {
+ .read_raw = max517_read_raw,
+ .write_raw = max517_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define MAX517_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = IIO_ST('u', 8, 8, 0), \
+}
+
+static const struct iio_chan_spec max517_channels[] = {
+ MAX517_CHANNEL(0),
+ MAX517_CHANNEL(1)
+};
+
+static int max517_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max517_data *data;
+ struct iio_dev *indio_dev;
+ struct max517_platform_data *platform_data = client->dev.platform_data;
+ int err;
+
+ indio_dev = iio_device_alloc(sizeof(*data));
+ if (indio_dev == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ /* establish that the iio_dev is a child of the i2c device */
+ indio_dev->dev.parent = &client->dev;
+
+ /* reduced channel set for MAX517 */
+ if (id->driver_data == ID_MAX517)
+ indio_dev->num_channels = 1;
+ else
+ indio_dev->num_channels = 2;
+ indio_dev->channels = max517_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &max517_info;
+
+ /*
+ * Reference voltage on MAX518 and default is 5V, else take vref_mv
+ * from platform_data
+ */
+ if (id->driver_data == ID_MAX518 || !platform_data) {
+ data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */
+ } else {
+ data->vref_mv[0] = platform_data->vref_mv[0];
+ data->vref_mv[1] = platform_data->vref_mv[1];
+ }
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto exit_free_device;
+
+ dev_info(&client->dev, "DAC registered\n");
+
+ return 0;
+
+exit_free_device:
+ iio_device_free(indio_dev);
+exit:
+ return err;
+}
+
+static int max517_remove(struct i2c_client *client)
+{
+ iio_device_unregister(i2c_get_clientdata(client));
+ iio_device_free(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static const struct i2c_device_id max517_id[] = {
+ { "max517", ID_MAX517 },
+ { "max518", ID_MAX518 },
+ { "max519", ID_MAX519 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max517_id);
+
+static struct i2c_driver max517_driver = {
+ .driver = {
+ .name = MAX517_DRV_NAME,
+ .pm = MAX517_PM_OPS,
+ },
+ .probe = max517_probe,
+ .remove = max517_remove,
+ .id_table = max517_id,
+};
+module_i2c_driver(max517_driver);
+
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
new file mode 100644
index 00000000..a612ec76
--- /dev/null
+++ b/drivers/iio/dac/mcp4725.c
@@ -0,0 +1,227 @@
+/*
+ * mcp4725.c - Support for Microchip MCP4725
+ *
+ * Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * Based on max517 by Roland Stigge <stigge@antcom.de>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * driver for the Microchip I2C 12-bit digital-to-analog converter (DAC)
+ * (7-bit I2C slave address 0x60, the three LSBs can be configured in
+ * hardware)
+ *
+ * writing the DAC value to EEPROM is not supported
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/iio/dac/mcp4725.h>
+
+#define MCP4725_DRV_NAME "mcp4725"
+
+struct mcp4725_data {
+ struct i2c_client *client;
+ u16 vref_mv;
+ u16 dac_value;
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int mcp4725_suspend(struct device *dev)
+{
+ u8 outbuf[2];
+
+ outbuf[0] = 0x3 << 4; /* power-down bits, 500 kOhm resistor */
+ outbuf[1] = 0;
+
+ return i2c_master_send(to_i2c_client(dev), outbuf, 2);
+}
+
+static int mcp4725_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ u8 outbuf[2];
+
+ /* restore previous DAC value */
+ outbuf[0] = (data->dac_value >> 8) & 0xf;
+ outbuf[1] = data->dac_value & 0xff;
+
+ return i2c_master_send(to_i2c_client(dev), outbuf, 2);
+}
+
+static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume);
+#define MCP4725_PM_OPS (&mcp4725_pm_ops)
+#else
+#define MCP4725_PM_OPS NULL
+#endif
+
+static const struct iio_chan_spec mcp4725_channel = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .scan_type = IIO_ST('u', 12, 16, 0),
+};
+
+static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ u8 outbuf[2];
+ int ret;
+
+ if (val >= (1 << 12) || val < 0)
+ return -EINVAL;
+
+ outbuf[0] = (val >> 8) & 0xf;
+ outbuf[1] = val & 0xff;
+
+ ret = i2c_master_send(data->client, outbuf, 2);
+ if (ret < 0)
+ return ret;
+ else if (ret != 2)
+ return -EIO;
+ else
+ return 0;
+}
+
+static int mcp4725_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ unsigned long scale_uv;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = data->dac_value;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = (data->vref_mv * 1000) >> 12;
+ *val = scale_uv / 1000000;
+ *val2 = scale_uv % 1000000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+}
+
+static int mcp4725_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = mcp4725_set_value(indio_dev, val);
+ data->dac_value = val;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct iio_info mcp4725_info = {
+ .read_raw = mcp4725_read_raw,
+ .write_raw = mcp4725_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int mcp4725_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mcp4725_data *data;
+ struct iio_dev *indio_dev;
+ struct mcp4725_platform_data *platform_data = client->dev.platform_data;
+ u8 inbuf[3];
+ int err;
+
+ if (!platform_data || !platform_data->vref_mv) {
+ dev_err(&client->dev, "invalid platform data");
+ err = -EINVAL;
+ goto exit;
+ }
+
+ indio_dev = iio_device_alloc(sizeof(*data));
+ if (indio_dev == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &mcp4725_info;
+ indio_dev->channels = &mcp4725_channel;
+ indio_dev->num_channels = 1;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data->vref_mv = platform_data->vref_mv;
+
+ /* read current DAC value */
+ err = i2c_master_recv(client, inbuf, 3);
+ if (err < 0) {
+ dev_err(&client->dev, "failed to read DAC value");
+ goto exit_free_device;
+ }
+ data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto exit_free_device;
+
+ dev_info(&client->dev, "MCP4725 DAC registered\n");
+
+ return 0;
+
+exit_free_device:
+ iio_device_free(indio_dev);
+exit:
+ return err;
+}
+
+static int mcp4725_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id mcp4725_id[] = {
+ { "mcp4725", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp4725_id);
+
+static struct i2c_driver mcp4725_driver = {
+ .driver = {
+ .name = MCP4725_DRV_NAME,
+ .pm = MCP4725_PM_OPS,
+ },
+ .probe = mcp4725_probe,
+ .remove = mcp4725_remove,
+ .id_table = mcp4725_id,
+};
+module_i2c_driver(mcp4725_driver);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("MCP4725 12-bit DAC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
new file mode 100644
index 00000000..6aaa33ef
--- /dev/null
+++ b/drivers/iio/frequency/Kconfig
@@ -0,0 +1,41 @@
+#
+# Frequency
+# Direct Digital Synthesis drivers (DDS)
+# Clock Distribution device drivers
+# Phase-Locked Loop (PLL) frequency synthesizers
+#
+
+menu "Frequency Synthesizers DDS/PLL"
+
+menu "Clock Generator/Distribution"
+
+config AD9523
+ tristate "Analog Devices AD9523 Low Jitter Clock Generator"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD9523 Low Jitter
+ Clock Generator. The driver provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad9523.
+
+endmenu
+
+#
+# Phase-Locked Loop (PLL) frequency synthesizers
+#
+
+menu "Phase-Locked Loop (PLL) frequency synthesizers"
+
+config ADF4350
+ tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices ADF4350/ADF4351
+ Wideband Synthesizers. The driver provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adf4350.
+
+endmenu
+endmenu
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
new file mode 100644
index 00000000..00d26e5d
--- /dev/null
+++ b/drivers/iio/frequency/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile iio/frequency
+#
+
+obj-$(CONFIG_AD9523) += ad9523.o
+obj-$(CONFIG_ADF4350) += adf4350.o
diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c
new file mode 100644
index 00000000..92276dee
--- /dev/null
+++ b/drivers/iio/frequency/ad9523.c
@@ -0,0 +1,1049 @@
+/*
+ * AD9523 SPI Low Jitter Clock Generator
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/frequency/ad9523.h>
+
+#define AD9523_READ (1 << 15)
+#define AD9523_WRITE (0 << 15)
+#define AD9523_CNT(x) (((x) - 1) << 13)
+#define AD9523_ADDR(x) ((x) & 0xFFF)
+
+#define AD9523_R1B (1 << 16)
+#define AD9523_R2B (2 << 16)
+#define AD9523_R3B (3 << 16)
+#define AD9523_TRANSF_LEN(x) ((x) >> 16)
+
+#define AD9523_SERIAL_PORT_CONFIG (AD9523_R1B | 0x0)
+#define AD9523_VERSION_REGISTER (AD9523_R1B | 0x2)
+#define AD9523_PART_REGISTER (AD9523_R1B | 0x3)
+#define AD9523_READBACK_CTRL (AD9523_R1B | 0x4)
+
+#define AD9523_EEPROM_CUSTOMER_VERSION_ID (AD9523_R2B | 0x6)
+
+#define AD9523_PLL1_REF_A_DIVIDER (AD9523_R2B | 0x11)
+#define AD9523_PLL1_REF_B_DIVIDER (AD9523_R2B | 0x13)
+#define AD9523_PLL1_REF_TEST_DIVIDER (AD9523_R1B | 0x14)
+#define AD9523_PLL1_FEEDBACK_DIVIDER (AD9523_R2B | 0x17)
+#define AD9523_PLL1_CHARGE_PUMP_CTRL (AD9523_R2B | 0x19)
+#define AD9523_PLL1_INPUT_RECEIVERS_CTRL (AD9523_R1B | 0x1A)
+#define AD9523_PLL1_REF_CTRL (AD9523_R1B | 0x1B)
+#define AD9523_PLL1_MISC_CTRL (AD9523_R1B | 0x1C)
+#define AD9523_PLL1_LOOP_FILTER_CTRL (AD9523_R1B | 0x1D)
+
+#define AD9523_PLL2_CHARGE_PUMP (AD9523_R1B | 0xF0)
+#define AD9523_PLL2_FEEDBACK_DIVIDER_AB (AD9523_R1B | 0xF1)
+#define AD9523_PLL2_CTRL (AD9523_R1B | 0xF2)
+#define AD9523_PLL2_VCO_CTRL (AD9523_R1B | 0xF3)
+#define AD9523_PLL2_VCO_DIVIDER (AD9523_R1B | 0xF4)
+#define AD9523_PLL2_LOOP_FILTER_CTRL (AD9523_R2B | 0xF6)
+#define AD9523_PLL2_R2_DIVIDER (AD9523_R1B | 0xF7)
+
+#define AD9523_CHANNEL_CLOCK_DIST(ch) (AD9523_R3B | (0x192 + 3 * ch))
+
+#define AD9523_PLL1_OUTPUT_CTRL (AD9523_R1B | 0x1BA)
+#define AD9523_PLL1_OUTPUT_CHANNEL_CTRL (AD9523_R1B | 0x1BB)
+
+#define AD9523_READBACK_0 (AD9523_R1B | 0x22C)
+#define AD9523_READBACK_1 (AD9523_R1B | 0x22D)
+
+#define AD9523_STATUS_SIGNALS (AD9523_R3B | 0x232)
+#define AD9523_POWER_DOWN_CTRL (AD9523_R1B | 0x233)
+#define AD9523_IO_UPDATE (AD9523_R1B | 0x234)
+
+#define AD9523_EEPROM_DATA_XFER_STATUS (AD9523_R1B | 0xB00)
+#define AD9523_EEPROM_ERROR_READBACK (AD9523_R1B | 0xB01)
+#define AD9523_EEPROM_CTRL1 (AD9523_R1B | 0xB02)
+#define AD9523_EEPROM_CTRL2 (AD9523_R1B | 0xB03)
+
+/* AD9523_SERIAL_PORT_CONFIG */
+
+#define AD9523_SER_CONF_SDO_ACTIVE (1 << 7)
+#define AD9523_SER_CONF_SOFT_RESET (1 << 5)
+
+/* AD9523_READBACK_CTRL */
+#define AD9523_READBACK_CTRL_READ_BUFFERED (1 << 0)
+
+/* AD9523_PLL1_CHARGE_PUMP_CTRL */
+#define AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(x) (((x) / 500) & 0x7F)
+#define AD9523_PLL1_CHARGE_PUMP_TRISTATE (1 << 7)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL (3 << 8)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_DOWN (2 << 8)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_UP (1 << 8)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_TRISTATE (0 << 8)
+#define AD9523_PLL1_BACKLASH_PW_MIN (0 << 10)
+#define AD9523_PLL1_BACKLASH_PW_LOW (1 << 10)
+#define AD9523_PLL1_BACKLASH_PW_HIGH (2 << 10)
+#define AD9523_PLL1_BACKLASH_PW_MAX (3 << 10)
+
+/* AD9523_PLL1_INPUT_RECEIVERS_CTRL */
+#define AD9523_PLL1_REF_TEST_RCV_EN (1 << 7)
+#define AD9523_PLL1_REFB_DIFF_RCV_EN (1 << 6)
+#define AD9523_PLL1_REFA_DIFF_RCV_EN (1 << 5)
+#define AD9523_PLL1_REFB_RCV_EN (1 << 4)
+#define AD9523_PLL1_REFA_RCV_EN (1 << 3)
+#define AD9523_PLL1_REFA_REFB_PWR_CTRL_EN (1 << 2)
+#define AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN (1 << 1)
+#define AD9523_PLL1_OSC_IN_DIFF_EN (1 << 0)
+
+/* AD9523_PLL1_REF_CTRL */
+#define AD9523_PLL1_BYPASS_REF_TEST_DIV_EN (1 << 7)
+#define AD9523_PLL1_BYPASS_FEEDBACK_DIV_EN (1 << 6)
+#define AD9523_PLL1_ZERO_DELAY_MODE_INT (1 << 5)
+#define AD9523_PLL1_ZERO_DELAY_MODE_EXT (0 << 5)
+#define AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN (1 << 4)
+#define AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN (1 << 3)
+#define AD9523_PLL1_ZD_IN_DIFF_EN (1 << 2)
+#define AD9523_PLL1_REFB_CMOS_NEG_INP_EN (1 << 1)
+#define AD9523_PLL1_REFA_CMOS_NEG_INP_EN (1 << 0)
+
+/* AD9523_PLL1_MISC_CTRL */
+#define AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN (1 << 7)
+#define AD9523_PLL1_OSC_CTRL_FAIL_VCC_BY2_EN (1 << 6)
+#define AD9523_PLL1_REF_MODE(x) ((x) << 2)
+#define AD9523_PLL1_BYPASS_REFB_DIV (1 << 1)
+#define AD9523_PLL1_BYPASS_REFA_DIV (1 << 0)
+
+/* AD9523_PLL1_LOOP_FILTER_CTRL */
+#define AD9523_PLL1_LOOP_FILTER_RZERO(x) ((x) & 0xF)
+
+/* AD9523_PLL2_CHARGE_PUMP */
+#define AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(x) ((x) / 3500)
+
+/* AD9523_PLL2_FEEDBACK_DIVIDER_AB */
+#define AD9523_PLL2_FB_NDIV_A_CNT(x) (((x) & 0x3) << 6)
+#define AD9523_PLL2_FB_NDIV_B_CNT(x) (((x) & 0x3F) << 0)
+#define AD9523_PLL2_FB_NDIV(a, b) (4 * (b) + (a))
+
+/* AD9523_PLL2_CTRL */
+#define AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL (3 << 0)
+#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_DOWN (2 << 0)
+#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_UP (1 << 0)
+#define AD9523_PLL2_CHARGE_PUMP_MODE_TRISTATE (0 << 0)
+#define AD9523_PLL2_BACKLASH_PW_MIN (0 << 2)
+#define AD9523_PLL2_BACKLASH_PW_LOW (1 << 2)
+#define AD9523_PLL2_BACKLASH_PW_HIGH (2 << 2)
+#define AD9523_PLL2_BACKLASH_PW_MAX (3 << 1)
+#define AD9523_PLL2_BACKLASH_CTRL_EN (1 << 4)
+#define AD9523_PLL2_FREQ_DOUBLER_EN (1 << 5)
+#define AD9523_PLL2_LOCK_DETECT_PWR_DOWN_EN (1 << 7)
+
+/* AD9523_PLL2_VCO_CTRL */
+#define AD9523_PLL2_VCO_CALIBRATE (1 << 1)
+#define AD9523_PLL2_FORCE_VCO_MIDSCALE (1 << 2)
+#define AD9523_PLL2_FORCE_REFERENCE_VALID (1 << 3)
+#define AD9523_PLL2_FORCE_RELEASE_SYNC (1 << 4)
+
+/* AD9523_PLL2_VCO_DIVIDER */
+#define AD9523_PLL2_VCO_DIV_M1(x) ((((x) - 3) & 0x3) << 0)
+#define AD9523_PLL2_VCO_DIV_M2(x) ((((x) - 3) & 0x3) << 4)
+#define AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN (1 << 2)
+#define AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN (1 << 6)
+
+/* AD9523_PLL2_LOOP_FILTER_CTRL */
+#define AD9523_PLL2_LOOP_FILTER_CPOLE1(x) (((x) & 0x7) << 0)
+#define AD9523_PLL2_LOOP_FILTER_RZERO(x) (((x) & 0x7) << 3)
+#define AD9523_PLL2_LOOP_FILTER_RPOLE2(x) (((x) & 0x7) << 6)
+#define AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN (1 << 8)
+
+/* AD9523_PLL2_R2_DIVIDER */
+#define AD9523_PLL2_R2_DIVIDER_VAL(x) (((x) & 0x1F) << 0)
+
+/* AD9523_CHANNEL_CLOCK_DIST */
+#define AD9523_CLK_DIST_DIV_PHASE(x) (((x) & 0x3F) << 18)
+#define AD9523_CLK_DIST_DIV_PHASE_REV(x) ((ret >> 18) & 0x3F)
+#define AD9523_CLK_DIST_DIV(x) ((((x) - 1) & 0x3FF) << 8)
+#define AD9523_CLK_DIST_DIV_REV(x) (((ret >> 8) & 0x3FF) + 1)
+#define AD9523_CLK_DIST_INV_DIV_OUTPUT_EN (1 << 7)
+#define AD9523_CLK_DIST_IGNORE_SYNC_EN (1 << 6)
+#define AD9523_CLK_DIST_PWR_DOWN_EN (1 << 5)
+#define AD9523_CLK_DIST_LOW_PWR_MODE_EN (1 << 4)
+#define AD9523_CLK_DIST_DRIVER_MODE(x) (((x) & 0xF) << 0)
+
+/* AD9523_PLL1_OUTPUT_CTRL */
+#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH6_M2 (1 << 7)
+#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH5_M2 (1 << 6)
+#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2 (1 << 5)
+#define AD9523_PLL1_OUTP_CTRL_CMOS_DRV_WEAK (1 << 4)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_1 (0 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_2 (1 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_4 (2 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_8 (4 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_16 (8 << 0)
+
+/* AD9523_PLL1_OUTPUT_CHANNEL_CTRL */
+#define AD9523_PLL1_OUTP_CH_CTRL_OUTPUT_PWR_DOWN_EN (1 << 7)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH9_M2 (1 << 6)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH8_M2 (1 << 5)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2 (1 << 4)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH3 (1 << 3)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH2 (1 << 2)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH1 (1 << 1)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0 (1 << 0)
+
+/* AD9523_READBACK_0 */
+#define AD9523_READBACK_0_STAT_PLL2_REF_CLK (1 << 7)
+#define AD9523_READBACK_0_STAT_PLL2_FB_CLK (1 << 6)
+#define AD9523_READBACK_0_STAT_VCXO (1 << 5)
+#define AD9523_READBACK_0_STAT_REF_TEST (1 << 4)
+#define AD9523_READBACK_0_STAT_REFB (1 << 3)
+#define AD9523_READBACK_0_STAT_REFA (1 << 2)
+#define AD9523_READBACK_0_STAT_PLL2_LD (1 << 1)
+#define AD9523_READBACK_0_STAT_PLL1_LD (1 << 0)
+
+/* AD9523_READBACK_1 */
+#define AD9523_READBACK_1_HOLDOVER_ACTIVE (1 << 3)
+#define AD9523_READBACK_1_AUTOMODE_SEL_REFB (1 << 2)
+#define AD9523_READBACK_1_VCO_CALIB_IN_PROGRESS (1 << 0)
+
+/* AD9523_STATUS_SIGNALS */
+#define AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL (1 << 16)
+#define AD9523_STATUS_MONITOR_01_PLL12_LOCKED (0x302)
+/* AD9523_POWER_DOWN_CTRL */
+#define AD9523_POWER_DOWN_CTRL_PLL1_PWR_DOWN (1 << 2)
+#define AD9523_POWER_DOWN_CTRL_PLL2_PWR_DOWN (1 << 1)
+#define AD9523_POWER_DOWN_CTRL_DIST_PWR_DOWN (1 << 0)
+
+/* AD9523_IO_UPDATE */
+#define AD9523_IO_UPDATE_EN (1 << 0)
+
+/* AD9523_EEPROM_DATA_XFER_STATUS */
+#define AD9523_EEPROM_DATA_XFER_IN_PROGRESS (1 << 0)
+
+/* AD9523_EEPROM_ERROR_READBACK */
+#define AD9523_EEPROM_ERROR_READBACK_FAIL (1 << 0)
+
+/* AD9523_EEPROM_CTRL1 */
+#define AD9523_EEPROM_CTRL1_SOFT_EEPROM (1 << 1)
+#define AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS (1 << 0)
+
+/* AD9523_EEPROM_CTRL2 */
+#define AD9523_EEPROM_CTRL2_REG2EEPROM (1 << 0)
+
+#define AD9523_NUM_CHAN 14
+#define AD9523_NUM_CHAN_ALT_CLK_SRC 10
+
+/* Helpers to avoid excess line breaks */
+#define AD_IFE(_pde, _a, _b) ((pdata->_pde) ? _a : _b)
+#define AD_IF(_pde, _a) AD_IFE(_pde, _a, 0)
+
+enum {
+ AD9523_STAT_PLL1_LD,
+ AD9523_STAT_PLL2_LD,
+ AD9523_STAT_REFA,
+ AD9523_STAT_REFB,
+ AD9523_STAT_REF_TEST,
+ AD9523_STAT_VCXO,
+ AD9523_STAT_PLL2_FB_CLK,
+ AD9523_STAT_PLL2_REF_CLK,
+ AD9523_SYNC,
+ AD9523_EEPROM,
+};
+
+enum {
+ AD9523_VCO1,
+ AD9523_VCO2,
+ AD9523_VCXO,
+ AD9523_NUM_CLK_SRC,
+};
+
+struct ad9523_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ struct ad9523_platform_data *pdata;
+ struct iio_chan_spec ad9523_channels[AD9523_NUM_CHAN];
+
+ unsigned long vcxo_freq;
+ unsigned long vco_freq;
+ unsigned long vco_out_freq[AD9523_NUM_CLK_SRC];
+ unsigned char vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
+{
+ struct ad9523_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /* We encode the register size 1..3 bytes into the register address.
+ * On transfer we get the size from the register datum, and make sure
+ * the result is properly aligned.
+ */
+
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[2],
+ .len = 2,
+ }, {
+ .rx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
+ .len = AD9523_TRANSF_LEN(addr),
+ },
+ };
+
+ st->data[0].d32 = cpu_to_be32(AD9523_READ |
+ AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
+ AD9523_ADDR(addr));
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "read failed (%d)", ret);
+ else
+ ret = be32_to_cpu(st->data[1].d32) & (0xFFFFFF >>
+ (8 * (3 - AD9523_TRANSF_LEN(addr))));
+
+ return ret;
+};
+
+static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val)
+{
+ struct ad9523_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[2],
+ .len = 2,
+ }, {
+ .tx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
+ .len = AD9523_TRANSF_LEN(addr),
+ },
+ };
+
+ st->data[0].d32 = cpu_to_be32(AD9523_WRITE |
+ AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
+ AD9523_ADDR(addr));
+ st->data[1].d32 = cpu_to_be32(val);
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "write failed (%d)", ret);
+
+ return ret;
+}
+
+static int ad9523_io_update(struct iio_dev *indio_dev)
+{
+ return ad9523_write(indio_dev, AD9523_IO_UPDATE, AD9523_IO_UPDATE_EN);
+}
+
+static int ad9523_vco_out_map(struct iio_dev *indio_dev,
+ unsigned ch, unsigned out)
+{
+ struct ad9523_state *st = iio_priv(indio_dev);
+ int ret;
+ unsigned mask;
+
+ switch (ch) {
+ case 0 ... 3:
+ ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
+ if (ret < 0)
+ break;
+ mask = AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0 << ch;
+ if (out) {
+ ret |= mask;
+ out = 2;
+ } else {
+ ret &= ~mask;
+ }
+ ret = ad9523_write(indio_dev,
+ AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
+ break;
+ case 4 ... 6:
+ ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CTRL);
+ if (ret < 0)
+ break;
+ mask = AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2 << (ch - 4);
+ if (out)
+ ret |= mask;
+ else
+ ret &= ~mask;
+ ret = ad9523_write(indio_dev, AD9523_PLL1_OUTPUT_CTRL, ret);
+ break;
+ case 7 ... 9:
+ ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
+ if (ret < 0)
+ break;
+ mask = AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2 << (ch - 7);
+ if (out)
+ ret |= mask;
+ else
+ ret &= ~mask;
+ ret = ad9523_write(indio_dev,
+ AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
+ break;
+ default:
+ return 0;
+ }
+
+ st->vco_out_map[ch] = out;
+
+ return ret;
+}
+
+static int ad9523_set_clock_provider(struct iio_dev *indio_dev,
+ unsigned ch, unsigned long freq)
+{
+ struct ad9523_state *st = iio_priv(indio_dev);
+ long tmp1, tmp2;
+ bool use_alt_clk_src;
+
+ switch (ch) {
+ case 0 ... 3:
+ use_alt_clk_src = (freq == st->vco_out_freq[AD9523_VCXO]);
+ break;
+ case 4 ... 9:
+ tmp1 = st->vco_out_freq[AD9523_VCO1] / freq;
+ tmp2 = st->vco_out_freq[AD9523_VCO2] / freq;
+ tmp1 *= freq;
+ tmp2 *= freq;
+ use_alt_clk_src = (abs(tmp1 - freq) > abs(tmp2 - freq));
+ break;
+ default:
+ /* Ch 10..14: No action required, return success */
+ return 0;
+ }
+
+ return ad9523_vco_out_map(indio_dev, ch, use_alt_clk_src);
+}
+
+static int ad9523_store_eeprom(struct iio_dev *indio_dev)
+{
+ int ret, tmp;
+
+ ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1,
+ AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS);
+ if (ret < 0)
+ return ret;
+ ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL2,
+ AD9523_EEPROM_CTRL2_REG2EEPROM);
+ if (ret < 0)
+ return ret;
+
+ tmp = 4;
+ do {
+ msleep(16);
+ ret = ad9523_read(indio_dev,
+ AD9523_EEPROM_DATA_XFER_STATUS);
+ if (ret < 0)
+ return ret;
+ } while ((ret & AD9523_EEPROM_DATA_XFER_IN_PROGRESS) && tmp--);
+
+ ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_read(indio_dev, AD9523_EEPROM_ERROR_READBACK);
+ if (ret < 0)
+ return ret;
+
+ if (ret & AD9523_EEPROM_ERROR_READBACK_FAIL) {
+ dev_err(&indio_dev->dev, "Verify EEPROM failed");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int ad9523_sync(struct iio_dev *indio_dev)
+{
+ int ret, tmp;
+
+ ret = ad9523_read(indio_dev, AD9523_STATUS_SIGNALS);
+ if (ret < 0)
+ return ret;
+
+ tmp = ret;
+ tmp |= AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
+
+ ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
+ if (ret < 0)
+ return ret;
+
+ ad9523_io_update(indio_dev);
+ tmp &= ~AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
+
+ ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
+ if (ret < 0)
+ return ret;
+
+ return ad9523_io_update(indio_dev);
+}
+
+static ssize_t ad9523_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ bool state;
+ int ret;
+
+ ret = strtobool(buf, &state);
+ if (ret < 0)
+ return ret;
+
+ if (!state)
+ return 0;
+
+ mutex_lock(&indio_dev->mlock);
+ switch ((u32)this_attr->address) {
+ case AD9523_SYNC:
+ ret = ad9523_sync(indio_dev);
+ break;
+ case AD9523_EEPROM:
+ ret = ad9523_store_eeprom(indio_dev);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static ssize_t ad9523_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad9523_read(indio_dev, AD9523_READBACK_0);
+ if (ret >= 0) {
+ ret = sprintf(buf, "%d\n", !!(ret & (1 <<
+ (u32)this_attr->address)));
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static IIO_DEVICE_ATTR(pll1_locked, S_IRUGO,
+ ad9523_show,
+ NULL,
+ AD9523_STAT_PLL1_LD);
+
+static IIO_DEVICE_ATTR(pll2_locked, S_IRUGO,
+ ad9523_show,
+ NULL,
+ AD9523_STAT_PLL2_LD);
+
+static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, S_IRUGO,
+ ad9523_show,
+ NULL,
+ AD9523_STAT_REFA);
+
+static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, S_IRUGO,
+ ad9523_show,
+ NULL,
+ AD9523_STAT_REFB);
+
+static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, S_IRUGO,
+ ad9523_show,
+ NULL,
+ AD9523_STAT_REF_TEST);
+
+static IIO_DEVICE_ATTR(vcxo_clk_present, S_IRUGO,
+ ad9523_show,
+ NULL,
+ AD9523_STAT_VCXO);
+
+static IIO_DEVICE_ATTR(pll2_feedback_clk_present, S_IRUGO,
+ ad9523_show,
+ NULL,
+ AD9523_STAT_PLL2_FB_CLK);
+
+static IIO_DEVICE_ATTR(pll2_reference_clk_present, S_IRUGO,
+ ad9523_show,
+ NULL,
+ AD9523_STAT_PLL2_REF_CLK);
+
+static IIO_DEVICE_ATTR(sync_dividers, S_IWUSR,
+ NULL,
+ ad9523_store,
+ AD9523_SYNC);
+
+static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR,
+ NULL,
+ ad9523_store,
+ AD9523_EEPROM);
+
+static struct attribute *ad9523_attributes[] = {
+ &iio_dev_attr_sync_dividers.dev_attr.attr,
+ &iio_dev_attr_store_eeprom.dev_attr.attr,
+ &iio_dev_attr_pll2_feedback_clk_present.dev_attr.attr,
+ &iio_dev_attr_pll2_reference_clk_present.dev_attr.attr,
+ &iio_dev_attr_pll1_reference_clk_a_present.dev_attr.attr,
+ &iio_dev_attr_pll1_reference_clk_b_present.dev_attr.attr,
+ &iio_dev_attr_pll1_reference_clk_test_present.dev_attr.attr,
+ &iio_dev_attr_vcxo_clk_present.dev_attr.attr,
+ &iio_dev_attr_pll1_locked.dev_attr.attr,
+ &iio_dev_attr_pll2_locked.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad9523_attribute_group = {
+ .attrs = ad9523_attributes,
+};
+
+static int ad9523_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad9523_state *st = iio_priv(indio_dev);
+ unsigned code;
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret < 0)
+ return ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ *val = !(ret & AD9523_CLK_DIST_PWR_DOWN_EN);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_FREQUENCY:
+ *val = st->vco_out_freq[st->vco_out_map[chan->channel]] /
+ AD9523_CLK_DIST_DIV_REV(ret);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PHASE:
+ code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) /
+ AD9523_CLK_DIST_DIV_REV(ret);
+ *val = code / 1000000;
+ *val2 = (code % 1000000) * 10;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+};
+
+static int ad9523_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad9523_state *st = iio_priv(indio_dev);
+ unsigned reg;
+ int ret, tmp, code;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
+ if (ret < 0)
+ goto out;
+
+ reg = ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val)
+ reg &= ~AD9523_CLK_DIST_PWR_DOWN_EN;
+ else
+ reg |= AD9523_CLK_DIST_PWR_DOWN_EN;
+ break;
+ case IIO_CHAN_INFO_FREQUENCY:
+ if (val <= 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = ad9523_set_clock_provider(indio_dev, chan->channel, val);
+ if (ret < 0)
+ goto out;
+ tmp = st->vco_out_freq[st->vco_out_map[chan->channel]] / val;
+ tmp = clamp(tmp, 1, 1024);
+ reg &= ~(0x3FF << 8);
+ reg |= AD9523_CLK_DIST_DIV(tmp);
+ break;
+ case IIO_CHAN_INFO_PHASE:
+ code = val * 1000000 + val2 % 1000000;
+ tmp = (code * AD9523_CLK_DIST_DIV_REV(ret)) / 3141592;
+ tmp = clamp(tmp, 0, 63);
+ reg &= ~AD9523_CLK_DIST_DIV_PHASE(~0);
+ reg |= AD9523_CLK_DIST_DIV_PHASE(tmp);
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = ad9523_write(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel),
+ reg);
+ if (ret < 0)
+ goto out;
+
+ ad9523_io_update(indio_dev);
+out:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static int ad9523_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ if (readval == NULL) {
+ ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval);
+ ad9523_io_update(indio_dev);
+ } else {
+ ret = ad9523_read(indio_dev, reg | AD9523_R1B);
+ if (ret < 0)
+ goto out_unlock;
+ *readval = ret;
+ ret = 0;
+ }
+
+out_unlock:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static const struct iio_info ad9523_info = {
+ .read_raw = &ad9523_read_raw,
+ .write_raw = &ad9523_write_raw,
+ .debugfs_reg_access = &ad9523_reg_access,
+ .attrs = &ad9523_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad9523_setup(struct iio_dev *indio_dev)
+{
+ struct ad9523_state *st = iio_priv(indio_dev);
+ struct ad9523_platform_data *pdata = st->pdata;
+ struct ad9523_channel_spec *chan;
+ unsigned long active_mask = 0;
+ int ret, i;
+
+ ret = ad9523_write(indio_dev, AD9523_SERIAL_PORT_CONFIG,
+ AD9523_SER_CONF_SOFT_RESET |
+ (st->spi->mode & SPI_3WIRE ? 0 :
+ AD9523_SER_CONF_SDO_ACTIVE));
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_READBACK_CTRL,
+ AD9523_READBACK_CTRL_READ_BUFFERED);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_io_update(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * PLL1 Setup
+ */
+ ret = ad9523_write(indio_dev, AD9523_PLL1_REF_A_DIVIDER,
+ pdata->refa_r_div);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL1_REF_B_DIVIDER,
+ pdata->refb_r_div);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL1_FEEDBACK_DIVIDER,
+ pdata->pll1_feedback_div);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL1_CHARGE_PUMP_CTRL,
+ AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata->
+ pll1_charge_pump_current_nA) |
+ AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL |
+ AD9523_PLL1_BACKLASH_PW_MIN);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL1_INPUT_RECEIVERS_CTRL,
+ AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_RCV_EN) |
+ AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_RCV_EN) |
+ AD_IF(osc_in_diff_en, AD9523_PLL1_OSC_IN_DIFF_EN) |
+ AD_IF(osc_in_cmos_neg_inp_en,
+ AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN) |
+ AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_DIFF_RCV_EN) |
+ AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_DIFF_RCV_EN));
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL1_REF_CTRL,
+ AD_IF(zd_in_diff_en, AD9523_PLL1_ZD_IN_DIFF_EN) |
+ AD_IF(zd_in_cmos_neg_inp_en,
+ AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN) |
+ AD_IF(zero_delay_mode_internal_en,
+ AD9523_PLL1_ZERO_DELAY_MODE_INT) |
+ AD_IF(osc_in_feedback_en, AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN) |
+ AD_IF(refa_cmos_neg_inp_en, AD9523_PLL1_REFA_CMOS_NEG_INP_EN) |
+ AD_IF(refb_cmos_neg_inp_en, AD9523_PLL1_REFB_CMOS_NEG_INP_EN));
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL1_MISC_CTRL,
+ AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN |
+ AD9523_PLL1_REF_MODE(pdata->ref_mode));
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL1_LOOP_FILTER_CTRL,
+ AD9523_PLL1_LOOP_FILTER_RZERO(pdata->pll1_loop_filter_rzero));
+ if (ret < 0)
+ return ret;
+ /*
+ * PLL2 Setup
+ */
+
+ ret = ad9523_write(indio_dev, AD9523_PLL2_CHARGE_PUMP,
+ AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata->
+ pll2_charge_pump_current_nA));
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL2_FEEDBACK_DIVIDER_AB,
+ AD9523_PLL2_FB_NDIV_A_CNT(pdata->pll2_ndiv_a_cnt) |
+ AD9523_PLL2_FB_NDIV_B_CNT(pdata->pll2_ndiv_b_cnt));
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL2_CTRL,
+ AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL |
+ AD9523_PLL2_BACKLASH_CTRL_EN |
+ AD_IF(pll2_freq_doubler_en, AD9523_PLL2_FREQ_DOUBLER_EN));
+ if (ret < 0)
+ return ret;
+
+ st->vco_freq = (pdata->vcxo_freq * (pdata->pll2_freq_doubler_en ? 2 : 1)
+ / pdata->pll2_r2_div) * AD9523_PLL2_FB_NDIV(pdata->
+ pll2_ndiv_a_cnt, pdata->pll2_ndiv_b_cnt);
+
+ ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_CTRL,
+ AD9523_PLL2_VCO_CALIBRATE);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_DIVIDER,
+ AD9523_PLL2_VCO_DIV_M1(pdata->pll2_vco_diff_m1) |
+ AD9523_PLL2_VCO_DIV_M2(pdata->pll2_vco_diff_m2) |
+ AD_IFE(pll2_vco_diff_m1, 0,
+ AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN) |
+ AD_IFE(pll2_vco_diff_m2, 0,
+ AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN));
+ if (ret < 0)
+ return ret;
+
+ if (pdata->pll2_vco_diff_m1)
+ st->vco_out_freq[AD9523_VCO1] =
+ st->vco_freq / pdata->pll2_vco_diff_m1;
+
+ if (pdata->pll2_vco_diff_m2)
+ st->vco_out_freq[AD9523_VCO2] =
+ st->vco_freq / pdata->pll2_vco_diff_m2;
+
+ st->vco_out_freq[AD9523_VCXO] = pdata->vcxo_freq;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL2_R2_DIVIDER,
+ AD9523_PLL2_R2_DIVIDER_VAL(pdata->pll2_r2_div));
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_PLL2_LOOP_FILTER_CTRL,
+ AD9523_PLL2_LOOP_FILTER_CPOLE1(pdata->cpole1) |
+ AD9523_PLL2_LOOP_FILTER_RZERO(pdata->rzero) |
+ AD9523_PLL2_LOOP_FILTER_RPOLE2(pdata->rpole2) |
+ AD_IF(rzero_bypass_en,
+ AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN));
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < pdata->num_channels; i++) {
+ chan = &pdata->channels[i];
+ if (chan->channel_num < AD9523_NUM_CHAN) {
+ __set_bit(chan->channel_num, &active_mask);
+ ret = ad9523_write(indio_dev,
+ AD9523_CHANNEL_CLOCK_DIST(chan->channel_num),
+ AD9523_CLK_DIST_DRIVER_MODE(chan->driver_mode) |
+ AD9523_CLK_DIST_DIV(chan->channel_divider) |
+ AD9523_CLK_DIST_DIV_PHASE(chan->divider_phase) |
+ (chan->sync_ignore_en ?
+ AD9523_CLK_DIST_IGNORE_SYNC_EN : 0) |
+ (chan->divider_output_invert_en ?
+ AD9523_CLK_DIST_INV_DIV_OUTPUT_EN : 0) |
+ (chan->low_power_mode_en ?
+ AD9523_CLK_DIST_LOW_PWR_MODE_EN : 0) |
+ (chan->output_dis ?
+ AD9523_CLK_DIST_PWR_DOWN_EN : 0));
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_vco_out_map(indio_dev, chan->channel_num,
+ chan->use_alt_clock_src);
+ if (ret < 0)
+ return ret;
+
+ st->ad9523_channels[i].type = IIO_ALTVOLTAGE;
+ st->ad9523_channels[i].output = 1;
+ st->ad9523_channels[i].indexed = 1;
+ st->ad9523_channels[i].channel = chan->channel_num;
+ st->ad9523_channels[i].extend_name =
+ chan->extended_name;
+ st->ad9523_channels[i].info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_PHASE) |
+ BIT(IIO_CHAN_INFO_FREQUENCY);
+ }
+ }
+
+ for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN)
+ ad9523_write(indio_dev,
+ AD9523_CHANNEL_CLOCK_DIST(i),
+ AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) |
+ AD9523_CLK_DIST_PWR_DOWN_EN);
+
+ ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS,
+ AD9523_STATUS_MONITOR_01_PLL12_LOCKED);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9523_io_update(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ad9523_probe(struct spi_device *spi)
+{
+ struct ad9523_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad9523_state *st;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&spi->dev, "no platform data?\n");
+ return -EINVAL;
+ }
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+ st->pdata = pdata;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
+ spi_get_device_id(spi)->name;
+ indio_dev->info = &ad9523_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->ad9523_channels;
+ indio_dev->num_channels = pdata->num_channels;
+
+ ret = ad9523_setup(indio_dev);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ dev_info(&spi->dev, "probed %s\n", indio_dev->name);
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+error_put_reg:
+ if (!IS_ERR(st->reg))
+ regulator_put(st->reg);
+
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad9523_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad9523_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (!IS_ERR(st->reg)) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad9523_id[] = {
+ {"ad9523-1", 9523},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad9523_id);
+
+static struct spi_driver ad9523_driver = {
+ .driver = {
+ .name = "ad9523",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad9523_probe,
+ .remove = ad9523_remove,
+ .id_table = ad9523_id,
+};
+module_spi_driver(ad9523_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
new file mode 100644
index 00000000..e76d4ace
--- /dev/null
+++ b/drivers/iio/frequency/adf4350.c
@@ -0,0 +1,486 @@
+/*
+ * ADF4350/ADF4351 SPI Wideband Synthesizer driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <asm/div64.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/frequency/adf4350.h>
+
+enum {
+ ADF4350_FREQ,
+ ADF4350_FREQ_REFIN,
+ ADF4350_FREQ_RESOLUTION,
+ ADF4350_PWRDOWN,
+};
+
+struct adf4350_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ struct adf4350_platform_data *pdata;
+ unsigned long clkin;
+ unsigned long chspc; /* Channel Spacing */
+ unsigned long fpfd; /* Phase Frequency Detector */
+ unsigned long min_out_freq;
+ unsigned r0_fract;
+ unsigned r0_int;
+ unsigned r1_mod;
+ unsigned r4_rf_div_sel;
+ unsigned long regs[6];
+ unsigned long regs_hw[6];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be32 val ____cacheline_aligned;
+};
+
+static struct adf4350_platform_data default_pdata = {
+ .clkin = 122880000,
+ .channel_spacing = 10000,
+ .r2_user_settings = ADF4350_REG2_PD_POLARITY_POS |
+ ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500),
+ .r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0),
+ .r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) |
+ ADF4350_REG4_MUTE_TILL_LOCK_EN,
+ .gpio_lock_detect = -1,
+};
+
+static int adf4350_sync_config(struct adf4350_state *st)
+{
+ int ret, i, doublebuf = 0;
+
+ for (i = ADF4350_REG5; i >= ADF4350_REG0; i--) {
+ if ((st->regs_hw[i] != st->regs[i]) ||
+ ((i == ADF4350_REG0) && doublebuf)) {
+
+ switch (i) {
+ case ADF4350_REG1:
+ case ADF4350_REG4:
+ doublebuf = 1;
+ break;
+ }
+
+ st->val = cpu_to_be32(st->regs[i] | i);
+ ret = spi_write(st->spi, &st->val, 4);
+ if (ret < 0)
+ return ret;
+ st->regs_hw[i] = st->regs[i];
+ dev_dbg(&st->spi->dev, "[%d] 0x%X\n",
+ i, (u32)st->regs[i] | i);
+ }
+ }
+ return 0;
+}
+
+static int adf4350_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct adf4350_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (reg > ADF4350_REG5)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ if (readval == NULL) {
+ st->regs[reg] = writeval & ~(BIT(0) | BIT(1) | BIT(2));
+ ret = adf4350_sync_config(st);
+ } else {
+ *readval = st->regs_hw[reg];
+ ret = 0;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int adf4350_tune_r_cnt(struct adf4350_state *st, unsigned short r_cnt)
+{
+ struct adf4350_platform_data *pdata = st->pdata;
+
+ do {
+ r_cnt++;
+ st->fpfd = (st->clkin * (pdata->ref_doubler_en ? 2 : 1)) /
+ (r_cnt * (pdata->ref_div2_en ? 2 : 1));
+ } while (st->fpfd > ADF4350_MAX_FREQ_PFD);
+
+ return r_cnt;
+}
+
+static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
+{
+ struct adf4350_platform_data *pdata = st->pdata;
+ u64 tmp;
+ u32 div_gcd, prescaler, chspc;
+ u16 mdiv, r_cnt = 0;
+ u8 band_sel_div;
+
+ if (freq > ADF4350_MAX_OUT_FREQ || freq < st->min_out_freq)
+ return -EINVAL;
+
+ if (freq > ADF4350_MAX_FREQ_45_PRESC) {
+ prescaler = ADF4350_REG1_PRESCALER;
+ mdiv = 75;
+ } else {
+ prescaler = 0;
+ mdiv = 23;
+ }
+
+ st->r4_rf_div_sel = 0;
+
+ while (freq < ADF4350_MIN_VCO_FREQ) {
+ freq <<= 1;
+ st->r4_rf_div_sel++;
+ }
+
+ /*
+ * Allow a predefined reference division factor
+ * if not set, compute our own
+ */
+ if (pdata->ref_div_factor)
+ r_cnt = pdata->ref_div_factor - 1;
+
+ chspc = st->chspc;
+
+ do {
+ do {
+ do {
+ r_cnt = adf4350_tune_r_cnt(st, r_cnt);
+ st->r1_mod = st->fpfd / chspc;
+ if (r_cnt > ADF4350_MAX_R_CNT) {
+ /* try higher spacing values */
+ chspc++;
+ r_cnt = 0;
+ }
+ } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt);
+ } while (r_cnt == 0);
+
+ tmp = freq * (u64)st->r1_mod + (st->fpfd >> 1);
+ do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
+ st->r0_fract = do_div(tmp, st->r1_mod);
+ st->r0_int = tmp;
+ } while (mdiv > st->r0_int);
+
+ band_sel_div = DIV_ROUND_UP(st->fpfd, ADF4350_MAX_BANDSEL_CLK);
+
+ if (st->r0_fract && st->r1_mod) {
+ div_gcd = gcd(st->r1_mod, st->r0_fract);
+ st->r1_mod /= div_gcd;
+ st->r0_fract /= div_gcd;
+ } else {
+ st->r0_fract = 0;
+ st->r1_mod = 1;
+ }
+
+ dev_dbg(&st->spi->dev, "VCO: %llu Hz, PFD %lu Hz\n"
+ "REF_DIV %d, R0_INT %d, R0_FRACT %d\n"
+ "R1_MOD %d, RF_DIV %d\nPRESCALER %s, BAND_SEL_DIV %d\n",
+ freq, st->fpfd, r_cnt, st->r0_int, st->r0_fract, st->r1_mod,
+ 1 << st->r4_rf_div_sel, prescaler ? "8/9" : "4/5",
+ band_sel_div);
+
+ st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) |
+ ADF4350_REG0_FRACT(st->r0_fract);
+
+ st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(1) |
+ ADF4350_REG1_MOD(st->r1_mod) |
+ prescaler;
+
+ st->regs[ADF4350_REG2] =
+ ADF4350_REG2_10BIT_R_CNT(r_cnt) |
+ ADF4350_REG2_DOUBLE_BUFF_EN |
+ (pdata->ref_doubler_en ? ADF4350_REG2_RMULT2_EN : 0) |
+ (pdata->ref_div2_en ? ADF4350_REG2_RDIV2_EN : 0) |
+ (pdata->r2_user_settings & (ADF4350_REG2_PD_POLARITY_POS |
+ ADF4350_REG2_LDP_6ns | ADF4350_REG2_LDF_INT_N |
+ ADF4350_REG2_CHARGE_PUMP_CURR_uA(5000) |
+ ADF4350_REG2_MUXOUT(0x7) | ADF4350_REG2_NOISE_MODE(0x3)));
+
+ st->regs[ADF4350_REG3] = pdata->r3_user_settings &
+ (ADF4350_REG3_12BIT_CLKDIV(0xFFF) |
+ ADF4350_REG3_12BIT_CLKDIV_MODE(0x3) |
+ ADF4350_REG3_12BIT_CSR_EN |
+ ADF4351_REG3_CHARGE_CANCELLATION_EN |
+ ADF4351_REG3_ANTI_BACKLASH_3ns_EN |
+ ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH);
+
+ st->regs[ADF4350_REG4] =
+ ADF4350_REG4_FEEDBACK_FUND |
+ ADF4350_REG4_RF_DIV_SEL(st->r4_rf_div_sel) |
+ ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(band_sel_div) |
+ ADF4350_REG4_RF_OUT_EN |
+ (pdata->r4_user_settings &
+ (ADF4350_REG4_OUTPUT_PWR(0x3) |
+ ADF4350_REG4_AUX_OUTPUT_PWR(0x3) |
+ ADF4350_REG4_AUX_OUTPUT_EN |
+ ADF4350_REG4_AUX_OUTPUT_FUND |
+ ADF4350_REG4_MUTE_TILL_LOCK_EN));
+
+ st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL;
+
+ return adf4350_sync_config(st);
+}
+
+static ssize_t adf4350_write(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct adf4350_state *st = iio_priv(indio_dev);
+ unsigned long long readin;
+ int ret;
+
+ ret = kstrtoull(buf, 10, &readin);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+ switch ((u32)private) {
+ case ADF4350_FREQ:
+ ret = adf4350_set_freq(st, readin);
+ break;
+ case ADF4350_FREQ_REFIN:
+ if (readin > ADF4350_MAX_FREQ_REFIN)
+ ret = -EINVAL;
+ else
+ st->clkin = readin;
+ break;
+ case ADF4350_FREQ_RESOLUTION:
+ if (readin == 0)
+ ret = -EINVAL;
+ else
+ st->chspc = readin;
+ break;
+ case ADF4350_PWRDOWN:
+ if (readin)
+ st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
+ else
+ st->regs[ADF4350_REG2] &= ~ADF4350_REG2_POWER_DOWN_EN;
+
+ adf4350_sync_config(st);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static ssize_t adf4350_read(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct adf4350_state *st = iio_priv(indio_dev);
+ unsigned long long val;
+ int ret = 0;
+
+ mutex_lock(&indio_dev->mlock);
+ switch ((u32)private) {
+ case ADF4350_FREQ:
+ val = (u64)((st->r0_int * st->r1_mod) + st->r0_fract) *
+ (u64)st->fpfd;
+ do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel));
+ /* PLL unlocked? return error */
+ if (gpio_is_valid(st->pdata->gpio_lock_detect))
+ if (!gpio_get_value(st->pdata->gpio_lock_detect)) {
+ dev_dbg(&st->spi->dev, "PLL un-locked\n");
+ ret = -EBUSY;
+ }
+ break;
+ case ADF4350_FREQ_REFIN:
+ val = st->clkin;
+ break;
+ case ADF4350_FREQ_RESOLUTION:
+ val = st->chspc;
+ break;
+ case ADF4350_PWRDOWN:
+ val = !!(st->regs[ADF4350_REG2] & ADF4350_REG2_POWER_DOWN_EN);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret < 0 ? ret : sprintf(buf, "%llu\n", val);
+}
+
+#define _ADF4350_EXT_INFO(_name, _ident) { \
+ .name = _name, \
+ .read = adf4350_read, \
+ .write = adf4350_write, \
+ .private = _ident, \
+}
+
+static const struct iio_chan_spec_ext_info adf4350_ext_info[] = {
+ /* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are
+ * values > 2^32 in order to support the entire frequency range
+ * in Hz. Using scale is a bit ugly.
+ */
+ _ADF4350_EXT_INFO("frequency", ADF4350_FREQ),
+ _ADF4350_EXT_INFO("frequency_resolution", ADF4350_FREQ_RESOLUTION),
+ _ADF4350_EXT_INFO("refin_frequency", ADF4350_FREQ_REFIN),
+ _ADF4350_EXT_INFO("powerdown", ADF4350_PWRDOWN),
+ { },
+};
+
+static const struct iio_chan_spec adf4350_chan = {
+ .type = IIO_ALTVOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .ext_info = adf4350_ext_info,
+};
+
+static const struct iio_info adf4350_info = {
+ .debugfs_reg_access = &adf4350_reg_access,
+ .driver_module = THIS_MODULE,
+};
+
+static int adf4350_probe(struct spi_device *spi)
+{
+ struct adf4350_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct adf4350_state *st;
+ int ret;
+
+ if (!pdata) {
+ dev_warn(&spi->dev, "no platform data? using default\n");
+
+ pdata = &default_pdata;
+ }
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+ st->pdata = pdata;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
+ spi_get_device_id(spi)->name;
+
+ indio_dev->info = &adf4350_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &adf4350_chan;
+ indio_dev->num_channels = 1;
+
+ st->chspc = pdata->channel_spacing;
+ st->clkin = pdata->clkin;
+
+ st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ?
+ ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ;
+
+ memset(st->regs_hw, 0xFF, sizeof(st->regs_hw));
+
+ if (gpio_is_valid(pdata->gpio_lock_detect)) {
+ ret = gpio_request(pdata->gpio_lock_detect, indio_dev->name);
+ if (ret) {
+ dev_err(&spi->dev, "fail to request lock detect GPIO-%d",
+ pdata->gpio_lock_detect);
+ goto error_disable_reg;
+ }
+ gpio_direction_input(pdata->gpio_lock_detect);
+ }
+
+ if (pdata->power_up_frequency) {
+ ret = adf4350_set_freq(st, pdata->power_up_frequency);
+ if (ret)
+ goto error_free_gpio;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_gpio;
+
+ return 0;
+
+error_free_gpio:
+ if (gpio_is_valid(pdata->gpio_lock_detect))
+ gpio_free(pdata->gpio_lock_detect);
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+error_put_reg:
+ if (!IS_ERR(st->reg))
+ regulator_put(st->reg);
+
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int adf4350_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adf4350_state *st = iio_priv(indio_dev);
+ struct regulator *reg = st->reg;
+
+ st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
+ adf4350_sync_config(st);
+
+ iio_device_unregister(indio_dev);
+
+ if (!IS_ERR(reg)) {
+ regulator_disable(reg);
+ regulator_put(reg);
+ }
+
+ if (gpio_is_valid(st->pdata->gpio_lock_detect))
+ gpio_free(st->pdata->gpio_lock_detect);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id adf4350_id[] = {
+ {"adf4350", 4350},
+ {"adf4351", 4351},
+ {}
+};
+
+static struct spi_driver adf4350_driver = {
+ .driver = {
+ .name = "adf4350",
+ .owner = THIS_MODULE,
+ },
+ .probe = adf4350_probe,
+ .remove = adf4350_remove,
+ .id_table = adf4350_id,
+};
+module_spi_driver(adf4350_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
new file mode 100644
index 00000000..107cafcb
--- /dev/null
+++ b/drivers/iio/gyro/Kconfig
@@ -0,0 +1,81 @@
+#
+# IIO Digital Gyroscope Sensor drivers configuration
+#
+menu "Digital gyroscope sensors"
+
+config ADIS16080
+ tristate "Analog Devices ADIS16080/100 Yaw Rate Gyroscope with SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices ADIS16080, ADIS16100 Yaw
+ Rate Gyroscope with SPI.
+
+config ADIS16136
+ tristate "Analog devices ADIS16136 and similar gyroscopes driver"
+ depends on SPI_MASTER
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ 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
+ select IIO_TRIGGERED_BUFFER
+ select HID_SENSOR_IIO_COMMON
+ select HID_SENSOR_IIO_TRIGGER
+ tristate "HID Gyroscope 3D"
+ help
+ 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, 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
new file mode 100644
index 00000000..225d2890
--- /dev/null
+++ b/drivers/iio/gyro/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for industrial I/O gyroscope sensor drivers
+#
+
+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/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c
new file mode 100644
index 00000000..e1bb5f99
--- /dev/null
+++ b/drivers/iio/gyro/adis16080.c
@@ -0,0 +1,259 @@
+/*
+ * ADIS16080/100 Yaw Rate Gyroscope with SPI driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define ADIS16080_DIN_GYRO (0 << 10) /* Gyroscope output */
+#define ADIS16080_DIN_TEMP (1 << 10) /* Temperature output */
+#define ADIS16080_DIN_AIN1 (2 << 10)
+#define ADIS16080_DIN_AIN2 (3 << 10)
+
+/*
+ * 1: Write contents on DIN to control register.
+ * 0: No changes to control register.
+ */
+
+#define ADIS16080_DIN_WRITE (1 << 15)
+
+struct adis16080_chip_info {
+ int scale_val;
+ int scale_val2;
+};
+
+/**
+ * struct adis16080_state - device instance specific data
+ * @us: actual spi_device to write data
+ * @info: chip specific parameters
+ * @buf: transmit or receive buffer
+ **/
+struct adis16080_state {
+ struct spi_device *us;
+ const struct adis16080_chip_info *info;
+
+ __be16 buf ____cacheline_aligned;
+};
+
+static int adis16080_read_sample(struct iio_dev *indio_dev,
+ u16 addr, int *val)
+{
+ struct adis16080_state *st = iio_priv(indio_dev);
+ struct spi_message m;
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->buf,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .rx_buf = &st->buf,
+ .len = 2,
+ },
+ };
+
+ st->buf = cpu_to_be16(addr | ADIS16080_DIN_WRITE);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+
+ ret = spi_sync(st->us, &m);
+ if (ret == 0)
+ *val = sign_extend32(be16_to_cpu(st->buf), 11);
+
+ return ret;
+}
+
+static int adis16080_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct adis16080_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = adis16080_read_sample(indio_dev, chan->address, val);
+ mutex_unlock(&indio_dev->mlock);
+ return ret ? ret : IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val = st->info->scale_val;
+ *val2 = st->info->scale_val2;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_VOLTAGE:
+ /* VREF = 5V, 12 bits */
+ *val = 5000;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_TEMP:
+ /* 85 C = 585, 25 C = 0 */
+ *val = 85000 - 25000;
+ *val2 = 585;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ /* 2.5 V = 0 */
+ *val = 2048;
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ /* 85 C = 585, 25 C = 0 */
+ *val = DIV_ROUND_CLOSEST(25 * 585, 85 - 25);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16080_channels[] = {
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = ADIS16080_DIN_GYRO,
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = ADIS16080_DIN_AIN1,
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = ADIS16080_DIN_AIN2,
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = ADIS16080_DIN_TEMP,
+ }
+};
+
+static const struct iio_info adis16080_info = {
+ .read_raw = &adis16080_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+enum {
+ ID_ADIS16080,
+ ID_ADIS16100,
+};
+
+static const struct adis16080_chip_info adis16080_chip_info[] = {
+ [ID_ADIS16080] = {
+ /* 80 degree = 819, 819 rad = 46925 degree */
+ .scale_val = 80,
+ .scale_val2 = 46925,
+ },
+ [ID_ADIS16100] = {
+ /* 300 degree = 1230, 1230 rad = 70474 degree */
+ .scale_val = 300,
+ .scale_val2 = 70474,
+ },
+};
+
+static int adis16080_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ int ret;
+ struct adis16080_state *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ /* Allocate the comms buffers */
+ st->us = spi;
+ st->info = &adis16080_chip_info[id->driver_data];
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->channels = adis16080_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16080_channels);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16080_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_dev;
+ return 0;
+
+error_free_dev:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+static int adis16080_remove(struct spi_device *spi)
+{
+ iio_device_unregister(spi_get_drvdata(spi));
+ iio_device_free(spi_get_drvdata(spi));
+
+ return 0;
+}
+
+static const struct spi_device_id adis16080_ids[] = {
+ { "adis16080", ID_ADIS16080 },
+ { "adis16100", ID_ADIS16100 },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, adis16080_ids);
+
+static struct spi_driver adis16080_driver = {
+ .driver = {
+ .name = "adis16080",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16080_probe,
+ .remove = adis16080_remove,
+ .id_table = adis16080_ids,
+};
+module_spi_driver(adis16080_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16080/100 Yaw Rate Gyroscope Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
new file mode 100644
index 00000000..058e6d5c
--- /dev/null
+++ b/drivers/iio/gyro/adis16136.c
@@ -0,0 +1,581 @@
+/*
+ * ADIS16133/ADIS16135/ADIS16136 gyroscope driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include <linux/debugfs.h>
+
+#define ADIS16136_REG_FLASH_CNT 0x00
+#define ADIS16136_REG_TEMP_OUT 0x02
+#define ADIS16136_REG_GYRO_OUT2 0x04
+#define ADIS16136_REG_GYRO_OUT 0x06
+#define ADIS16136_REG_GYRO_OFF2 0x08
+#define ADIS16136_REG_GYRO_OFF 0x0A
+#define ADIS16136_REG_ALM_MAG1 0x10
+#define ADIS16136_REG_ALM_MAG2 0x12
+#define ADIS16136_REG_ALM_SAMPL1 0x14
+#define ADIS16136_REG_ALM_SAMPL2 0x16
+#define ADIS16136_REG_ALM_CTRL 0x18
+#define ADIS16136_REG_GPIO_CTRL 0x1A
+#define ADIS16136_REG_MSC_CTRL 0x1C
+#define ADIS16136_REG_SMPL_PRD 0x1E
+#define ADIS16136_REG_AVG_CNT 0x20
+#define ADIS16136_REG_DEC_RATE 0x22
+#define ADIS16136_REG_SLP_CTRL 0x24
+#define ADIS16136_REG_DIAG_STAT 0x26
+#define ADIS16136_REG_GLOB_CMD 0x28
+#define ADIS16136_REG_LOT1 0x32
+#define ADIS16136_REG_LOT2 0x34
+#define ADIS16136_REG_LOT3 0x36
+#define ADIS16136_REG_PROD_ID 0x38
+#define ADIS16136_REG_SERIAL_NUM 0x3A
+
+#define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL 2
+#define ADIS16136_DIAG_STAT_SPI_FAIL 3
+#define ADIS16136_DIAG_STAT_SELF_TEST_FAIL 5
+#define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL 6
+
+#define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11)
+#define ADIS16136_MSC_CTRL_SELF_TEST BIT(10)
+
+struct adis16136_chip_info {
+ unsigned int precision;
+ unsigned int fullscale;
+};
+
+struct adis16136 {
+ const struct adis16136_chip_info *chip_info;
+
+ struct adis adis;
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+static ssize_t adis16136_show_serial(struct file *file,
+ char __user *userbuf, size_t count, loff_t *ppos)
+{
+ struct adis16136 *adis16136 = file->private_data;
+ uint16_t lot1, lot2, lot3, serial;
+ char buf[20];
+ size_t len;
+ int ret;
+
+ ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM,
+ &serial);
+ if (ret < 0)
+ return ret;
+
+ ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1);
+ if (ret < 0)
+ return ret;
+
+ ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2);
+ if (ret < 0)
+ return ret;
+
+ ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3);
+ if (ret < 0)
+ return ret;
+
+ len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2,
+ lot3, serial);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static const struct file_operations adis16136_serial_fops = {
+ .open = simple_open,
+ .read = adis16136_show_serial,
+ .llseek = default_llseek,
+ .owner = THIS_MODULE,
+};
+
+static int adis16136_show_product_id(void *arg, u64 *val)
+{
+ struct adis16136 *adis16136 = arg;
+ u16 prod_id;
+ int ret;
+
+ ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
+ &prod_id);
+ if (ret < 0)
+ return ret;
+
+ *val = prod_id;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16136_product_id_fops,
+ adis16136_show_product_id, NULL, "%llu\n");
+
+static int adis16136_show_flash_count(void *arg, u64 *val)
+{
+ struct adis16136 *adis16136 = arg;
+ uint16_t flash_count;
+ int ret;
+
+ ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT,
+ &flash_count);
+ if (ret < 0)
+ return ret;
+
+ *val = flash_count;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16136_flash_count_fops,
+ adis16136_show_flash_count, NULL, "%lld\n");
+
+static int adis16136_debugfs_init(struct iio_dev *indio_dev)
+{
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+
+ debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
+ adis16136, &adis16136_serial_fops);
+ debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
+ adis16136, &adis16136_product_id_fops);
+ debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
+ adis16136, &adis16136_flash_count_fops);
+
+ return 0;
+}
+
+#else
+
+static int adis16136_debugfs_init(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+#endif
+
+static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq)
+{
+ unsigned int t;
+
+ t = 32768 / freq;
+ if (t < 0xf)
+ t = 0xf;
+ else if (t > 0xffff)
+ t = 0xffff;
+ else
+ t--;
+
+ return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t);
+}
+
+static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
+{
+ uint16_t t;
+ int ret;
+
+ ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
+ if (ret < 0)
+ return ret;
+
+ *freq = 32768 / (t + 1);
+
+ return 0;
+}
+
+static ssize_t adis16136_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);
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val == 0)
+ return -EINVAL;
+
+ ret = adis16136_set_freq(adis16136, val);
+
+ return ret ? ret : len;
+}
+
+static ssize_t adis16136_read_frequency(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+ unsigned int freq;
+ int ret;
+
+ ret = adis16136_get_freq(adis16136, &freq);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", freq);
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ adis16136_read_frequency,
+ adis16136_write_frequency);
+
+static const unsigned adis16136_3db_divisors[] = {
+ [0] = 2, /* Special case */
+ [1] = 6,
+ [2] = 12,
+ [3] = 25,
+ [4] = 50,
+ [5] = 100,
+ [6] = 200,
+ [7] = 200, /* Not a valid setting */
+};
+
+static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
+{
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+ unsigned int freq;
+ int i, ret;
+
+ ret = adis16136_get_freq(adis16136, &freq);
+ if (ret < 0)
+ return ret;
+
+ for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
+ if (freq / adis16136_3db_divisors[i] >= val)
+ break;
+ }
+
+ return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i);
+}
+
+static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
+{
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+ unsigned int freq;
+ uint16_t val16;
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+
+ ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = adis16136_get_freq(adis16136, &freq);
+ if (ret < 0)
+ goto err_unlock;
+
+ *val = freq / adis16136_3db_divisors[val16 & 0x07];
+
+err_unlock:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : IIO_VAL_INT;
+}
+
+static int adis16136_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+ uint32_t val32;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan, 0, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val = adis16136->chip_info->precision;
+ *val2 = (adis16136->chip_info->fullscale << 16);
+ return IIO_VAL_FRACTIONAL;
+ case IIO_TEMP:
+ *val = 10;
+ *val2 = 697000; /* 0.010697 degree Celsius */
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = adis_read_reg_32(&adis16136->adis,
+ ADIS16136_REG_GYRO_OFF2, &val32);
+ if (ret < 0)
+ return ret;
+
+ *val = sign_extend32(val32, 31);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return adis16136_get_filter(indio_dev, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adis16136_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int val, int val2, long info)
+{
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return adis_write_reg_32(&adis16136->adis,
+ ADIS16136_REG_GYRO_OFF2, val);
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return adis16136_set_filter(indio_dev, val);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+enum {
+ ADIS16136_SCAN_GYRO,
+ ADIS16136_SCAN_TEMP,
+};
+
+static const struct iio_chan_spec adis16136_channels[] = {
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+
+ .address = ADIS16136_REG_GYRO_OUT2,
+ .scan_index = ADIS16136_SCAN_GYRO,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = ADIS16136_REG_TEMP_OUT,
+ .scan_index = ADIS16136_SCAN_TEMP,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static struct attribute *adis16136_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adis16136_attribute_group = {
+ .attrs = adis16136_attributes,
+};
+
+static const struct iio_info adis16136_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &adis16136_attribute_group,
+ .read_raw = &adis16136_read_raw,
+ .write_raw = &adis16136_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .debugfs_reg_access = adis_debugfs_reg_access,
+};
+
+static int adis16136_stop_device(struct iio_dev *indio_dev)
+{
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+ int ret;
+
+ ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff);
+ if (ret)
+ dev_err(&indio_dev->dev,
+ "Could not power down device: %d\n", ret);
+
+ return ret;
+}
+
+static int adis16136_initial_setup(struct iio_dev *indio_dev)
+{
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+ unsigned int device_id;
+ uint16_t prod_id;
+ int ret;
+
+ ret = adis_initial_startup(&adis16136->adis);
+ if (ret)
+ return ret;
+
+ ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
+ &prod_id);
+ if (ret)
+ return ret;
+
+ sscanf(indio_dev->name, "adis%u\n", &device_id);
+
+ if (prod_id != device_id)
+ dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
+ device_id, prod_id);
+
+ return 0;
+}
+
+static const char * const adis16136_status_error_msgs[] = {
+ [ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed",
+ [ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure",
+ [ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error",
+ [ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error",
+};
+
+static const struct adis_data adis16136_data = {
+ .diag_stat_reg = ADIS16136_REG_DIAG_STAT,
+ .glob_cmd_reg = ADIS16136_REG_GLOB_CMD,
+ .msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
+
+ .self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
+ .startup_delay = 80,
+
+ .read_delay = 10,
+ .write_delay = 10,
+
+ .status_error_msgs = adis16136_status_error_msgs,
+ .status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) |
+ BIT(ADIS16136_DIAG_STAT_SPI_FAIL) |
+ BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) |
+ BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL),
+};
+
+enum adis16136_id {
+ ID_ADIS16133,
+ ID_ADIS16135,
+ ID_ADIS16136,
+};
+
+static const struct adis16136_chip_info adis16136_chip_info[] = {
+ [ID_ADIS16133] = {
+ .precision = IIO_DEGREE_TO_RAD(1200),
+ .fullscale = 24000,
+ },
+ [ID_ADIS16135] = {
+ .precision = IIO_DEGREE_TO_RAD(300),
+ .fullscale = 24000,
+ },
+ [ID_ADIS16136] = {
+ .precision = IIO_DEGREE_TO_RAD(450),
+ .fullscale = 24623,
+ },
+};
+
+static int adis16136_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct adis16136 *adis16136;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*adis16136));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ adis16136 = iio_priv(indio_dev);
+
+ adis16136->chip_info = &adis16136_chip_info[id->driver_data];
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->channels = adis16136_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16136_channels);
+ indio_dev->info = &adis16136_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data);
+ if (ret)
+ goto error_free_dev;
+
+ ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL);
+ if (ret)
+ goto error_free_dev;
+
+ ret = adis16136_initial_setup(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_stop_device;
+
+ adis16136_debugfs_init(indio_dev);
+
+ return 0;
+
+error_stop_device:
+ adis16136_stop_device(indio_dev);
+error_cleanup_buffer:
+ adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
+error_free_dev:
+ iio_device_free(indio_dev);
+ return ret;
+}
+
+static int adis16136_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis16136 *adis16136 = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis16136_stop_device(indio_dev);
+
+ adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id adis16136_ids[] = {
+ { "adis16133", ID_ADIS16133 },
+ { "adis16135", ID_ADIS16135 },
+ { "adis16136", ID_ADIS16136 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adis16136_ids);
+
+static struct spi_driver adis16136_driver = {
+ .driver = {
+ .name = "adis16136",
+ .owner = THIS_MODULE,
+ },
+ .id_table = adis16136_ids,
+ .probe = adis16136_probe,
+ .remove = adis16136_remove,
+};
+module_spi_driver(adis16136_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/adxrs450.c b/drivers/iio/gyro/adxrs450.c
new file mode 100644
index 00000000..8bd72b49
--- /dev/null
+++ b/drivers/iio/gyro/adxrs450.c
@@ -0,0 +1,494 @@
+/*
+ * ADXRS450/ADXRS453 Digital Output Gyroscope Driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.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
+ * @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);
+ tx = ADXRS450_READ_DATA | (reg_address << 17);
+
+ if (!(hweight32(tx) & 1))
+ tx |= ADXRS450_P;
+
+ 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(st->rx) >> 5) & 0xFFFF;
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+/**
+ * adxrs450_spi_write_reg_16() - write 2 bytes data to a register pair
+ * @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,
+ u8 reg_address,
+ u16 val)
+{
+ struct adxrs450_state *st = iio_priv(indio_dev);
+ u32 tx;
+ int ret;
+
+ mutex_lock(&st->buf_lock);
+ tx = ADXRS450_WRITE_DATA | (reg_address << 17) | (val << 1);
+
+ if (!(hweight32(tx) & 1))
+ tx |= ADXRS450_P;
+
+ 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);
+ 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
+ * @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 = cpu_to_be32(ADXRS450_SENSOR_DATA);
+
+ 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(st->rx) >> 10) & 0xFFFF;
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return 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)
+{
+ int ret;
+ u32 tx;
+ struct spi_transfer xfers = {
+ .tx_buf = &st->tx,
+ .rx_buf = &st->rx,
+ .bits_per_word = 8,
+ .len = sizeof(st->tx),
+ };
+
+ mutex_lock(&st->buf_lock);
+ tx = ADXRS450_SENSOR_DATA;
+ if (chk)
+ tx |= (ADXRS450_CHK | ADXRS450_P);
+ st->tx = cpu_to_be32(tx);
+ ret = spi_sync_transfer(st->us, &xfers, 1);
+ if (ret) {
+ dev_err(&st->us->dev, "Problem while reading initializing data\n");
+ goto error_ret;
+ }
+
+ *val = be32_to_cpu(st->rx);
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+/* Recommended Startup Sequence by spec */
+static int adxrs450_initial_setup(struct iio_dev *indio_dev)
+{
+ u32 t;
+ u16 data;
+ int ret;
+ struct adxrs450_state *st = iio_priv(indio_dev);
+
+ msleep(ADXRS450_STARTUP_DELAY*2);
+ ret = adxrs450_spi_initial(st, &t, 1);
+ if (ret)
+ return ret;
+ if (t != 0x01)
+ 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);
+ if (ret)
+ return ret;
+
+ msleep(ADXRS450_STARTUP_DELAY);
+ ret = adxrs450_spi_initial(st, &t, 0);
+ if (ret)
+ return ret;
+ if (((t & 0xff) | 0x01) != 0xff || ADXRS450_GET_ST(t) != 2) {
+ dev_err(&st->us->dev, "The second response is not correct!\n");
+ return -EIO;
+
+ }
+ ret = adxrs450_spi_initial(st, &t, 0);
+ if (ret)
+ return ret;
+ if (((t & 0xff) | 0x01) != 0xff || ADXRS450_GET_ST(t) != 2) {
+ dev_err(&st->us->dev, "The third response is not correct!\n");
+ return -EIO;
+
+ }
+ ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_FAULT1, &data);
+ if (ret)
+ return ret;
+ if (data & 0x0fff) {
+ dev_err(&st->us->dev, "The device is not in normal status!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int adxrs450_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ 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);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int adxrs450_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ int ret;
+ s16 t;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ ret = adxrs450_spi_sensor_data(indio_dev, &t);
+ if (ret)
+ break;
+ *val = t;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_TEMP:
+ ret = adxrs450_spi_read_reg_16(indio_dev,
+ ADXRS450_TEMP1, &t);
+ if (ret)
+ break;
+ *val = (t >> 6) + 225;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val = 0;
+ *val2 = 218166;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_TEMP:
+ *val = 200;
+ *val2 = 0;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW:
+ ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_QUAD1, &t);
+ if (ret)
+ break;
+ *val = t;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_DNC1, &t);
+ if (ret)
+ break;
+ *val = sign_extend32(t, 9);
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct iio_chan_spec adxrs450_channels[2][2] = {
+ [ID_ADXRS450] = {
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }
+ },
+ [ID_ADXRS453] = {
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW),
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }
+ },
+};
+
+static const struct iio_info adxrs450_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &adxrs450_read_raw,
+ .write_raw = &adxrs450_write_raw,
+};
+
+static int adxrs450_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adxrs450_state *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ st = iio_priv(indio_dev);
+ st->us = spi;
+ mutex_init(&st->buf_lock);
+ /* This is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adxrs450_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels =
+ adxrs450_channels[spi_get_device_id(spi)->driver_data];
+ indio_dev->num_channels = ARRAY_SIZE(adxrs450_channels);
+ indio_dev->name = spi->dev.driver->name;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_dev;
+
+ /* Get the device into a sane initial state */
+ ret = adxrs450_initial_setup(indio_dev);
+ if (ret)
+ goto error_initial;
+ return 0;
+error_initial:
+ iio_device_unregister(indio_dev);
+error_free_dev:
+ iio_device_free(indio_dev);
+
+error_ret:
+ return ret;
+}
+
+static int adxrs450_remove(struct spi_device *spi)
+{
+ iio_device_unregister(spi_get_drvdata(spi));
+ iio_device_free(spi_get_drvdata(spi));
+
+ return 0;
+}
+
+static const struct spi_device_id adxrs450_id[] = {
+ {"adxrs450", ID_ADXRS450},
+ {"adxrs453", ID_ADXRS453},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adxrs450_id);
+
+static struct spi_driver adxrs450_driver = {
+ .driver = {
+ .name = "adxrs450",
+ .owner = THIS_MODULE,
+ },
+ .probe = adxrs450_probe,
+ .remove = adxrs450_remove,
+ .id_table = adxrs450_id,
+};
+module_spi_driver(adxrs450_driver);
+
+MODULE_AUTHOR("Cliff Cai <cliff.cai@xxxxxxxxxx>");
+MODULE_DESCRIPTION("Analog Devices ADXRS450/ADXRS453 Gyroscope SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
new file mode 100644
index 00000000..bc943dd4
--- /dev/null
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -0,0 +1,404 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Gyro-3D: 0x200076*/
+#define DRIVER_NAME "HID-SENSOR-200076"
+
+enum gyro_3d_channel {
+ CHANNEL_SCAN_INDEX_X,
+ CHANNEL_SCAN_INDEX_Y,
+ CHANNEL_SCAN_INDEX_Z,
+ GYRO_3D_CHANNEL_MAX,
+};
+
+struct gyro_3d_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_common common_attributes;
+ struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
+ u32 gyro_val[GYRO_3D_CHANNEL_MAX];
+};
+
+static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = {
+ HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS,
+ HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS,
+ HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec gyro_3d_channels[] = {
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_HYSTERESIS),
+ .scan_index = CHANNEL_SCAN_INDEX_X,
+ }, {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_HYSTERESIS),
+ .scan_index = CHANNEL_SCAN_INDEX_Y,
+ }, {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_HYSTERESIS),
+ .scan_index = CHANNEL_SCAN_INDEX_Z,
+ }
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void gyro_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+ int channel, int size)
+{
+ channels[channel].scan_type.sign = 's';
+ /* Real storage bits will change based on the report desc. */
+ channels[channel].scan_type.realbits = size * 8;
+ /* Maximum size of a sample to capture is u32 */
+ channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int gyro_3d_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+ int report_id = -1;
+ u32 address;
+ int ret;
+ int ret_type;
+
+ *val = 0;
+ *val2 = 0;
+ switch (mask) {
+ case 0:
+ report_id = gyro_state->gyro[chan->scan_index].report_id;
+ address = gyro_3d_addresses[chan->scan_index];
+ if (report_id >= 0)
+ *val = sensor_hub_input_attr_get_raw_value(
+ gyro_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_GYRO_3D, address,
+ report_id);
+ else {
+ *val = 0;
+ return -EINVAL;
+ }
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = gyro_state->gyro[CHANNEL_SCAN_INDEX_X].units;
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = hid_sensor_convert_exponent(
+ gyro_state->gyro[CHANNEL_SCAN_INDEX_X].unit_expo);
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_read_samp_freq_value(
+ &gyro_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_read_raw_hyst_value(
+ &gyro_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret_type = -EINVAL;
+ break;
+ }
+
+ return ret_type;
+}
+
+/* Channel write_raw handler */
+static int gyro_3d_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_write_samp_freq_value(
+ &gyro_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_write_raw_hyst_value(
+ &gyro_state->common_attributes, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int gyro_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info gyro_3d_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &gyro_3d_read_raw,
+ .write_raw = &gyro_3d_write_raw,
+ .write_raw_get_fmt = &gyro_3d_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ iio_push_to_buffers(indio_dev, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "gyro_3d_proc_event [%d]\n",
+ gyro_state->common_attributes.data_ready);
+ if (gyro_state->common_attributes.data_ready)
+ hid_sensor_push_data(indio_dev,
+ (u8 *)gyro_state->gyro_val,
+ sizeof(gyro_state->gyro_val));
+
+ return 0;
+}
+
+/* Capture samples in local storage */
+static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+ int offset;
+ int ret = -EINVAL;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS:
+ case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS:
+ case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS:
+ offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
+ gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
+ *(u32 *)raw_data;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int gyro_3d_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ unsigned usage_id,
+ struct gyro_3d_state *st)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
+ ret = sensor_hub_input_get_attribute_info(hsdev,
+ HID_INPUT_REPORT,
+ usage_id,
+ HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + i,
+ &st->gyro[CHANNEL_SCAN_INDEX_X + i]);
+ if (ret < 0)
+ break;
+ gyro_3d_adjust_channel_bit_mask(channels,
+ CHANNEL_SCAN_INDEX_X + i,
+ st->gyro[CHANNEL_SCAN_INDEX_X + i].size);
+ }
+ dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n",
+ st->gyro[0].index,
+ st->gyro[0].report_id,
+ st->gyro[1].index, st->gyro[1].report_id,
+ st->gyro[2].index, st->gyro[2].report_id);
+
+ return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int hid_gyro_3d_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ static const char *name = "gyro_3d";
+ struct iio_dev *indio_dev;
+ struct gyro_3d_state *gyro_state;
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_chan_spec *channels;
+
+ indio_dev = iio_device_alloc(sizeof(struct gyro_3d_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+
+ gyro_state = iio_priv(indio_dev);
+ gyro_state->common_attributes.hsdev = hsdev;
+ gyro_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev,
+ HID_USAGE_SENSOR_GYRO_3D,
+ &gyro_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes\n");
+ goto error_free_dev;
+ }
+
+ channels = kmemdup(gyro_3d_channels, sizeof(gyro_3d_channels),
+ GFP_KERNEL);
+ if (!channels) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "failed to duplicate channels\n");
+ goto error_free_dev;
+ }
+
+ ret = gyro_3d_parse_report(pdev, hsdev, channels,
+ HID_USAGE_SENSOR_GYRO_3D, gyro_state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup attributes\n");
+ goto error_free_dev_mem;
+ }
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &gyro_3d_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ NULL, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+ goto error_free_dev_mem;
+ }
+ gyro_state->common_attributes.data_ready = false;
+ ret = hid_sensor_setup_trigger(indio_dev, name,
+ &gyro_state->common_attributes);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "trigger setup failed\n");
+ goto error_unreg_buffer_funcs;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_trigger;
+ }
+
+ gyro_state->callbacks.send_event = gyro_3d_proc_event;
+ gyro_state->callbacks.capture_sample = gyro_3d_capture_sample;
+ gyro_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D,
+ &gyro_state->callbacks);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "callback reg failed\n");
+ goto error_iio_unreg;
+ }
+
+ return ret;
+
+error_iio_unreg:
+ iio_device_unregister(indio_dev);
+error_remove_trigger:
+ hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+ kfree(indio_dev->channels);
+error_free_dev:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int hid_gyro_3d_remove(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
+ iio_device_unregister(indio_dev);
+ hid_sensor_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ kfree(indio_dev->channels);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver hid_gyro_3d_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = hid_gyro_3d_probe,
+ .remove = hid_gyro_3d_remove,
+};
+module_platform_driver(hid_gyro_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Gyroscope 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c
new file mode 100644
index 00000000..14917fae
--- /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 = iio_trigger_get_drvdata(trig);
+ 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;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = iio_trigger_register(st->trig);
+ if (ret)
+ goto error_free_irq;
+
+ /* select default trigger */
+ indio_dev->trig = iio_trigger_get(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 00000000..d66605d2
--- /dev/null
+++ b/drivers/iio/gyro/itg3200_core.c
@@ -0,0 +1,398 @@
+/*
+ * 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_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_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .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_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .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 00000000..25ee236b
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro.h
@@ -0,0 +1,44 @@
+/*
+ * 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 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 00000000..69017c7e
--- /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 = iio_trigger_get_drvdata(trig);
+
+ 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 00000000..1fabc3ff
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_core.c
@@ -0,0 +1,367 @@
+/*
+ * 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.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] = LSM330D_GYRO_DEV_NAME,
+ [2] = LSM330DLC_GYRO_DEV_NAME,
+ [3] = L3G4IS_GYRO_DEV_NAME,
+ [4] = 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,
+};
+
+#ifdef CONFIG_IIO_TRIGGER
+static const struct iio_trigger_ops st_gyro_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = ST_GYRO_TRIGGER_SET_STATE,
+};
+#define ST_GYRO_TRIGGER_OPS (&st_gyro_trigger_ops)
+#else
+#define ST_GYRO_TRIGGER_OPS NULL
+#endif
+
+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 00000000..c1755ce2
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_i2c.c
@@ -0,0 +1,83 @@
+/*
+ * 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/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 },
+ { 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 00000000..b37fc9e0
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_spi.c
@@ -0,0 +1,82 @@
+/*
+ * 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/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 },
+ { 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/iio_core.h b/drivers/iio/iio_core.h
new file mode 100644
index 00000000..05c1b745
--- /dev/null
+++ b/drivers/iio/iio_core.h
@@ -0,0 +1,63 @@
+/* The industrial I/O core function defs.
+ *
+ * Copyright (c) 2008 Jonathan Cameron
+ *
+ * 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.
+ *
+ * These definitions are meant for use only within the IIO core, not individual
+ * drivers.
+ */
+
+#ifndef _IIO_CORE_H_
+#define _IIO_CORE_H_
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+struct iio_chan_spec;
+struct iio_dev;
+
+extern struct device_type iio_device_type;
+
+int __iio_add_chan_devattr(const char *postfix,
+ struct iio_chan_spec const *chan,
+ ssize_t (*func)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*writefunc)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len),
+ u64 mask,
+ bool generic,
+ struct device *dev,
+ struct list_head *attr_list);
+
+/* Event interface flags */
+#define IIO_BUSY_BIT_POS 1
+
+#ifdef CONFIG_IIO_BUFFER
+struct poll_table_struct;
+
+unsigned int iio_buffer_poll(struct file *filp,
+ struct poll_table_struct *wait);
+ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
+ size_t n, loff_t *f_ps);
+
+
+#define iio_buffer_poll_addr (&iio_buffer_poll)
+#define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer)
+
+#else
+
+#define iio_buffer_poll_addr NULL
+#define iio_buffer_read_first_n_outer_addr NULL
+
+#endif
+
+int iio_device_register_eventset(struct iio_dev *indio_dev);
+void iio_device_unregister_eventset(struct iio_dev *indio_dev);
+int iio_event_getfd(struct iio_dev *indio_dev);
+
+#endif
diff --git a/drivers/iio/iio_core_trigger.h b/drivers/iio/iio_core_trigger.h
new file mode 100644
index 00000000..6f7c56fc
--- /dev/null
+++ b/drivers/iio/iio_core_trigger.h
@@ -0,0 +1,46 @@
+
+/* The industrial I/O core, trigger consumer handling functions
+ *
+ * Copyright (c) 2008 Jonathan Cameron
+ *
+ * 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.
+ */
+
+#ifdef CONFIG_IIO_TRIGGER
+/**
+ * iio_device_register_trigger_consumer() - set up an iio_dev to use triggers
+ * @indio_dev: iio_dev associated with the device that will consume the trigger
+ **/
+void iio_device_register_trigger_consumer(struct iio_dev *indio_dev);
+
+/**
+ * iio_device_unregister_trigger_consumer() - reverse the registration process
+ * @indio_dev: iio_dev associated with the device that consumed the trigger
+ **/
+void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev);
+
+#else
+
+/**
+ * iio_device_register_trigger_consumer() - set up an iio_dev to use triggers
+ * @indio_dev: iio_dev associated with the device that will consume the trigger
+ **/
+static int iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
+{
+ return 0;
+};
+
+/**
+ * iio_device_unregister_trigger_consumer() - reverse the registration process
+ * @indio_dev: iio_dev associated with the device that consumed the trigger
+ **/
+static void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev)
+{
+};
+
+#endif /* CONFIG_TRIGGER_CONSUMER */
+
+
+
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
new file mode 100644
index 00000000..6d12fd10
--- /dev/null
+++ b/drivers/iio/imu/Kconfig
@@ -0,0 +1,5 @@
+
+source "drivers/staging/iio/imu/inv_mpu/Kconfig"
+
+
+
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
new file mode 100644
index 00000000..4814d514
--- /dev/null
+++ b/drivers/iio/imu/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the industrial I/O core.
+#
+
+obj-y += inv_mpu/
+
diff --git a/drivers/iio/imu/adis16400.h b/drivers/iio/imu/adis16400.h
new file mode 100644
index 00000000..0916bf6b
--- /dev/null
+++ b/drivers/iio/imu/adis16400.h
@@ -0,0 +1,213 @@
+/*
+ * adis16400.h support Analog Devices ADIS16400
+ * 3d 18g accelerometers,
+ * 3d gyroscopes,
+ * 3d 2.5gauss magnetometers via SPI
+ *
+ * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ *
+ * Loosely based upon lis3l02dq.h
+ *
+ * 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 SPI_ADIS16400_H_
+#define SPI_ADIS16400_H_
+
+#include <linux/iio/imu/adis.h>
+
+#define ADIS16400_STARTUP_DELAY 290 /* ms */
+#define ADIS16400_MTEST_DELAY 90 /* ms */
+
+#define ADIS16400_FLASH_CNT 0x00 /* Flash memory write count */
+#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */
+#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */
+#define ADIS16400_YGYRO_OUT 0x06 /* Y-axis gyroscope output */
+#define ADIS16400_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */
+#define ADIS16400_XACCL_OUT 0x0A /* X-axis accelerometer output */
+#define ADIS16400_YACCL_OUT 0x0C /* Y-axis accelerometer output */
+#define ADIS16400_ZACCL_OUT 0x0E /* Z-axis accelerometer output */
+#define ADIS16400_XMAGN_OUT 0x10 /* X-axis magnetometer measurement */
+#define ADIS16400_YMAGN_OUT 0x12 /* Y-axis magnetometer measurement */
+#define ADIS16400_ZMAGN_OUT 0x14 /* Z-axis magnetometer measurement */
+#define ADIS16400_TEMP_OUT 0x16 /* Temperature output */
+#define ADIS16400_AUX_ADC 0x18 /* Auxiliary ADC measurement */
+
+#define ADIS16350_XTEMP_OUT 0x10 /* X-axis gyroscope temperature measurement */
+#define ADIS16350_YTEMP_OUT 0x12 /* Y-axis gyroscope temperature measurement */
+#define ADIS16350_ZTEMP_OUT 0x14 /* Z-axis gyroscope temperature measurement */
+
+#define ADIS16300_PITCH_OUT 0x12 /* X axis inclinometer output measurement */
+#define ADIS16300_ROLL_OUT 0x14 /* Y axis inclinometer output measurement */
+#define ADIS16300_AUX_ADC 0x16 /* Auxiliary ADC measurement */
+
+#define ADIS16448_BARO_OUT 0x16 /* Barometric pressure output */
+#define ADIS16448_TEMP_OUT 0x18 /* Temperature output */
+
+/* Calibration parameters */
+#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */
+#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */
+#define ADIS16400_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */
+#define ADIS16400_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */
+#define ADIS16400_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */
+#define ADIS16400_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */
+#define ADIS16400_XMAGN_HIF 0x26 /* X-axis magnetometer, hard-iron factor */
+#define ADIS16400_YMAGN_HIF 0x28 /* Y-axis magnetometer, hard-iron factor */
+#define ADIS16400_ZMAGN_HIF 0x2A /* Z-axis magnetometer, hard-iron factor */
+#define ADIS16400_XMAGN_SIF 0x2C /* X-axis magnetometer, soft-iron factor */
+#define ADIS16400_YMAGN_SIF 0x2E /* Y-axis magnetometer, soft-iron factor */
+#define ADIS16400_ZMAGN_SIF 0x30 /* Z-axis magnetometer, soft-iron factor */
+
+#define ADIS16400_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */
+#define ADIS16400_MSC_CTRL 0x34 /* Miscellaneous control */
+#define ADIS16400_SMPL_PRD 0x36 /* Internal sample period (rate) control */
+#define ADIS16400_SENS_AVG 0x38 /* Dynamic range and digital filter control */
+#define ADIS16400_SLP_CNT 0x3A /* Sleep mode control */
+#define ADIS16400_DIAG_STAT 0x3C /* System status */
+
+/* Alarm functions */
+#define ADIS16400_GLOB_CMD 0x3E /* System command */
+#define ADIS16400_ALM_MAG1 0x40 /* Alarm 1 amplitude threshold */
+#define ADIS16400_ALM_MAG2 0x42 /* Alarm 2 amplitude threshold */
+#define ADIS16400_ALM_SMPL1 0x44 /* Alarm 1 sample size */
+#define ADIS16400_ALM_SMPL2 0x46 /* Alarm 2 sample size */
+#define ADIS16400_ALM_CTRL 0x48 /* Alarm control */
+#define ADIS16400_AUX_DAC 0x4A /* Auxiliary DAC data */
+
+#define ADIS16334_LOT_ID1 0x52 /* Lot identification code 1 */
+#define ADIS16334_LOT_ID2 0x54 /* Lot identification code 2 */
+#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */
+#define ADIS16334_SERIAL_NUMBER 0x58 /* Serial number, lot specific */
+
+#define ADIS16400_ERROR_ACTIVE (1<<14)
+#define ADIS16400_NEW_DATA (1<<14)
+
+/* MSC_CTRL */
+#define ADIS16400_MSC_CTRL_MEM_TEST (1<<11)
+#define ADIS16400_MSC_CTRL_INT_SELF_TEST (1<<10)
+#define ADIS16400_MSC_CTRL_NEG_SELF_TEST (1<<9)
+#define ADIS16400_MSC_CTRL_POS_SELF_TEST (1<<8)
+#define ADIS16400_MSC_CTRL_GYRO_BIAS (1<<7)
+#define ADIS16400_MSC_CTRL_ACCL_ALIGN (1<<6)
+#define ADIS16400_MSC_CTRL_DATA_RDY_EN (1<<2)
+#define ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1)
+#define ADIS16400_MSC_CTRL_DATA_RDY_DIO2 (1<<0)
+
+/* SMPL_PRD */
+#define ADIS16400_SMPL_PRD_TIME_BASE (1<<7)
+#define ADIS16400_SMPL_PRD_DIV_MASK 0x7F
+
+/* DIAG_STAT */
+#define ADIS16400_DIAG_STAT_ZACCL_FAIL 15
+#define ADIS16400_DIAG_STAT_YACCL_FAIL 14
+#define ADIS16400_DIAG_STAT_XACCL_FAIL 13
+#define ADIS16400_DIAG_STAT_XGYRO_FAIL 12
+#define ADIS16400_DIAG_STAT_YGYRO_FAIL 11
+#define ADIS16400_DIAG_STAT_ZGYRO_FAIL 10
+#define ADIS16400_DIAG_STAT_ALARM2 9
+#define ADIS16400_DIAG_STAT_ALARM1 8
+#define ADIS16400_DIAG_STAT_FLASH_CHK 6
+#define ADIS16400_DIAG_STAT_SELF_TEST 5
+#define ADIS16400_DIAG_STAT_OVERFLOW 4
+#define ADIS16400_DIAG_STAT_SPI_FAIL 3
+#define ADIS16400_DIAG_STAT_FLASH_UPT 2
+#define ADIS16400_DIAG_STAT_POWER_HIGH 1
+#define ADIS16400_DIAG_STAT_POWER_LOW 0
+
+/* GLOB_CMD */
+#define ADIS16400_GLOB_CMD_SW_RESET (1<<7)
+#define ADIS16400_GLOB_CMD_P_AUTO_NULL (1<<4)
+#define ADIS16400_GLOB_CMD_FLASH_UPD (1<<3)
+#define ADIS16400_GLOB_CMD_DAC_LATCH (1<<2)
+#define ADIS16400_GLOB_CMD_FAC_CALIB (1<<1)
+#define ADIS16400_GLOB_CMD_AUTO_NULL (1<<0)
+
+/* SLP_CNT */
+#define ADIS16400_SLP_CNT_POWER_OFF (1<<8)
+
+#define ADIS16334_RATE_DIV_SHIFT 8
+#define ADIS16334_RATE_INT_CLK BIT(0)
+
+#define ADIS16400_SPI_SLOW (u32)(300 * 1000)
+#define ADIS16400_SPI_BURST (u32)(1000 * 1000)
+#define ADIS16400_SPI_FAST (u32)(2000 * 1000)
+
+#define ADIS16400_HAS_PROD_ID BIT(0)
+#define ADIS16400_NO_BURST BIT(1)
+#define ADIS16400_HAS_SLOW_MODE BIT(2)
+#define ADIS16400_HAS_SERIAL_NUMBER BIT(3)
+
+struct adis16400_state;
+
+struct adis16400_chip_info {
+ const struct iio_chan_spec *channels;
+ const int num_channels;
+ const long flags;
+ unsigned int gyro_scale_micro;
+ unsigned int accel_scale_micro;
+ int temp_scale_nano;
+ int temp_offset;
+ int (*set_freq)(struct adis16400_state *st, unsigned int freq);
+ int (*get_freq)(struct adis16400_state *st);
+};
+
+/**
+ * struct adis16400_state - device instance specific data
+ * @variant: chip variant info
+ * @filt_int: integer part of requested filter frequency
+ * @adis: adis device
+ **/
+struct adis16400_state {
+ struct adis16400_chip_info *variant;
+ int filt_int;
+
+ struct adis adis;
+};
+
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum {
+ ADIS16400_SCAN_SUPPLY,
+ ADIS16400_SCAN_GYRO_X,
+ ADIS16400_SCAN_GYRO_Y,
+ ADIS16400_SCAN_GYRO_Z,
+ ADIS16400_SCAN_ACC_X,
+ ADIS16400_SCAN_ACC_Y,
+ ADIS16400_SCAN_ACC_Z,
+ ADIS16400_SCAN_MAGN_X,
+ ADIS16400_SCAN_MAGN_Y,
+ ADIS16400_SCAN_MAGN_Z,
+ ADIS16400_SCAN_BARO,
+ ADIS16350_SCAN_TEMP_X,
+ ADIS16350_SCAN_TEMP_Y,
+ ADIS16350_SCAN_TEMP_Z,
+ ADIS16300_SCAN_INCLI_X,
+ ADIS16300_SCAN_INCLI_Y,
+ ADIS16400_SCAN_ADC,
+ ADIS16400_SCAN_TIMESTAMP,
+};
+
+#ifdef CONFIG_IIO_BUFFER
+
+ssize_t adis16400_read_data_from_ring(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+
+int adis16400_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask);
+irqreturn_t adis16400_trigger_handler(int irq, void *p);
+
+#else /* CONFIG_IIO_BUFFER */
+
+#define adis16400_update_scan_mode NULL
+#define adis16400_trigger_handler NULL
+
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* SPI_ADIS16400_H_ */
diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c
new file mode 100644
index 00000000..d6ece2d1
--- /dev/null
+++ b/drivers/iio/imu/adis16400_core.c
@@ -0,0 +1,971 @@
+/*
+ * adis16400.c support Analog Devices ADIS16400/5
+ * 3d 2g Linear Accelerometers,
+ * 3d Gyroscopes,
+ * 3d Magnetometers via SPI
+ *
+ * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * 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/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+
+#include "adis16400.h"
+
+#ifdef CONFIG_DEBUG_FS
+
+static ssize_t adis16400_show_serial_number(struct file *file,
+ char __user *userbuf, size_t count, loff_t *ppos)
+{
+ struct adis16400_state *st = file->private_data;
+ u16 lot1, lot2, serial_number;
+ char buf[16];
+ size_t len;
+ int ret;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID1, &lot1);
+ if (ret < 0)
+ return ret;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID2, &lot2);
+ if (ret < 0)
+ return ret;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16334_SERIAL_NUMBER,
+ &serial_number);
+ if (ret < 0)
+ return ret;
+
+ len = snprintf(buf, sizeof(buf), "%.4x-%.4x-%.4x\n", lot1, lot2,
+ serial_number);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static const struct file_operations adis16400_serial_number_fops = {
+ .open = simple_open,
+ .read = adis16400_show_serial_number,
+ .llseek = default_llseek,
+ .owner = THIS_MODULE,
+};
+
+static int adis16400_show_product_id(void *arg, u64 *val)
+{
+ struct adis16400_state *st = arg;
+ uint16_t prod_id;
+ int ret;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16400_PRODUCT_ID, &prod_id);
+ if (ret < 0)
+ return ret;
+
+ *val = prod_id;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16400_product_id_fops,
+ adis16400_show_product_id, NULL, "%lld\n");
+
+static int adis16400_show_flash_count(void *arg, u64 *val)
+{
+ struct adis16400_state *st = arg;
+ uint16_t flash_count;
+ int ret;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16400_FLASH_CNT, &flash_count);
+ if (ret < 0)
+ return ret;
+
+ *val = flash_count;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16400_flash_count_fops,
+ adis16400_show_flash_count, NULL, "%lld\n");
+
+static int adis16400_debugfs_init(struct iio_dev *indio_dev)
+{
+ struct adis16400_state *st = iio_priv(indio_dev);
+
+ if (st->variant->flags & ADIS16400_HAS_SERIAL_NUMBER)
+ debugfs_create_file("serial_number", 0400,
+ indio_dev->debugfs_dentry, st,
+ &adis16400_serial_number_fops);
+ if (st->variant->flags & ADIS16400_HAS_PROD_ID)
+ debugfs_create_file("product_id", 0400,
+ indio_dev->debugfs_dentry, st,
+ &adis16400_product_id_fops);
+ debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
+ st, &adis16400_flash_count_fops);
+
+ return 0;
+}
+
+#else
+
+static int adis16400_debugfs_init(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+#endif
+
+enum adis16400_chip_variant {
+ ADIS16300,
+ ADIS16334,
+ ADIS16350,
+ ADIS16360,
+ ADIS16362,
+ ADIS16364,
+ ADIS16400,
+ ADIS16448,
+};
+
+static int adis16334_get_freq(struct adis16400_state *st)
+{
+ int ret;
+ uint16_t t;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
+ if (ret < 0)
+ return ret;
+
+ t >>= ADIS16334_RATE_DIV_SHIFT;
+
+ return 819200 >> t;
+}
+
+static int adis16334_set_freq(struct adis16400_state *st, unsigned int freq)
+{
+ unsigned int t;
+
+ if (freq < 819200)
+ t = ilog2(819200 / freq);
+ else
+ t = 0;
+
+ if (t > 0x31)
+ t = 0x31;
+
+ t <<= ADIS16334_RATE_DIV_SHIFT;
+ t |= ADIS16334_RATE_INT_CLK;
+
+ return adis_write_reg_16(&st->adis, ADIS16400_SMPL_PRD, t);
+}
+
+static int adis16400_get_freq(struct adis16400_state *st)
+{
+ int sps, ret;
+ uint16_t t;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
+ if (ret < 0)
+ return ret;
+
+ sps = (t & ADIS16400_SMPL_PRD_TIME_BASE) ? 52851 : 1638404;
+ sps /= (t & ADIS16400_SMPL_PRD_DIV_MASK) + 1;
+
+ return sps;
+}
+
+static int adis16400_set_freq(struct adis16400_state *st, unsigned int freq)
+{
+ unsigned int t;
+ uint8_t val = 0;
+
+ t = 1638404 / freq;
+ if (t >= 128) {
+ val |= ADIS16400_SMPL_PRD_TIME_BASE;
+ t = 52851 / freq;
+ if (t >= 128)
+ t = 127;
+ } else if (t != 0) {
+ t--;
+ }
+
+ val |= t;
+
+ if (t >= 0x0A || (val & ADIS16400_SMPL_PRD_TIME_BASE))
+ st->adis.spi->max_speed_hz = ADIS16400_SPI_SLOW;
+ else
+ st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
+
+ return adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val);
+}
+
+static ssize_t adis16400_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct adis16400_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = st->variant->get_freq(st);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d.%.3d\n", ret / 1000, ret % 1000);
+}
+
+static const unsigned adis16400_3db_divisors[] = {
+ [0] = 2, /* Special case */
+ [1] = 6,
+ [2] = 12,
+ [3] = 25,
+ [4] = 50,
+ [5] = 100,
+ [6] = 200,
+ [7] = 200, /* Not a valid setting */
+};
+
+static int adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val)
+{
+ struct adis16400_state *st = iio_priv(indio_dev);
+ uint16_t val16;
+ int i, ret;
+
+ for (i = ARRAY_SIZE(adis16400_3db_divisors) - 1; i >= 1; i--) {
+ if (sps / adis16400_3db_divisors[i] >= val)
+ break;
+ }
+
+ ret = adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16);
+ if (ret < 0)
+ return ret;
+
+ ret = adis_write_reg_16(&st->adis, ADIS16400_SENS_AVG,
+ (val16 & ~0x07) | i);
+ return ret;
+}
+
+static ssize_t adis16400_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);
+ struct adis16400_state *st = iio_priv(indio_dev);
+ int i, f, val;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100, &i, &f);
+ if (ret)
+ return ret;
+
+ val = i * 1000 + f;
+
+ if (val <= 0)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ st->variant->set_freq(st, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+/* Power down the device */
+static int adis16400_stop_device(struct iio_dev *indio_dev)
+{
+ struct adis16400_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = adis_write_reg_16(&st->adis, ADIS16400_SLP_CNT,
+ ADIS16400_SLP_CNT_POWER_OFF);
+ if (ret)
+ dev_err(&indio_dev->dev,
+ "problem with turning device off: SLP_CNT");
+
+ return ret;
+}
+
+static int adis16400_initial_setup(struct iio_dev *indio_dev)
+{
+ struct adis16400_state *st = iio_priv(indio_dev);
+ uint16_t prod_id, smp_prd;
+ unsigned int device_id;
+ int ret;
+
+ /* use low spi speed for init if the device has a slow mode */
+ if (st->variant->flags & ADIS16400_HAS_SLOW_MODE)
+ st->adis.spi->max_speed_hz = ADIS16400_SPI_SLOW;
+ else
+ st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
+ st->adis.spi->mode = SPI_MODE_3;
+ spi_setup(st->adis.spi);
+
+ ret = adis_initial_startup(&st->adis);
+ if (ret)
+ return ret;
+
+ if (st->variant->flags & ADIS16400_HAS_PROD_ID) {
+ ret = adis_read_reg_16(&st->adis,
+ ADIS16400_PRODUCT_ID, &prod_id);
+ if (ret)
+ goto err_ret;
+
+ sscanf(indio_dev->name, "adis%u\n", &device_id);
+
+ if (prod_id != device_id)
+ dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
+ device_id, prod_id);
+
+ dev_info(&indio_dev->dev, "%s: prod_id 0x%04x at CS%d (irq %d)\n",
+ indio_dev->name, prod_id,
+ st->adis.spi->chip_select, st->adis.spi->irq);
+ }
+ /* use high spi speed if possible */
+ if (st->variant->flags & ADIS16400_HAS_SLOW_MODE) {
+ ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &smp_prd);
+ if (ret)
+ goto err_ret;
+
+ if ((smp_prd & ADIS16400_SMPL_PRD_DIV_MASK) < 0x0A) {
+ st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
+ spi_setup(st->adis.spi);
+ }
+ }
+
+err_ret:
+ return ret;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ adis16400_read_frequency,
+ adis16400_write_frequency);
+
+static const uint8_t adis16400_addresses[] = {
+ [ADIS16400_SCAN_GYRO_X] = ADIS16400_XGYRO_OFF,
+ [ADIS16400_SCAN_GYRO_Y] = ADIS16400_YGYRO_OFF,
+ [ADIS16400_SCAN_GYRO_Z] = ADIS16400_ZGYRO_OFF,
+ [ADIS16400_SCAN_ACC_X] = ADIS16400_XACCL_OFF,
+ [ADIS16400_SCAN_ACC_Y] = ADIS16400_YACCL_OFF,
+ [ADIS16400_SCAN_ACC_Z] = ADIS16400_ZACCL_OFF,
+};
+
+static int adis16400_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+ struct adis16400_state *st = iio_priv(indio_dev);
+ int ret, sps;
+
+ switch (info) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ mutex_lock(&indio_dev->mlock);
+ ret = adis_write_reg_16(&st->adis,
+ adis16400_addresses[chan->scan_index], val);
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ /*
+ * Need to cache values so we can update if the frequency
+ * changes.
+ */
+ mutex_lock(&indio_dev->mlock);
+ st->filt_int = val;
+ /* Work out update to current value */
+ sps = st->variant->get_freq(st);
+ if (sps < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return sps;
+ }
+
+ ret = adis16400_set_filter(indio_dev, sps,
+ val * 1000 + val2 / 1000);
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adis16400_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct adis16400_state *st = iio_priv(indio_dev);
+ int16_t val16;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan, 0, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val = 0;
+ *val2 = st->variant->gyro_scale_micro;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_VOLTAGE:
+ *val = 0;
+ if (chan->channel == 0) {
+ *val = 2;
+ *val2 = 418000; /* 2.418 mV */
+ } else {
+ *val = 0;
+ *val2 = 805800; /* 805.8 uV */
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = st->variant->accel_scale_micro;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_MAGN:
+ *val = 0;
+ *val2 = 500; /* 0.5 mgauss */
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = st->variant->temp_scale_nano / 1000000;
+ *val2 = (st->variant->temp_scale_nano % 1000000);
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ mutex_lock(&indio_dev->mlock);
+ ret = adis_read_reg_16(&st->adis,
+ adis16400_addresses[chan->scan_index], &val16);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret)
+ return ret;
+ val16 = ((val16 & 0xFFF) << 4) >> 4;
+ *val = val16;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ /* currently only temperature */
+ *val = st->variant->temp_offset;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ mutex_lock(&indio_dev->mlock);
+ /* Need both the number of taps and the sampling frequency */
+ ret = adis_read_reg_16(&st->adis,
+ ADIS16400_SENS_AVG,
+ &val16);
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ ret = st->variant->get_freq(st);
+ if (ret >= 0) {
+ ret /= adis16400_3db_divisors[val16 & 0x07];
+ *val = ret / 1000;
+ *val2 = (ret % 1000) * 1000;
+ }
+ mutex_unlock(&indio_dev->mlock);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+#define ADIS16400_VOLTAGE_CHAN(addr, bits, name, si) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = 0, \
+ .extend_name = name, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (addr), \
+ .scan_index = (si), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define ADIS16400_SUPPLY_CHAN(addr, bits) \
+ ADIS16400_VOLTAGE_CHAN(addr, bits, "supply", ADIS16400_SCAN_SUPPLY)
+
+#define ADIS16400_AUX_ADC_CHAN(addr, bits) \
+ ADIS16400_VOLTAGE_CHAN(addr, bits, NULL, ADIS16400_SCAN_ADC)
+
+#define ADIS16400_GYRO_CHAN(mod, addr, bits) { \
+ .type = IIO_ANGL_VEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .address = addr, \
+ .scan_index = ADIS16400_SCAN_GYRO_ ## mod, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define ADIS16400_ACCEL_CHAN(mod, addr, bits) { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .address = (addr), \
+ .scan_index = ADIS16400_SCAN_ACC_ ## mod, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define ADIS16400_MAGN_CHAN(mod, addr, bits) { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .address = (addr), \
+ .scan_index = ADIS16400_SCAN_MAGN_ ## mod, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define ADIS16400_MOD_TEMP_NAME_X "x"
+#define ADIS16400_MOD_TEMP_NAME_Y "y"
+#define ADIS16400_MOD_TEMP_NAME_Z "z"
+
+#define ADIS16400_MOD_TEMP_CHAN(mod, addr, bits) { \
+ .type = IIO_TEMP, \
+ .indexed = 1, \
+ .channel = 0, \
+ .extend_name = ADIS16400_MOD_TEMP_NAME_ ## mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .address = (addr), \
+ .scan_index = ADIS16350_SCAN_TEMP_ ## mod, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define ADIS16400_TEMP_CHAN(addr, bits) { \
+ .type = IIO_TEMP, \
+ .indexed = 1, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (addr), \
+ .scan_index = ADIS16350_SCAN_TEMP_X, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define ADIS16400_INCLI_CHAN(mod, addr, bits) { \
+ .type = IIO_INCLI, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (addr), \
+ .scan_index = ADIS16300_SCAN_INCLI_ ## mod, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec adis16400_channels[] = {
+ ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 14),
+ ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
+ ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
+ ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
+ ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
+ ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
+ ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
+ ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 14),
+ ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 14),
+ ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 14),
+ ADIS16400_TEMP_CHAN(ADIS16400_TEMP_OUT, 12),
+ ADIS16400_AUX_ADC_CHAN(ADIS16400_AUX_ADC, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec adis16448_channels[] = {
+ ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 16),
+ ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 16),
+ ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 16),
+ ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 16),
+ ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 16),
+ ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 16),
+ ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 16),
+ ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 16),
+ ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 16),
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .address = ADIS16448_BARO_OUT,
+ .scan_index = ADIS16400_SCAN_BARO,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ ADIS16400_TEMP_CHAN(ADIS16448_TEMP_OUT, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec adis16350_channels[] = {
+ ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 12),
+ ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
+ ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
+ ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
+ ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
+ ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
+ ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
+ ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 14),
+ ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 14),
+ ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 14),
+ ADIS16400_AUX_ADC_CHAN(ADIS16300_AUX_ADC, 12),
+ ADIS16400_MOD_TEMP_CHAN(X, ADIS16350_XTEMP_OUT, 12),
+ ADIS16400_MOD_TEMP_CHAN(Y, ADIS16350_YTEMP_OUT, 12),
+ ADIS16400_MOD_TEMP_CHAN(Z, ADIS16350_ZTEMP_OUT, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec adis16300_channels[] = {
+ ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 12),
+ ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
+ ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
+ ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
+ ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
+ ADIS16400_TEMP_CHAN(ADIS16350_XTEMP_OUT, 12),
+ ADIS16400_AUX_ADC_CHAN(ADIS16300_AUX_ADC, 12),
+ ADIS16400_INCLI_CHAN(X, ADIS16300_PITCH_OUT, 13),
+ ADIS16400_INCLI_CHAN(Y, ADIS16300_ROLL_OUT, 13),
+ IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec adis16334_channels[] = {
+ ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
+ ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
+ ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
+ ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
+ ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
+ ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
+ ADIS16400_TEMP_CHAN(ADIS16350_XTEMP_OUT, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static struct attribute *adis16400_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adis16400_attribute_group = {
+ .attrs = adis16400_attributes,
+};
+
+static struct adis16400_chip_info adis16400_chips[] = {
+ [ADIS16300] = {
+ .channels = adis16300_channels,
+ .num_channels = ARRAY_SIZE(adis16300_channels),
+ .flags = ADIS16400_HAS_SLOW_MODE,
+ .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+ .accel_scale_micro = 5884,
+ .temp_scale_nano = 140000000, /* 0.14 C */
+ .temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
+ .set_freq = adis16400_set_freq,
+ .get_freq = adis16400_get_freq,
+ },
+ [ADIS16334] = {
+ .channels = adis16334_channels,
+ .num_channels = ARRAY_SIZE(adis16334_channels),
+ .flags = ADIS16400_HAS_PROD_ID | ADIS16400_NO_BURST |
+ ADIS16400_HAS_SERIAL_NUMBER,
+ .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+ .accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */
+ .temp_scale_nano = 67850000, /* 0.06785 C */
+ .temp_offset = 25000000 / 67850, /* 25 C = 0x00 */
+ .set_freq = adis16334_set_freq,
+ .get_freq = adis16334_get_freq,
+ },
+ [ADIS16350] = {
+ .channels = adis16350_channels,
+ .num_channels = ARRAY_SIZE(adis16350_channels),
+ .gyro_scale_micro = IIO_DEGREE_TO_RAD(73260), /* 0.07326 deg/s */
+ .accel_scale_micro = IIO_G_TO_M_S_2(2522), /* 0.002522 g */
+ .temp_scale_nano = 145300000, /* 0.1453 C */
+ .temp_offset = 25000000 / 145300, /* 25 C = 0x00 */
+ .flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE,
+ .set_freq = adis16400_set_freq,
+ .get_freq = adis16400_get_freq,
+ },
+ [ADIS16360] = {
+ .channels = adis16350_channels,
+ .num_channels = ARRAY_SIZE(adis16350_channels),
+ .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
+ ADIS16400_HAS_SERIAL_NUMBER,
+ .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+ .accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */
+ .temp_scale_nano = 136000000, /* 0.136 C */
+ .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
+ .set_freq = adis16400_set_freq,
+ .get_freq = adis16400_get_freq,
+ },
+ [ADIS16362] = {
+ .channels = adis16350_channels,
+ .num_channels = ARRAY_SIZE(adis16350_channels),
+ .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
+ ADIS16400_HAS_SERIAL_NUMBER,
+ .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+ .accel_scale_micro = IIO_G_TO_M_S_2(333), /* 0.333 mg */
+ .temp_scale_nano = 136000000, /* 0.136 C */
+ .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
+ .set_freq = adis16400_set_freq,
+ .get_freq = adis16400_get_freq,
+ },
+ [ADIS16364] = {
+ .channels = adis16350_channels,
+ .num_channels = ARRAY_SIZE(adis16350_channels),
+ .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
+ ADIS16400_HAS_SERIAL_NUMBER,
+ .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+ .accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */
+ .temp_scale_nano = 136000000, /* 0.136 C */
+ .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
+ .set_freq = adis16400_set_freq,
+ .get_freq = adis16400_get_freq,
+ },
+ [ADIS16400] = {
+ .channels = adis16400_channels,
+ .num_channels = ARRAY_SIZE(adis16400_channels),
+ .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE,
+ .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+ .accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */
+ .temp_scale_nano = 140000000, /* 0.14 C */
+ .temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
+ .set_freq = adis16400_set_freq,
+ .get_freq = adis16400_get_freq,
+ },
+ [ADIS16448] = {
+ .channels = adis16448_channels,
+ .num_channels = ARRAY_SIZE(adis16448_channels),
+ .flags = ADIS16400_HAS_PROD_ID |
+ ADIS16400_HAS_SERIAL_NUMBER,
+ .gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */
+ .accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */
+ .temp_scale_nano = 73860000, /* 0.07386 C */
+ .temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
+ .set_freq = adis16334_set_freq,
+ .get_freq = adis16334_get_freq,
+ }
+};
+
+static const struct iio_info adis16400_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &adis16400_read_raw,
+ .write_raw = &adis16400_write_raw,
+ .attrs = &adis16400_attribute_group,
+ .update_scan_mode = adis16400_update_scan_mode,
+ .debugfs_reg_access = adis_debugfs_reg_access,
+};
+
+static const unsigned long adis16400_burst_scan_mask[] = {
+ ~0UL,
+ 0,
+};
+
+static const char * const adis16400_status_error_msgs[] = {
+ [ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
+ [ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
+ [ADIS16400_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
+ [ADIS16400_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
+ [ADIS16400_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
+ [ADIS16400_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
+ [ADIS16400_DIAG_STAT_ALARM2] = "Alarm 2 active",
+ [ADIS16400_DIAG_STAT_ALARM1] = "Alarm 1 active",
+ [ADIS16400_DIAG_STAT_FLASH_CHK] = "Flash checksum error",
+ [ADIS16400_DIAG_STAT_SELF_TEST] = "Self test error",
+ [ADIS16400_DIAG_STAT_OVERFLOW] = "Sensor overrange",
+ [ADIS16400_DIAG_STAT_SPI_FAIL] = "SPI failure",
+ [ADIS16400_DIAG_STAT_FLASH_UPT] = "Flash update failed",
+ [ADIS16400_DIAG_STAT_POWER_HIGH] = "Power supply above 5.25V",
+ [ADIS16400_DIAG_STAT_POWER_LOW] = "Power supply below 4.75V",
+};
+
+static const struct adis_data adis16400_data = {
+ .msc_ctrl_reg = ADIS16400_MSC_CTRL,
+ .glob_cmd_reg = ADIS16400_GLOB_CMD,
+ .diag_stat_reg = ADIS16400_DIAG_STAT,
+
+ .read_delay = 50,
+ .write_delay = 50,
+
+ .self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST,
+ .startup_delay = ADIS16400_STARTUP_DELAY,
+
+ .status_error_msgs = adis16400_status_error_msgs,
+ .status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) |
+ BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) |
+ BIT(ADIS16400_DIAG_STAT_XACCL_FAIL) |
+ BIT(ADIS16400_DIAG_STAT_XGYRO_FAIL) |
+ BIT(ADIS16400_DIAG_STAT_YGYRO_FAIL) |
+ BIT(ADIS16400_DIAG_STAT_ZGYRO_FAIL) |
+ BIT(ADIS16400_DIAG_STAT_ALARM2) |
+ BIT(ADIS16400_DIAG_STAT_ALARM1) |
+ BIT(ADIS16400_DIAG_STAT_FLASH_CHK) |
+ BIT(ADIS16400_DIAG_STAT_SELF_TEST) |
+ BIT(ADIS16400_DIAG_STAT_OVERFLOW) |
+ BIT(ADIS16400_DIAG_STAT_SPI_FAIL) |
+ BIT(ADIS16400_DIAG_STAT_FLASH_UPT) |
+ BIT(ADIS16400_DIAG_STAT_POWER_HIGH) |
+ BIT(ADIS16400_DIAG_STAT_POWER_LOW),
+};
+
+static int adis16400_probe(struct spi_device *spi)
+{
+ struct adis16400_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ /* setup the industrialio driver allocated elements */
+ st->variant = &adis16400_chips[spi_get_device_id(spi)->driver_data];
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->channels = st->variant->channels;
+ indio_dev->num_channels = st->variant->num_channels;
+ indio_dev->info = &adis16400_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (!(st->variant->flags & ADIS16400_NO_BURST))
+ indio_dev->available_scan_masks = adis16400_burst_scan_mask;
+
+ ret = adis_init(&st->adis, indio_dev, spi, &adis16400_data);
+ if (ret)
+ goto error_free_dev;
+
+ ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev,
+ adis16400_trigger_handler);
+ if (ret)
+ goto error_free_dev;
+
+ /* Get the device into a sane initial state */
+ ret = adis16400_initial_setup(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer;
+
+ adis16400_debugfs_init(indio_dev);
+ return 0;
+
+error_cleanup_buffer:
+ adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+error_free_dev:
+ iio_device_free(indio_dev);
+ return ret;
+}
+
+static int adis16400_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis16400_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis16400_stop_device(indio_dev);
+
+ adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id adis16400_id[] = {
+ {"adis16300", ADIS16300},
+ {"adis16334", ADIS16334},
+ {"adis16350", ADIS16350},
+ {"adis16354", ADIS16350},
+ {"adis16355", ADIS16350},
+ {"adis16360", ADIS16360},
+ {"adis16362", ADIS16362},
+ {"adis16364", ADIS16364},
+ {"adis16365", ADIS16360},
+ {"adis16400", ADIS16400},
+ {"adis16405", ADIS16400},
+ {"adis16448", ADIS16448},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adis16400_id);
+
+static struct spi_driver adis16400_driver = {
+ .driver = {
+ .name = "adis16400",
+ .owner = THIS_MODULE,
+ },
+ .id_table = adis16400_id,
+ .probe = adis16400_probe,
+ .remove = adis16400_remove,
+};
+module_spi_driver(adis16400_driver);
+
+MODULE_AUTHOR("Manuel Stahl <manuel.stahl@iis.fraunhofer.de>");
+MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/inv_mpu/Kconfig b/drivers/iio/imu/inv_mpu/Kconfig
new file mode 100644
index 00000000..21cc237c
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/Kconfig
@@ -0,0 +1,24 @@
+#
+# inv-mpu-iio driver for Invensense MPU devices and combos
+#
+
+config INV_MPU_IIO
+ tristate "Invensense MPU devices"
+ depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && IIO_TRIGGER && !INV_MPU
+ default n
+ help
+ This driver supports the Invensense MPU devices.
+ This includes MPU6050/MPU3050/MPU9150/ITG3500/MPU6500/MPU9250.
+ This driver can be built as a module. The module will be called
+ inv-mpu-iio.
+
+config INV_IIO_MPU3050_ACCEL_SLAVE_BMA250
+ bool "Invensense MPU3050 slave accelerometer device for bma250"
+ depends on INV_MPU_IIO
+ default n
+ help
+ This is slave device enable MPU3050 accelerometer slave device.
+ Right now, it is only bma250. For other acceleromter device,
+ it can be added to this menu if the proper interface is filled.
+ There are some interface function to be defined.
+
diff --git a/drivers/iio/imu/inv_mpu/Makefile b/drivers/iio/imu/inv_mpu/Makefile
new file mode 100644
index 00000000..3bbb2699
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/Makefile
@@ -0,0 +1,27 @@
+#
+# Makefile for Invensense inv-mpu-iio device.
+#
+
+obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o
+
+inv-mpu-iio-objs := inv_mpu_core.o
+inv-mpu-iio-objs += inv_mpu_ring.o
+inv-mpu-iio-objs += inv_mpu_trigger.o
+inv-mpu-iio-objs += inv_mpu_misc.o
+inv-mpu-iio-objs += inv_mpu3050_iio.o
+inv-mpu-iio-objs += dmpDefaultMPU6050.o
+
+CFLAGS_inv_mpu_core.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu_ring.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu_trigger.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu_misc.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu3050_iio.o += -Idrivers/staging/iio
+CFLAGS_dmpDefaultMPU6050.o += -Idrivers/staging/iio
+
+# the Bosch BMA250 driver is added to the inv-mpu device driver because it
+# must be connected to an MPU3050 device on the secondary slave bus.
+ifeq ($(CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250), y)
+inv-mpu-iio-objs += inv_slave_bma250.o
+CFLAGS_inv_slave_bma250.o += -Idrivers/staging/iio
+endif
+
diff --git a/drivers/iio/imu/inv_mpu/README b/drivers/iio/imu/inv_mpu/README
new file mode 100644
index 00000000..d59e2d1d
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/README
@@ -0,0 +1,569 @@
+Kernel driver inv-mpu-iio
+Author: Invensense <http://invensense.com>
+
+Table of Contents:
+==================
+- Description
+- Integrating the Driver in the Linux Kernel
+- Board and Platform Data
+ > Interrupt Pin
+ > Platform Data
+- Board File Modifications for Secondary I2C Configuration
+ > MPU-6050 + AKM8963 on the secondary I2C interface
+ > MPU-6500 + AKM8963 on the secondary I2C interface
+ > MPU-9150
+ > MPU-9250
+ > MPU-3050 + BMA250 on the secondary I2C interface
+- Board File Modifications for Invensense Devices
+ > MPU-3050
+ > ITG-3500
+ > MPU-6050
+ > MPU-6500
+ > MPU-9150
+ > MPU-9250
+- IIO Subsystem
+ > Communicating with the Driver in Userspace
+ > ITG-3500
+ > MPU-6050 and MPU-6500
+ > MPU-9150
+ > MPU-9250
+ > MPU-3050 + BMA250 on the secondary I2C interface
+- Suspend and Resume
+- DMP Event
+- Motion Event
+- Streaming Data to an Userspace Application
+- Recommended Sysfs Entry Setup Sequence
+ > With DMP Firmware
+ > Without DMP Firmware
+- Test Applications
+ > Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250
+ > Running Test Applications with MPU-3050/ITG-3500
+
+
+
+Description
+===========
+This document describes how to install the Invensense device driver into a
+Linux kernel. The Invensense driver currently supports the following sensors:
+- ITG-3500
+- MPU-6050
+- MPU-9150
+- MPU-6500
+- MPU-9250
+- MPU-3050
+
+The slave address of each device is either 0x68 or 0x69, depending on the AD0
+pin value of the device. Please refer to the appropriate product specification
+document for further information regarding the AD0 pin. The driver supports both
+addresses.
+
+The following files are included in this package:
+- Kconfig
+- Makefile
+- inv_mpu_core.c
+- inv_mpu_misc.c
+- inv_mpu_trigger.c
+- inv_mpu3050_iio.c
+- inv_mpu_iio.h
+- inv_mpu_ring.c
+- inv_slave_bma250.c
+- dmpDefaultMPU6050.c
+- dmpkey.h
+- dmpmap.h
+- mpu.h
+
+
+Integrating the Driver in the Linux Kernel
+==========================================
+Please add the files as follows:
+- Add mpu.h to "kernel/include/linux".
+- Add all other files to drivers/staging/iio/imu/inv_mpu
+(another directory is acceptable, but this is the recommended destination)
+
+In order to see the driver in menuconfig when building the kernel, please
+make modifications as shown below:
+
+ modify "drivers/staging/iio/imu/Kconfig" with:
+ >> source "drivers/staging/iio/imu/inv_mpu/Kconfig"
+
+ modify "drivers/staging/iio/imu/Makefile" with:
+ >> obj-y += inv_mpu/
+
+
+Board and Platform Data
+=======================
+In order to recognize the Invensense device on the I2C bus, the board file must
+be modified.
+The i2c_board_info instance must be defined as shown below.
+
+Interrupt Pin
+-------------
+The hardcoded value of 140 corresponds to the GPIO input pin connected to the
+Invensense device's interrupt pin.
+This pin will most likely be different for your platform, and the value should
+be changed accordingly.
+
+Platform Data
+-------------
+The platform data (orientation matrix and secondary bus configurations) must be
+modified as show below, according to your particular platform configuration.
+
+Please note that the MPU-9150 it is treated as a MPU-6050 with AKM8975 on the
+device's secondary I2C interface. Thus the secondary I2C address must be
+provided.
+
+Please note that the MPU-9250 it is treated as a MPU-6500 with AKM8963 on the
+device's secondary I2C interface. Thus the secondary I2C address must be
+provided.
+
+Board File Modifications for Secondary I2C Configuration
+========================================================
+For the Panda Board, the board file can be found at
+arch/arm/mach-omap2/board-omap4panda.c.
+Please modify the pertinent baord file in your system according to the examples
+shown below:
+
+MPU-6050 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8963,
+ .secondary_i2c_addr = 0x0E
+};
+
+MPU-6500 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8963,
+ .secondary_i2c_addr = 0x0E
+};
+
+MPU-9150
+--------
+For MPU-9150, please provide the following secondary I2C bus information.
+
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8975,
+ .secondary_i2c_addr = 0x0E
+};
+
+MPU-9250
+--------
+For MPU-9250, please provide the following secondary I2C bus information.
+
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8963,
+ .secondary_i2c_addr = 0x0C
+};
+
+MPU-3050 + BMA250 on the secondary I2C interface
+------------------------------------------------
+For BMA250 on the secondary I2C bus, please provide the following information.
+
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_ACCEL,
+ .sec_slave_id = ACCEL_ID_BMA250,
+ .secondary_i2c_addr = 0x18,
+};
+
+
+Board File Modifications for Invensense Devices
+===============================================
+For Invensense devices, please provide the i2c init data as shown in the
+examples below.
+
+In the _i2c_init function, the device is registered in the following manner:
+
+ // arch/arm/mach-omap2/board-omap4panda.c
+ // in static int __init omap4_panda_i2c_init(void)
+ omap_register_i2c_bus(4, 400,
+ single_chip_board_info,
+ ARRAY_SIZE(single_chip_board_info));
+
+MPU-3050
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu3050", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+ITG-3050
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("itg3500", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU6050
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6050", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU6500
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6500", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU9150
+-------
+arch/arm/mach-omap2/board-omap4panda.c
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu9150", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU9250
+-------
+arch/arm/mach-omap2/board-omap4panda.c
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu9250", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+IIO subsystem
+=============
+A successful installation will create the following two new directories under
+/sys/bus/iio/devices:
+ - iio:device0
+ - trigger0
+
+Also, a new file, "iio:device0", will be created in the /dev/ diretory.
+(if you have more than one IIO device, the file will be named "iio:deviceX",
+where X is a number)
+
+
+Communicating with the Driver in Userspace
+------------------------------------------
+The driver generates several files in sysfs upon installation.
+These files are used to communicate with the driver. The files can be found
+at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above).
+
+A brief description of the pertinent files for each Invensense device is shown
+below:
+
+ITG-3500
+--------
+temperature (Read-only)
+--Read temperature data directly from the temperature register.
+
+sampling_frequency (Read/write)
+--Configure the ADC sampling rate and FIFO output rate.
+
+sampling_frequency_available(read-only)
+--show commonly used frequency
+
+clock_source (Read-only)
+--Check which clock-source is used by the chip.
+
+power_state (Read/write)
+--turn on/off the power supply
+
+self_test (read-only)
+--read this entry trigger self test. The return value is D.
+D is the success/fail.
+For different chip, the result is different for success/fail.
+1 means success 0 means fail. The LSB of D is for gyro; the bit
+next to LSB of D is for accel. The bit 2 of D is for compass result.
+
+key (read-only)
+--show the key value of this driver. Used by MPL.
+
+gyro_matrix (read-only)
+--show the orientation matrix obtained from the board file.
+
+MPU-6050 and MPU-6500
+---------------------
+MPU-6050 and MPU-6500 have all sysfs files belonging to ITG-3500 (shown above).
+In addition, it has the files below:
+
+gyro_enable (read/write)
+--enable/disable gyro functionality. Affects raw_gyro. Turning this off this
+ will shut down gyro and save power.
+
+accl_enable (read/write)
+--enable/disable accelerometer functionality. Affects raw_accl.
+Turning this off this will shut down accel and save power.
+
+firmware_loaded (read/write)
+--Flag indicating the whether firmware is loaded or not in the DMP engine.
+0 means no firmware loaded. 1 means firmware is already loaded . This
+flag can only be written as 0. It internally updates to 1.
+
+dmp_on(read/write)
+--This entry controls whether to run DMP or not.
+Write 1 to enable DMP and write 0 to disable dmp.
+Please note that firmware_loaded must be 1 in order to enable DMP.
+
+dmp_int_on(read/write)
+--This entry controls whether dmp interrupt is on/off.
+Please note that firmware_loaded must be 1.
+Also, we'd like to remind you that it is sometimes advantageous to
+turn interrupts off while the DMP is running.
+
+dmp_output_rate
+--control dmp output rate when dmp is on.
+
+dmp_event_int_on(read/write)
+--This entry controls whether dmp event interrupt is on/off.
+Please note that turning this on will turn off the data interrupt.
+Interrupts will be generated only when events occur.
+This is useful for saving power when the system is waiting for a special event
+to wake up.
+
+dmp_firmware (write only binary file)
+--DMP firmware code is loaded into this entry.
+If loading is successful, the firmware_loaded flag will be updated to 1.
+In order to load new firmware, the firmware_loaded flag must be first set to 0.
+
+accel_matrix
+--orientation matrix for accelerometer.
+
+quaternion_on
+--Turn on/off quaterniion data output. DMP is required for this feature.
+
+pedometer_time
+pedometer_steps,
+--Pedometer related entries
+
+event_tap
+event_display_orientation
+event_accel_motion
+--Event related entries.
+Please poll these entries to read their values. Direct reads will yield
+meaningless results.
+Further details are provided in the DMP Events section of this README.
+
+tap_on
+--Controls tap function of DMP
+
+tap_time
+tap_min_count
+tap_threshold
+--Tap related entries. Controls various parameters of tap function.
+
+display_orientation_on
+--Turn on/off display orientation function of DMP.
+
+quaternion_on
+--Turn on/off quaterniion data output. DMP is required for this feature.
+
+Low power accel motion interrupt related settings.
+if motion_lpa_on is set, this will disable all engines except accel. Accel will
+enter low power mode and the whole chip will be turned on/off at specific frequency.
+-----------------------------------------------------------------------------
+motion_lpa_duration
+--set motion duration. in ms. This means filtered out all the motino interrupts
+ during this period.
+
+motion_lpa_threshold
+--set motion threshold. in mg. The maximum is 1020mg and resolution is 32mg.
+
+motion_lpa_on
+--turn on/off motion function.
+
+motion_lpa_freq
+--motion lpa frequency. which determines power on/off frequency.
+------------------------------------------------------------------------------
+MPU-9150
+--------
+MPU-9150 has all of MPU-6050's entries. It also has two additional entries,
+described below.
+
+compass_enable (read/write)
+--Enables compass function.
+
+compass_matrix (read-only)
+--Compass orientation matrix
+
+MPU-3050 with BMA250 on secondary I2C interface
+-----------------------------------------------
+MPU-3050 with BMA250 on the secondary I2C interface has ever ITG-3500 entry.
+It also has two additional entries, shown below:
+
+accl_matrix
+
+accl_enable
+
+Suspend and Resume
+===================================================
+The suspend and resume functions are call backs registered to the system
+and executed when the system goes in suspend and resumes.
+It is enabled when CONFIG_PM is defined.
+The current behavior is simple:
+- suspend will turn off the chip
+- resume will turn on the chip
+
+However, it is possible for the driver to do more complex things;
+for example, leaving pedometers running when system is off. This can save whole
+system power while letting pedometer working. Other behaviors are possible
+too.
+
+DMP Event
+=========
+A DMP Event is an event that is output by the DMP unit within the Invensense
+device (MPU).
+Only the MPU-6050, MPU-6500, MPU-9250, MPU-9150, MPU-9250 feature the DMP.
+
+There are three sysfs entries for DMP events:
+- event_tap
+- event_orientation
+- event_display_orientation
+
+These four events must be polled before reading.
+
+The proper method to poll sysfs is as follows:
+1. open file.
+2. dummy read.
+3. poll.
+4. once the poll passed, use fopen and fread to read the sysfs entry.
+5. interpret the data.
+
+Motion Event
+============
+Motion event are accelerometer enabled event. They don't need dmp nor data
+output, only the accelerometer engine needs to be on.
+MPU6050/MPU6500 supports motion and no motion event: these are the eight sysfs entries
+------------------------------------------------------------------------------
+motion_threshold:
+threshold for motion detection.
+
+motion_dur:
+duration for motion to trigger.
+
+motion_on:
+turn on/off of motion function.
+
+event_accel_motion:
+pollable event sysfs entry for motion.
+------------------------------------------------------------
+
+Streaming Data to an Userspace Application
+==========================================
+When streaming data to an userspace application, we recommend that you access
+gyro/accel/compass data via /dev/iio:device0.
+
+Please follow the steps below to read data at a constant rate from the driver:
+
+1. Write a 1 to power_state to turn on the chip. This is the default setting
+ after installing the driver.
+2. Write the desired output rate to fifo_rate.
+3. Write 1 to enable to turn on the event.
+4. Read /dev/iio:device0 to get a string of gyro/accel/compass data.
+5. Parse this string to obtain each gyro/accel/compass element.
+6. If dmp firmware code is loaded, use "dmp_on" to enable/disable dmp.
+7. If compass is enabled, the output will contain compass data.
+
+
+Recommended Sysfs Entry Setup Senquence
+=======================================
+
+Without DMP Firmware
+--------------------
+1. Set "power_state" to 1,
+2. Set the scale and fifo rate values according to your needs.
+3. Set gyro_enable, accel_enable, and compass_enable according to your needs.
+ For example:
+ - If you only want gyro data, set accel_enable to 0 (and compass_enable to
+ 0, if applicable).
+ - If you only want accel data, set gyro_enable to 0 (and compass_enable to
+ 0, if applicable).
+ - If you only want compass data, set gyro_enable to 0 and accel_enable to 0.
+4. Set "enable" to 1.
+5. You will now get the output that you want.
+
+With DMP Firmware
+-----------------
+1. Set "power_state" to 1.
+2. Write "0" to firmware_loaded if it is not zero already.
+3. Load firmware into "dmp_firmware" as a whole. Don't split the DMP firmware
+ image.
+4. Make sure firmware_loaded is 1 after loading the DMP image.
+5. Make appropriate configurations as shown above in the "without DMP firmware"
+ case.
+6. Set dmp_on to 1.
+7. Set "enable" to 1.
+
+Please note that the enable function uses the enable entry under
+"/sys/bus/iio/devices/iio:device0/buffer"
+
+Test Applications
+=================
+A test application is located under software/simple_apps/mpu_iio.
+This application is stand-alone in that it cannot be run concurrently with other
+entities trying to access the device node(s) or sysfs entries; in particular,
+the
+
+Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250
+---------------------------------------------------------
+To run test applications with MPU-9150, MPU-9250, MPU-6050, or MPU-6500 devices,
+please use the following commands:
+
+1. For tap/display orientation events:
+ mpu_iio -c 10 -l 3 -p
+
+2. In addition, to test the motion interrupt (and no_motion on MPU6050) use:
+ mpu_iio -c 10 -l 3 -p -m
+
+3. For printing data normally:
+ mpu_iio -c 10 -l 3 -r
+
+Running Test Applications with MPU-3050/ITG-3500
+------------------------------------------------
+To run test applications with MPU-3050 or ITG-3500 devices,
+please use the following command:
+
+1. For printing data normally:
+ mpu_iio -c 10 -l 3 -r
+
+Please use mpu_iio.c and iio_utils.h as example code for your development
+purposes.
diff --git a/drivers/iio/imu/inv_mpu/dmpDefaultMPU6050.c b/drivers/iio/imu/inv_mpu/dmpDefaultMPU6050.c
new file mode 100644
index 00000000..b49fc0a5
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/dmpDefaultMPU6050.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file dmpDefaultMPU6050.c
+ * @brief dmp Default data
+ * @details This file is part of invensense mpu driver code
+ *
+ */
+
+#include "dmpKey.h"
+#include "dmpmap.h"
+
+#define CFG_LP_QUAT (2710)
+#define END_ORIENT_TEMP (1874)
+#define CFG_27 (2738)
+#define CFG_20 (2232)
+#define CFG_23 (2741)
+#define CFG_FIFO_ON_EVENT (2687)
+#define END_PREDICTION_UPDATE (1769)
+#define CGNOTICE_INTR (2629)
+#define X_GRT_Y_TMP (1366)
+#define CFG_DR_INT (1029)
+#define CFG_AUTH (1035)
+#define UPDATE_PROP_ROT (1843)
+#define FCFG_1 (1062)
+#define SKIP_X_GRT_Y_TMP (1367)
+#define SKIP_END_COMPARE (1443)
+#define FCFG_3 (1110)
+#define FCFG_2 (1066)
+#define END_COMPARE_Y_X_TMP2 (1463)
+#define END_COMPARE_Y_X_TMP3 (1442)
+#define FCFG_7 (1076)
+#define FCFG_6 (1128)
+#define FLAT_STATE_END (1721)
+#define SWING_END_4 (1624)
+#define SWING_END_2 (1573)
+#define SWING_END_3 (1595)
+#define SWING_END_1 (1558)
+#define CFG_8 (2716)
+#define CFG_15 (2724)
+#define CFG_16 (2742)
+#define FLAT_STATE_END_TEMP (1691)
+#define END_COMPARE_Y_X_TMP (1415)
+#define DO_NOT_UPDATE_PROP_ROT (1847)
+#define CFG_7 (1221)
+#define END_COMPARE_Y_X (1492)
+#define SKIP_SWING_END_1 (1559)
+#define SKIP_SWING_END_3 (1596)
+#define SKIP_SWING_END_2 (1574)
+#define TILTG75_START (1680)
+#define CFG_6 (2749)
+#define TILTL75_END (1677)
+#define END_ORIENT (1892)
+#define CFG_FLICK_IN (2582)
+#define TILTL75_START (1651)
+#define CFG_MOTION_BIAS (1224)
+#define X_GRT_Y (1416)
+#define TEMPLABEL (2332)
+#define CFG_DISPLAY_ORIENT_INT (1861)
+#define X_GRT_Y_TMP2 (1387)
+
+#define D_0_22 (22+512)
+#define D_0_24 (24+512)
+
+#define D_0_36 (36)
+#define D_0_52 (52)
+#define D_0_96 (96)
+#define D_0_104 (104)
+#define D_0_108 (108)
+#define D_0_163 (163)
+#define D_0_188 (188)
+#define D_0_192 (192)
+#define D_0_224 (224)
+#define D_0_228 (228)
+#define D_0_232 (232)
+#define D_0_236 (236)
+
+#define D_1_2 (256 + 2)
+#define D_1_4 (256 + 4)
+#define D_1_8 (256 + 8)
+#define D_1_10 (256 + 10)
+#define D_1_24 (256 + 24)
+#define D_1_28 (256 + 28)
+#define D_1_36 (256 + 36)
+#define D_1_40 (256 + 40)
+#define D_1_44 (256 + 44)
+#define D_1_72 (256 + 72)
+#define D_1_74 (256 + 74)
+#define D_1_79 (256 + 79)
+#define D_1_88 (256 + 88)
+#define D_1_90 (256 + 90)
+#define D_1_92 (256 + 92)
+#define D_1_96 (256 + 96)
+#define D_1_98 (256 + 98)
+#define D_1_106 (256 + 106)
+#define D_1_108 (256 + 108)
+#define D_1_112 (256 + 112)
+#define D_1_128 (256 + 144)
+#define D_1_152 (256 + 12)
+#define D_1_160 (256 + 160)
+#define D_1_176 (256 + 176)
+#define D_1_178 (256 + 178)
+#define D_1_218 (256 + 218)
+#define D_1_232 (256 + 232)
+#define D_1_236 (256 + 236)
+#define D_1_240 (256 + 240)
+#define D_1_244 (256 + 244)
+#define D_1_250 (256 + 250)
+#define D_1_252 (256 + 252)
+#define D_2_12 (512 + 12)
+#define D_2_96 (512 + 96)
+#define D_2_108 (512 + 108)
+#define D_2_208 (512 + 208)
+#define D_2_224 (512 + 224)
+#define D_2_236 (512 + 236)
+#define D_2_244 (512 + 244)
+#define D_2_248 (512 + 248)
+#define D_2_252 (512 + 252)
+
+#define CPASS_BIAS_X (35 * 16 + 4)
+#define CPASS_BIAS_Y (35 * 16 + 8)
+#define CPASS_BIAS_Z (35 * 16 + 12)
+#define CPASS_MTX_00 (36 * 16)
+#define CPASS_MTX_01 (36 * 16 + 4)
+#define CPASS_MTX_02 (36 * 16 + 8)
+#define CPASS_MTX_10 (36 * 16 + 12)
+#define CPASS_MTX_11 (37 * 16)
+#define CPASS_MTX_12 (37 * 16 + 4)
+#define CPASS_MTX_20 (37 * 16 + 8)
+#define CPASS_MTX_21 (37 * 16 + 12)
+#define CPASS_MTX_22 (43 * 16 + 12)
+#define D_ACT0 (40 * 16)
+#define D_ACSX (40 * 16 + 4)
+#define D_ACSY (40 * 16 + 8)
+#define D_ACSZ (40 * 16 + 12)
+
+#define FLICK_MSG (45 * 16 + 4)
+#define FLICK_COUNTER (45 * 16 + 8)
+#define FLICK_LOWER (45 * 16 + 12)
+#define FLICK_UPPER (46 * 16 + 12)
+
+#define D_AUTH_OUT (992)
+#define D_AUTH_IN (996)
+#define D_AUTH_A (1000)
+#define D_AUTH_B (1004)
+
+#define D_PEDSTD_BP_B (768 + 0x1C)
+#define D_PEDSTD_HP_A (768 + 0x78)
+#define D_PEDSTD_HP_B (768 + 0x7C)
+#define D_PEDSTD_BP_A4 (768 + 0x40)
+#define D_PEDSTD_BP_A3 (768 + 0x44)
+#define D_PEDSTD_BP_A2 (768 + 0x48)
+#define D_PEDSTD_BP_A1 (768 + 0x4C)
+#define D_PEDSTD_INT_THRSH (768 + 0x68)
+#define D_PEDSTD_CLIP (768 + 0x6C)
+#define D_PEDSTD_SB (768 + 0x28)
+#define D_PEDSTD_SB_TIME (768 + 0x2C)
+#define D_PEDSTD_PEAKTHRSH (768 + 0x98)
+#define D_PEDSTD_TIML (768 + 0x2A)
+#define D_PEDSTD_TIMH (768 + 0x2E)
+#define D_PEDSTD_PEAK (768 + 0X94)
+#define D_PEDSTD_STEPCTR (768 + 0x60)
+#define D_PEDSTD_TIMECTR (964)
+#define D_PEDSTD_DECI (768 + 0xA0)
+
+#define D_HOST_NO_MOT (976)
+#define D_ACCEL_BIAS (660)
+
+#define D_ORIENT_GAP (76)
+
+#define D_TILT0_H (48)
+#define D_TILT0_L (50)
+#define D_TILT1_H (52)
+#define D_TILT1_L (54)
+#define D_TILT2_H (56)
+#define D_TILT2_L (58)
+#define D_TILT3_H (60)
+#define D_TILT3_L (62)
+
+static const struct tKeyLabel dmpTConfig[] = {
+ {KEY_CFG_27, CFG_27},
+ {KEY_CFG_20, CFG_20},
+ {KEY_CFG_23, CFG_23},
+ {KEY_CFG_FIFO_ON_EVENT, CFG_FIFO_ON_EVENT},
+ {KEY_CGNOTICE_INTR, CGNOTICE_INTR},
+ {KEY_X_GRT_Y_TMP, X_GRT_Y_TMP},
+ {KEY_CFG_DR_INT, CFG_DR_INT},
+ {KEY_CFG_AUTH, CFG_AUTH},
+ {KEY_FCFG_1, FCFG_1},
+ {KEY_SKIP_X_GRT_Y_TMP, SKIP_X_GRT_Y_TMP},
+ {KEY_SKIP_END_COMPARE, SKIP_END_COMPARE},
+ {KEY_FCFG_3, FCFG_3},
+ {KEY_FCFG_2, FCFG_2},
+ {KEY_END_COMPARE_Y_X_TMP2, END_COMPARE_Y_X_TMP2},
+ {KEY_CFG_DISPLAY_ORIENT_INT, CFG_DISPLAY_ORIENT_INT},
+ {KEY_FCFG_7, FCFG_7},
+ {KEY_FCFG_6, FCFG_6},
+ {KEY_CFG_8, CFG_8},
+ {KEY_CFG_15, CFG_15},
+ {KEY_CFG_16, CFG_16},
+ {KEY_END_COMPARE_Y_X_TMP, END_COMPARE_Y_X_TMP},
+ {KEY_CFG_6, CFG_6},
+ {KEY_END_COMPARE_Y_X, END_COMPARE_Y_X},
+ {KEY_CFG_LP_QUAT, CFG_LP_QUAT},
+ {KEY_END_ORIENT, END_ORIENT},
+ {KEY_CFG_FLICK_IN, CFG_FLICK_IN},
+ {KEY_CFG_7, CFG_7},
+ {KEY_CFG_MOTION_BIAS, CFG_MOTION_BIAS},
+ {KEY_X_GRT_Y, X_GRT_Y},
+ {KEY_TEMPLABEL, TEMPLABEL},
+ {KEY_END_COMPARE_Y_X_TMP3, END_COMPARE_Y_X_TMP3},
+ {KEY_X_GRT_Y_TMP2, X_GRT_Y_TMP2},
+ {KEY_D_0_22, D_0_22},
+ {KEY_D_0_96, D_0_96},
+ {KEY_D_0_104, D_0_104},
+ {KEY_D_0_108, D_0_108},
+ {KEY_D_1_36, D_1_36},
+ {KEY_D_1_40, D_1_40},
+ {KEY_D_1_44, D_1_44},
+ {KEY_D_1_72, D_1_72},
+ {KEY_D_1_74, D_1_74},
+ {KEY_D_1_79, D_1_79},
+ {KEY_D_1_88, D_1_88},
+ {KEY_D_1_90, D_1_90},
+ {KEY_D_1_92, D_1_92},
+ {KEY_D_1_160, D_1_160},
+ {KEY_D_1_176, D_1_176},
+ {KEY_D_1_178, D_1_178},
+ {KEY_D_1_218, D_1_218},
+ {KEY_D_1_232, D_1_232},
+ {KEY_D_1_250, D_1_250},
+ {KEY_DMP_TAPW_MIN, DMP_TAPW_MIN},
+ {KEY_DMP_TAP_THR_X, DMP_TAP_THX},
+ {KEY_DMP_TAP_THR_Y, DMP_TAP_THY},
+ {KEY_DMP_TAP_THR_Z, DMP_TAP_THZ},
+ {KEY_DMP_SH_TH_Y, DMP_SH_TH_Y},
+ {KEY_DMP_SH_TH_X, DMP_SH_TH_X},
+ {KEY_DMP_SH_TH_Z, DMP_SH_TH_Z},
+ {KEY_DMP_ORIENT, DMP_ORIENT},
+ {KEY_D_AUTH_OUT, D_AUTH_OUT},
+ {KEY_D_AUTH_IN, D_AUTH_IN},
+ {KEY_D_AUTH_A, D_AUTH_A},
+ {KEY_D_AUTH_B, D_AUTH_B},
+ {KEY_CPASS_BIAS_X, CPASS_BIAS_X},
+ {KEY_CPASS_BIAS_Y, CPASS_BIAS_Y},
+ {KEY_CPASS_BIAS_Z, CPASS_BIAS_Z},
+ {KEY_CPASS_MTX_00, CPASS_MTX_00},
+ {KEY_CPASS_MTX_01, CPASS_MTX_01},
+ {KEY_CPASS_MTX_02, CPASS_MTX_02},
+ {KEY_CPASS_MTX_10, CPASS_MTX_10},
+ {KEY_CPASS_MTX_11, CPASS_MTX_11},
+ {KEY_CPASS_MTX_12, CPASS_MTX_12},
+ {KEY_CPASS_MTX_20, CPASS_MTX_20},
+ {KEY_CPASS_MTX_21, CPASS_MTX_21},
+ {KEY_CPASS_MTX_22, CPASS_MTX_22},
+ {KEY_D_ACT0, D_ACT0},
+ {KEY_D_ACSX, D_ACSX},
+ {KEY_D_ACSY, D_ACSY},
+ {KEY_D_ACSZ, D_ACSZ},
+ {KEY_FLICK_MSG, FLICK_MSG},
+ {KEY_FLICK_COUNTER, FLICK_COUNTER},
+ {KEY_FLICK_LOWER, FLICK_LOWER},
+ {KEY_FLICK_UPPER, FLICK_UPPER},
+ {KEY_D_PEDSTD_BP_B, D_PEDSTD_BP_B},
+ {KEY_D_PEDSTD_HP_A, D_PEDSTD_HP_A},
+ {KEY_D_PEDSTD_HP_B, D_PEDSTD_HP_B},
+ {KEY_D_PEDSTD_BP_A4, D_PEDSTD_BP_A4},
+ {KEY_D_PEDSTD_BP_A3, D_PEDSTD_BP_A3},
+ {KEY_D_PEDSTD_BP_A2, D_PEDSTD_BP_A2},
+ {KEY_D_PEDSTD_BP_A1, D_PEDSTD_BP_A1},
+ {KEY_D_PEDSTD_INT_THRSH, D_PEDSTD_INT_THRSH},
+ {KEY_D_PEDSTD_CLIP, D_PEDSTD_CLIP},
+ {KEY_D_PEDSTD_SB, D_PEDSTD_SB},
+ {KEY_D_PEDSTD_SB_TIME, D_PEDSTD_SB_TIME},
+ {KEY_D_PEDSTD_PEAKTHRSH, D_PEDSTD_PEAKTHRSH},
+ {KEY_D_PEDSTD_TIML, D_PEDSTD_TIML},
+ {KEY_D_PEDSTD_TIMH, D_PEDSTD_TIMH},
+ {KEY_D_PEDSTD_PEAK, D_PEDSTD_PEAK},
+ {KEY_D_PEDSTD_STEPCTR, D_PEDSTD_STEPCTR},
+ {KEY_D_PEDSTD_TIMECTR, D_PEDSTD_TIMECTR},
+ {KEY_D_PEDSTD_DECI, D_PEDSTD_DECI},
+ {KEY_D_HOST_NO_MOT, D_HOST_NO_MOT},
+ {KEY_D_ACCEL_BIAS, D_ACCEL_BIAS},
+};
+#define NUM_LOCAL_KEYS (sizeof(dmpTConfig)/sizeof(dmpTConfig[0]))
+
+static struct tKeyLabel keys[NUM_KEYS];
+
+unsigned short inv_dmp_get_address(unsigned short key)
+{
+ static int isSorted;
+ if (!isSorted) {
+ int kk;
+ for (kk = 0; kk < NUM_KEYS; ++kk) {
+ keys[kk].addr = 0xffff;
+ keys[kk].key = kk;
+ }
+ for (kk = 0; kk < NUM_LOCAL_KEYS; ++kk)
+ keys[dmpTConfig[kk].key].addr = dmpTConfig[kk].addr;
+ isSorted = 1;
+ }
+ if (key >= NUM_KEYS)
+ return 0xffff;
+ return keys[key].addr;
+}
+/**
+ * @}
+ */
diff --git a/drivers/iio/imu/inv_mpu/dmpKey.h b/drivers/iio/imu/inv_mpu/dmpKey.h
new file mode 100644
index 00000000..150f429d
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/dmpKey.h
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file dmpKey.h
+ * @brief dmp Key definition
+ * @details This file is part of invensense mpu driver code
+ *
+ */
+
+#ifndef DMPKEY_H__
+#define DMPKEY_H__
+
+#define KEY_CFG_25 (0)
+#define KEY_CFG_24 (KEY_CFG_25 + 1)
+#define KEY_CFG_26 (KEY_CFG_24 + 1)
+#define KEY_CFG_27 (KEY_CFG_26 + 1)
+#define KEY_CFG_21 (KEY_CFG_27 + 1)
+#define KEY_CFG_20 (KEY_CFG_21 + 1)
+#define KEY_CFG_TAP4 (KEY_CFG_20 + 1)
+#define KEY_CFG_TAP5 (KEY_CFG_TAP4 + 1)
+#define KEY_CFG_TAP6 (KEY_CFG_TAP5 + 1)
+#define KEY_CFG_TAP7 (KEY_CFG_TAP6 + 1)
+#define KEY_CFG_TAP0 (KEY_CFG_TAP7 + 1)
+#define KEY_CFG_TAP1 (KEY_CFG_TAP0 + 1)
+#define KEY_CFG_TAP2 (KEY_CFG_TAP1 + 1)
+#define KEY_CFG_TAP3 (KEY_CFG_TAP2 + 1)
+#define KEY_CFG_TAP_QUANTIZE (KEY_CFG_TAP3 + 1)
+#define KEY_CFG_TAP_JERK (KEY_CFG_TAP_QUANTIZE + 1)
+#define KEY_CFG_DR_INT (KEY_CFG_TAP_JERK + 1)
+#define KEY_CFG_AUTH (KEY_CFG_DR_INT + 1)
+#define KEY_CFG_TAP_SAVE_ACCB (KEY_CFG_AUTH + 1)
+#define KEY_CFG_TAP_CLEAR_STICKY (KEY_CFG_TAP_SAVE_ACCB + 1)
+#define KEY_CFG_FIFO_ON_EVENT (KEY_CFG_TAP_CLEAR_STICKY + 1)
+#define KEY_FCFG_ACCEL_INPUT (KEY_CFG_FIFO_ON_EVENT + 1)
+#define KEY_FCFG_ACCEL_INIT (KEY_FCFG_ACCEL_INPUT + 1)
+#define KEY_CFG_23 (KEY_FCFG_ACCEL_INIT + 1)
+#define KEY_FCFG_1 (KEY_CFG_23 + 1)
+#define KEY_FCFG_3 (KEY_FCFG_1 + 1)
+#define KEY_FCFG_2 (KEY_FCFG_3 + 1)
+#define KEY_CFG_3D (KEY_FCFG_2 + 1)
+#define KEY_CFG_3B (KEY_CFG_3D + 1)
+#define KEY_CFG_3C (KEY_CFG_3B + 1)
+#define KEY_FCFG_5 (KEY_CFG_3C + 1)
+#define KEY_FCFG_4 (KEY_FCFG_5 + 1)
+#define KEY_FCFG_7 (KEY_FCFG_4 + 1)
+#define KEY_FCFG_FSCALE (KEY_FCFG_7 + 1)
+#define KEY_FCFG_AZ (KEY_FCFG_FSCALE + 1)
+#define KEY_FCFG_6 (KEY_FCFG_AZ + 1)
+#define KEY_FCFG_LSB4 (KEY_FCFG_6 + 1)
+#define KEY_CFG_12 (KEY_FCFG_LSB4 + 1)
+#define KEY_CFG_14 (KEY_CFG_12 + 1)
+#define KEY_CFG_15 (KEY_CFG_14 + 1)
+#define KEY_CFG_16 (KEY_CFG_15 + 1)
+#define KEY_CFG_18 (KEY_CFG_16 + 1)
+#define KEY_CFG_6 (KEY_CFG_18 + 1)
+#define KEY_CFG_7 (KEY_CFG_6 + 1)
+#define KEY_CFG_4 (KEY_CFG_7 + 1)
+#define KEY_CFG_5 (KEY_CFG_4 + 1)
+#define KEY_CFG_2 (KEY_CFG_5 + 1)
+#define KEY_CFG_3 (KEY_CFG_2 + 1)
+#define KEY_CFG_1 (KEY_CFG_3 + 1)
+#define KEY_CFG_EXTERNAL (KEY_CFG_1 + 1)
+#define KEY_CFG_8 (KEY_CFG_EXTERNAL + 1)
+#define KEY_CFG_9 (KEY_CFG_8 + 1)
+#define KEY_CFG_ORIENT_3 (KEY_CFG_9 + 1)
+#define KEY_CFG_ORIENT_2 (KEY_CFG_ORIENT_3 + 1)
+#define KEY_CFG_ORIENT_1 (KEY_CFG_ORIENT_2 + 1)
+#define KEY_CFG_GYRO_SOURCE (KEY_CFG_ORIENT_1 + 1)
+#define KEY_CFG_ORIENT_IRQ_1 (KEY_CFG_GYRO_SOURCE + 1)
+#define KEY_CFG_ORIENT_IRQ_2 (KEY_CFG_ORIENT_IRQ_1 + 1)
+#define KEY_CFG_ORIENT_IRQ_3 (KEY_CFG_ORIENT_IRQ_2 + 1)
+#define KEY_FCFG_MAG_VAL (KEY_CFG_ORIENT_IRQ_3 + 1)
+#define KEY_FCFG_MAG_MOV (KEY_FCFG_MAG_VAL + 1)
+#define KEY_CFG_LP_QUAT (KEY_FCFG_MAG_MOV + 1)
+
+/* MPU6050 keys */
+#define KEY_CFG_ACCEL_FILTER (KEY_CFG_LP_QUAT + 1)
+#define KEY_CFG_MOTION_BIAS (KEY_CFG_ACCEL_FILTER + 1)
+#define KEY_TEMPLABEL (KEY_CFG_MOTION_BIAS + 1)
+
+#define KEY_D_0_22 (KEY_TEMPLABEL + 1)
+#define KEY_D_0_24 (KEY_D_0_22 + 1)
+#define KEY_D_0_36 (KEY_D_0_24 + 1)
+#define KEY_D_0_52 (KEY_D_0_36 + 1)
+#define KEY_D_0_96 (KEY_D_0_52 + 1)
+#define KEY_D_0_104 (KEY_D_0_96 + 1)
+#define KEY_D_0_108 (KEY_D_0_104 + 1)
+#define KEY_D_0_163 (KEY_D_0_108 + 1)
+#define KEY_D_0_188 (KEY_D_0_163 + 1)
+#define KEY_D_0_192 (KEY_D_0_188 + 1)
+#define KEY_D_0_224 (KEY_D_0_192 + 1)
+#define KEY_D_0_228 (KEY_D_0_224 + 1)
+#define KEY_D_0_232 (KEY_D_0_228 + 1)
+#define KEY_D_0_236 (KEY_D_0_232 + 1)
+
+#define KEY_DMP_PREVPTAT (KEY_D_0_236 + 1)
+#define KEY_D_1_2 (KEY_DMP_PREVPTAT + 1)
+#define KEY_D_1_4 (KEY_D_1_2 + 1)
+#define KEY_D_1_8 (KEY_D_1_4 + 1)
+#define KEY_D_1_10 (KEY_D_1_8 + 1)
+#define KEY_D_1_24 (KEY_D_1_10 + 1)
+#define KEY_D_1_28 (KEY_D_1_24 + 1)
+#define KEY_D_1_36 (KEY_D_1_28 + 1)
+#define KEY_D_1_40 (KEY_D_1_36 + 1)
+#define KEY_D_1_44 (KEY_D_1_40 + 1)
+#define KEY_D_1_72 (KEY_D_1_44 + 1)
+#define KEY_D_1_74 (KEY_D_1_72 + 1)
+#define KEY_D_1_79 (KEY_D_1_74 + 1)
+#define KEY_D_1_88 (KEY_D_1_79 + 1)
+#define KEY_D_1_90 (KEY_D_1_88 + 1)
+#define KEY_D_1_92 (KEY_D_1_90 + 1)
+#define KEY_D_1_96 (KEY_D_1_92 + 1)
+#define KEY_D_1_98 (KEY_D_1_96 + 1)
+#define KEY_D_1_100 (KEY_D_1_98 + 1)
+#define KEY_D_1_106 (KEY_D_1_100 + 1)
+#define KEY_D_1_108 (KEY_D_1_106 + 1)
+#define KEY_D_1_112 (KEY_D_1_108 + 1)
+#define KEY_D_1_128 (KEY_D_1_112 + 1)
+#define KEY_D_1_152 (KEY_D_1_128 + 1)
+#define KEY_D_1_160 (KEY_D_1_152 + 1)
+#define KEY_D_1_168 (KEY_D_1_160 + 1)
+#define KEY_D_1_175 (KEY_D_1_168 + 1)
+#define KEY_D_1_176 (KEY_D_1_175 + 1)
+#define KEY_D_1_178 (KEY_D_1_176 + 1)
+#define KEY_D_1_179 (KEY_D_1_178 + 1)
+#define KEY_D_1_218 (KEY_D_1_179 + 1)
+#define KEY_D_1_232 (KEY_D_1_218 + 1)
+#define KEY_D_1_236 (KEY_D_1_232 + 1)
+#define KEY_D_1_240 (KEY_D_1_236 + 1)
+#define KEY_D_1_244 (KEY_D_1_240 + 1)
+#define KEY_D_1_250 (KEY_D_1_244 + 1)
+#define KEY_D_1_252 (KEY_D_1_250 + 1)
+#define KEY_D_2_12 (KEY_D_1_252 + 1)
+#define KEY_D_2_96 (KEY_D_2_12 + 1)
+#define KEY_D_2_108 (KEY_D_2_96 + 1)
+#define KEY_D_2_208 (KEY_D_2_108 + 1)
+#define KEY_FLICK_MSG (KEY_D_2_208 + 1)
+#define KEY_FLICK_COUNTER (KEY_FLICK_MSG + 1)
+#define KEY_FLICK_LOWER (KEY_FLICK_COUNTER + 1)
+#define KEY_CFG_FLICK_IN (KEY_FLICK_LOWER + 1)
+#define KEY_FLICK_UPPER (KEY_CFG_FLICK_IN + 1)
+#define KEY_CGNOTICE_INTR (KEY_FLICK_UPPER + 1)
+#define KEY_D_2_224 (KEY_CGNOTICE_INTR + 1)
+#define KEY_D_2_244 (KEY_D_2_224 + 1)
+#define KEY_D_2_248 (KEY_D_2_244 + 1)
+#define KEY_D_2_252 (KEY_D_2_248 + 1)
+
+#define KEY_D_GYRO_BIAS_X (KEY_D_2_252 + 1)
+#define KEY_D_GYRO_BIAS_Y (KEY_D_GYRO_BIAS_X + 1)
+#define KEY_D_GYRO_BIAS_Z (KEY_D_GYRO_BIAS_Y + 1)
+#define KEY_D_GYRO_ENABLE (KEY_D_GYRO_BIAS_Z + 1)
+#define KEY_D_ACCEL_ENABLE (KEY_D_GYRO_ENABLE + 1)
+#define KEY_D_QUAT_ENABLE (KEY_D_ACCEL_ENABLE + 1)
+#define KEY_D_CR_TIME_G (KEY_D_QUAT_ENABLE + 1)
+#define KEY_D_CR_TIME_A (KEY_D_CR_TIME_G + 1)
+#define KEY_D_CR_TIME_Q (KEY_D_CR_TIME_A + 1)
+#define KEY_D_CS_TAX (KEY_D_CR_TIME_Q + 1)
+#define KEY_D_CS_TAY (KEY_D_CS_TAX + 1)
+#define KEY_D_CS_TAZ (KEY_D_CS_TAY + 1)
+
+#define KEY_D_CS_TGX (KEY_D_CS_TAZ + 1)
+#define KEY_D_CS_TGY (KEY_D_CS_TGX + 1)
+#define KEY_D_CS_TGZ (KEY_D_CS_TGY + 1)
+#define KEY_D_CS_TQ0 (KEY_D_CS_TGZ + 1)
+#define KEY_D_CS_TQ1 (KEY_D_CS_TQ0 + 1)
+#define KEY_D_CS_TQ2 (KEY_D_CS_TQ1 + 1)
+#define KEY_D_CS_TQ3 (KEY_D_CS_TQ2 + 1)
+
+/* Compass keys */
+#define KEY_CPASS_BIAS_X (KEY_D_CS_TQ3 + 1)
+#define KEY_CPASS_BIAS_Y (KEY_CPASS_BIAS_X + 1)
+#define KEY_CPASS_BIAS_Z (KEY_CPASS_BIAS_Y + 1)
+#define KEY_CPASS_MTX_00 (KEY_CPASS_BIAS_Z + 1)
+#define KEY_CPASS_MTX_01 (KEY_CPASS_MTX_00 + 1)
+#define KEY_CPASS_MTX_02 (KEY_CPASS_MTX_01 + 1)
+#define KEY_CPASS_MTX_10 (KEY_CPASS_MTX_02 + 1)
+#define KEY_CPASS_MTX_11 (KEY_CPASS_MTX_10 + 1)
+#define KEY_CPASS_MTX_12 (KEY_CPASS_MTX_11 + 1)
+#define KEY_CPASS_MTX_20 (KEY_CPASS_MTX_12 + 1)
+#define KEY_CPASS_MTX_21 (KEY_CPASS_MTX_20 + 1)
+#define KEY_CPASS_MTX_22 (KEY_CPASS_MTX_21 + 1)
+
+/* Gesture Keys */
+#define KEY_DMP_TAPW_MIN (KEY_CPASS_MTX_22 + 1)
+#define KEY_DMP_TAP_THR_X (KEY_DMP_TAPW_MIN + 1)
+#define KEY_DMP_TAP_THR_Y (KEY_DMP_TAP_THR_X + 1)
+#define KEY_DMP_TAP_THR_Z (KEY_DMP_TAP_THR_Y + 1)
+#define KEY_DMP_SH_TH_Y (KEY_DMP_TAP_THR_Z + 1)
+#define KEY_DMP_SH_TH_X (KEY_DMP_SH_TH_Y + 1)
+#define KEY_DMP_SH_TH_Z (KEY_DMP_SH_TH_X + 1)
+#define KEY_DMP_ORIENT (KEY_DMP_SH_TH_Z + 1)
+#define KEY_D_ACT0 (KEY_DMP_ORIENT + 1)
+#define KEY_D_ACSX (KEY_D_ACT0 + 1)
+#define KEY_D_ACSY (KEY_D_ACSX + 1)
+#define KEY_D_ACSZ (KEY_D_ACSY + 1)
+
+#define KEY_X_GRT_Y_TMP (KEY_D_ACSZ + 1)
+#define KEY_SKIP_X_GRT_Y_TMP (KEY_X_GRT_Y_TMP + 1)
+#define KEY_SKIP_END_COMPARE (KEY_SKIP_X_GRT_Y_TMP + 1)
+#define KEY_END_COMPARE_Y_X_TMP2 (KEY_SKIP_END_COMPARE + 1)
+#define KEY_CFG_DISPLAY_ORIENT_INT (KEY_END_COMPARE_Y_X_TMP2 + 1)
+#define KEY_NO_ORIENT_INTERRUPT (KEY_CFG_DISPLAY_ORIENT_INT + 1)
+#define KEY_END_COMPARE_Y_X_TMP (KEY_NO_ORIENT_INTERRUPT + 1)
+#define KEY_END_ORIENT_1 (KEY_END_COMPARE_Y_X_TMP + 1)
+#define KEY_END_COMPARE_Y_X (KEY_END_ORIENT_1 + 1)
+#define KEY_END_ORIENT (KEY_END_COMPARE_Y_X + 1)
+#define KEY_X_GRT_Y (KEY_END_ORIENT + 1)
+#define KEY_NOT_TIME_MINUS_1 (KEY_X_GRT_Y + 1)
+#define KEY_END_COMPARE_Y_X_TMP3 (KEY_NOT_TIME_MINUS_1 + 1)
+#define KEY_X_GRT_Y_TMP2 (KEY_END_COMPARE_Y_X_TMP3 + 1)
+
+/* Authenticate Keys */
+#define KEY_D_AUTH_OUT (KEY_X_GRT_Y_TMP2 + 1)
+#define KEY_D_AUTH_IN (KEY_D_AUTH_OUT + 1)
+#define KEY_D_AUTH_A (KEY_D_AUTH_IN + 1)
+#define KEY_D_AUTH_B (KEY_D_AUTH_A + 1)
+
+/* Pedometer standalone only keys */
+#define KEY_D_PEDSTD_BP_B (KEY_D_AUTH_B + 1)
+#define KEY_D_PEDSTD_HP_A (KEY_D_PEDSTD_BP_B + 1)
+#define KEY_D_PEDSTD_HP_B (KEY_D_PEDSTD_HP_A + 1)
+#define KEY_D_PEDSTD_BP_A4 (KEY_D_PEDSTD_HP_B + 1)
+#define KEY_D_PEDSTD_BP_A3 (KEY_D_PEDSTD_BP_A4 + 1)
+#define KEY_D_PEDSTD_BP_A2 (KEY_D_PEDSTD_BP_A3 + 1)
+#define KEY_D_PEDSTD_BP_A1 (KEY_D_PEDSTD_BP_A2 + 1)
+#define KEY_D_PEDSTD_INT_THRSH (KEY_D_PEDSTD_BP_A1 + 1)
+#define KEY_D_PEDSTD_CLIP (KEY_D_PEDSTD_INT_THRSH + 1)
+#define KEY_D_PEDSTD_SB (KEY_D_PEDSTD_CLIP + 1)
+#define KEY_D_PEDSTD_SB_TIME (KEY_D_PEDSTD_SB + 1)
+#define KEY_D_PEDSTD_PEAKTHRSH (KEY_D_PEDSTD_SB_TIME + 1)
+#define KEY_D_PEDSTD_TIML (KEY_D_PEDSTD_PEAKTHRSH + 1)
+#define KEY_D_PEDSTD_TIMH (KEY_D_PEDSTD_TIML + 1)
+#define KEY_D_PEDSTD_PEAK (KEY_D_PEDSTD_TIMH + 1)
+#define KEY_D_PEDSTD_TIMECTR (KEY_D_PEDSTD_PEAK + 1)
+#define KEY_D_PEDSTD_STEPCTR (KEY_D_PEDSTD_TIMECTR + 1)
+#define KEY_D_PEDSTD_WALKTIME (KEY_D_PEDSTD_STEPCTR + 1)
+#define KEY_D_PEDSTD_DECI (KEY_D_PEDSTD_WALKTIME + 1)
+
+/*Host Based No Motion*/
+#define KEY_D_HOST_NO_MOT (KEY_D_PEDSTD_DECI + 1)
+#define KEY_D_ACCEL_BIAS (KEY_D_HOST_NO_MOT + 1)
+
+#define NUM_KEYS (KEY_D_ACCEL_BIAS + 1)
+
+struct tKeyLabel {
+ unsigned short key;
+ unsigned short addr;
+};
+
+#define DINA0A 0x0a
+#define DINA22 0x22
+#define DINA42 0x42
+#define DINA5A 0x5a
+
+#define DINA06 0x06
+#define DINA0E 0x0e
+#define DINA16 0x16
+#define DINA1E 0x1e
+#define DINA26 0x26
+#define DINA2E 0x2e
+#define DINA36 0x36
+#define DINA3E 0x3e
+#define DINA46 0x46
+#define DINA4E 0x4e
+#define DINA56 0x56
+#define DINA5E 0x5e
+#define DINA66 0x66
+#define DINA6E 0x6e
+#define DINA76 0x76
+#define DINA7E 0x7e
+
+#define DINA00 0x00
+#define DINA08 0x08
+#define DINA10 0x10
+#define DINA18 0x18
+#define DINA20 0x20
+#define DINA28 0x28
+#define DINA30 0x30
+#define DINA38 0x38
+#define DINA40 0x40
+#define DINA48 0x48
+#define DINA50 0x50
+#define DINA58 0x58
+#define DINA60 0x60
+#define DINA68 0x68
+#define DINA70 0x70
+#define DINA78 0x78
+
+#define DINA04 0x04
+#define DINA0C 0x0c
+#define DINA14 0x14
+#define DINA1C 0x1C
+#define DINA24 0x24
+#define DINA2C 0x2c
+#define DINA34 0x34
+#define DINA3C 0x3c
+#define DINA44 0x44
+#define DINA4C 0x4c
+#define DINA54 0x54
+#define DINA5C 0x5c
+#define DINA64 0x64
+#define DINA6C 0x6c
+#define DINA74 0x74
+#define DINA7C 0x7c
+
+#define DINA01 0x01
+#define DINA09 0x09
+#define DINA11 0x11
+#define DINA19 0x19
+#define DINA21 0x21
+#define DINA29 0x29
+#define DINA31 0x31
+#define DINA39 0x39
+#define DINA41 0x41
+#define DINA49 0x49
+#define DINA51 0x51
+#define DINA59 0x59
+#define DINA61 0x61
+#define DINA69 0x69
+#define DINA71 0x71
+#define DINA79 0x79
+
+#define DINA25 0x25
+#define DINA2D 0x2d
+#define DINA35 0x35
+#define DINA3D 0x3d
+#define DINA4D 0x4d
+#define DINA55 0x55
+#define DINA5D 0x5D
+#define DINA6D 0x6d
+#define DINA75 0x75
+#define DINA7D 0x7d
+
+#define DINADC 0xdc
+#define DINAF2 0xf2
+#define DINAAB 0xab
+#define DINAAA 0xaa
+#define DINAF1 0xf1
+#define DINADF 0xdf
+#define DINADA 0xda
+#define DINAB1 0xb1
+#define DINAB9 0xb9
+#define DINAF3 0xf3
+#define DINA8B 0x8b
+#define DINAA3 0xa3
+#define DINA91 0x91
+#define DINAB6 0xb6
+#define DINAB4 0xb4
+
+
+#define DINC00 0x00
+#define DINC01 0x01
+#define DINC02 0x02
+#define DINC03 0x03
+#define DINC08 0x08
+#define DINC09 0x09
+#define DINC0A 0x0a
+#define DINC0B 0x0b
+#define DINC10 0x10
+#define DINC11 0x11
+#define DINC12 0x12
+#define DINC13 0x13
+#define DINC18 0x18
+#define DINC19 0x19
+#define DINC1A 0x1a
+#define DINC1B 0x1b
+
+#define DINC20 0x20
+#define DINC21 0x21
+#define DINC22 0x22
+#define DINC23 0x23
+#define DINC28 0x28
+#define DINC29 0x29
+#define DINC2A 0x2a
+#define DINC2B 0x2b
+#define DINC30 0x30
+#define DINC31 0x31
+#define DINC32 0x32
+#define DINC33 0x33
+#define DINC38 0x38
+#define DINC39 0x39
+#define DINC3A 0x3a
+#define DINC3B 0x3b
+
+#define DINC40 0x40
+#define DINC41 0x41
+#define DINC42 0x42
+#define DINC43 0x43
+#define DINC48 0x48
+#define DINC49 0x49
+#define DINC4A 0x4a
+#define DINC4B 0x4b
+#define DINC50 0x50
+#define DINC51 0x51
+#define DINC52 0x52
+#define DINC53 0x53
+#define DINC58 0x58
+#define DINC59 0x59
+#define DINC5A 0x5a
+#define DINC5B 0x5b
+
+#define DINC60 0x60
+#define DINC61 0x61
+#define DINC62 0x62
+#define DINC63 0x63
+#define DINC68 0x68
+#define DINC69 0x69
+#define DINC6A 0x6a
+#define DINC6B 0x6b
+#define DINC70 0x70
+#define DINC71 0x71
+#define DINC72 0x72
+#define DINC73 0x73
+#define DINC78 0x78
+#define DINC79 0x79
+#define DINC7A 0x7a
+#define DINC7B 0x7b
+#define DIND40 0x40
+#define DINA80 0x80
+#define DINA90 0x90
+#define DINAA0 0xa0
+#define DINAC9 0xc9
+#define DINACB 0xcb
+#define DINACD 0xcd
+#define DINACF 0xcf
+#define DINAC8 0xc8
+#define DINACA 0xca
+#define DINACC 0xcc
+#define DINACE 0xce
+#define DINAD8 0xd8
+#define DINADD 0xdd
+#define DINAF8 0xf0
+#define DINAFE 0xfe
+
+#define DINBF8 0xf8
+#define DINAC0 0xb0
+#define DINAC1 0xb1
+#define DINAC2 0xb4
+#define DINAC3 0xb5
+#define DINAC4 0xb8
+#define DINAC5 0xb9
+#define DINBC0 0xc0
+#define DINBC2 0xc2
+#define DINBC4 0xc4
+#define DINBC6 0xc6
+
+#endif
diff --git a/drivers/iio/imu/inv_mpu/dmpmap.h b/drivers/iio/imu/inv_mpu/dmpmap.h
new file mode 100644
index 00000000..28f59af0
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/dmpmap.h
@@ -0,0 +1,283 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file dmpmap.h
+ * @brief dmp map definition
+ * @details This file is part of invensense mpu driver code
+ *
+ */
+#ifndef DMPMAP_H
+#define DMPMAP_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define DMP_PTAT 0
+#define DMP_XGYR 2
+#define DMP_YGYR 4
+#define DMP_ZGYR 6
+#define DMP_XACC 8
+#define DMP_YACC 10
+#define DMP_ZACC 12
+#define DMP_ADC1 14
+#define DMP_ADC2 16
+#define DMP_ADC3 18
+#define DMP_BIASUNC 20
+#define DMP_FIFORT 22
+#define DMP_INVGSFH 24
+#define DMP_INVGSFL 26
+#define DMP_1H 28
+#define DMP_1L 30
+#define DMP_BLPFSTCH 32
+#define DMP_BLPFSTCL 34
+#define DMP_BLPFSXH 36
+#define DMP_BLPFSXL 38
+#define DMP_BLPFSYH 40
+#define DMP_BLPFSYL 42
+#define DMP_BLPFSZH 44
+#define DMP_BLPFSZL 46
+#define DMP_BLPFMTC 48
+#define DMP_SMC 50
+#define DMP_BLPFMXH 52
+#define DMP_BLPFMXL 54
+#define DMP_BLPFMYH 56
+#define DMP_BLPFMYL 58
+#define DMP_BLPFMZH 60
+#define DMP_BLPFMZL 62
+#define DMP_BLPFC 64
+#define DMP_SMCTH 66
+#define DMP_0H2 68
+#define DMP_0L2 70
+#define DMP_BERR2H 72
+#define DMP_BERR2L 74
+#define DMP_BERR2NH 76
+#define DMP_SMCINC 78
+#define DMP_ANGVBXH 80
+#define DMP_ANGVBXL 82
+#define DMP_ANGVBYH 84
+#define DMP_ANGVBYL 86
+#define DMP_ANGVBZH 88
+#define DMP_ANGVBZL 90
+#define DMP_BERR1H 92
+#define DMP_BERR1L 94
+#define DMP_ATCH 96
+#define DMP_BIASUNCSF 98
+#define DMP_ACT2H 100
+#define DMP_ACT2L 102
+#define DMP_GSFH 104
+#define DMP_GSFL 106
+#define DMP_GH 108
+#define DMP_GL 110
+#define DMP_0_5H 112
+#define DMP_0_5L 114
+#define DMP_0_0H 116
+#define DMP_0_0L 118
+#define DMP_1_0H 120
+#define DMP_1_0L 122
+#define DMP_1_5H 124
+#define DMP_1_5L 126
+#define DMP_TMP1AH 128
+#define DMP_TMP1AL 130
+#define DMP_TMP2AH 132
+#define DMP_TMP2AL 134
+#define DMP_TMP3AH 136
+#define DMP_TMP3AL 138
+#define DMP_TMP4AH 140
+#define DMP_TMP4AL 142
+#define DMP_XACCW 144
+#define DMP_TMP5 146
+#define DMP_XACCB 148
+#define DMP_TMP8 150
+#define DMP_YACCB 152
+#define DMP_TMP9 154
+#define DMP_ZACCB 156
+#define DMP_TMP10 158
+#define DMP_DZH 160
+#define DMP_DZL 162
+#define DMP_XGCH 164
+#define DMP_XGCL 166
+#define DMP_YGCH 168
+#define DMP_YGCL 170
+#define DMP_ZGCH 172
+#define DMP_ZGCL 174
+#define DMP_YACCW 176
+#define DMP_TMP7 178
+#define DMP_AFB1H 180
+#define DMP_AFB1L 182
+#define DMP_AFB2H 184
+#define DMP_AFB2L 186
+#define DMP_MAGFBH 188
+#define DMP_MAGFBL 190
+#define DMP_QT1H 192
+#define DMP_QT1L 194
+#define DMP_QT2H 196
+#define DMP_QT2L 198
+#define DMP_QT3H 200
+#define DMP_QT3L 202
+#define DMP_QT4H 204
+#define DMP_QT4L 206
+#define DMP_CTRL1H 208
+#define DMP_CTRL1L 210
+#define DMP_CTRL2H 212
+#define DMP_CTRL2L 214
+#define DMP_CTRL3H 216
+#define DMP_CTRL3L 218
+#define DMP_CTRL4H 220
+#define DMP_CTRL4L 222
+#define DMP_CTRLS1 224
+#define DMP_CTRLSF1 226
+#define DMP_CTRLS2 228
+#define DMP_CTRLSF2 230
+#define DMP_CTRLS3 232
+#define DMP_CTRLSFNLL 234
+#define DMP_CTRLS4 236
+#define DMP_CTRLSFNL2 238
+#define DMP_CTRLSFNL 240
+#define DMP_TMP30 242
+#define DMP_CTRLSFJT 244
+#define DMP_TMP31 246
+#define DMP_TMP11 248
+#define DMP_CTRLSF2_2 250
+#define DMP_TMP12 252
+#define DMP_CTRLSF1_2 254
+#define DMP_PREVPTAT 256
+#define DMP_ACCZB 258
+#define DMP_ACCXB 264
+#define DMP_ACCYB 266
+#define DMP_1HB 272
+#define DMP_1LB 274
+#define DMP_0H 276
+#define DMP_0L 278
+#define DMP_ASR22H 280
+#define DMP_ASR22L 282
+#define DMP_ASR6H 284
+#define DMP_ASR6L 286
+#define DMP_TMP13 288
+#define DMP_TMP14 290
+#define DMP_FINTXH 292
+#define DMP_FINTXL 294
+#define DMP_FINTYH 296
+#define DMP_FINTYL 298
+#define DMP_FINTZH 300
+#define DMP_FINTZL 302
+#define DMP_TMP1BH 304
+#define DMP_TMP1BL 306
+#define DMP_TMP2BH 308
+#define DMP_TMP2BL 310
+#define DMP_TMP3BH 312
+#define DMP_TMP3BL 314
+#define DMP_TMP4BH 316
+#define DMP_TMP4BL 318
+#define DMP_STXG 320
+#define DMP_ZCTXG 322
+#define DMP_STYG 324
+#define DMP_ZCTYG 326
+#define DMP_STZG 328
+#define DMP_ZCTZG 330
+#define DMP_CTRLSFJT2 332
+#define DMP_CTRLSFJTCNT 334
+#define DMP_PVXG 336
+#define DMP_TMP15 338
+#define DMP_PVYG 340
+#define DMP_TMP16 342
+#define DMP_PVZG 344
+#define DMP_TMP17 346
+#define DMP_MNMFLAGH 352
+#define DMP_MNMFLAGL 354
+#define DMP_MNMTMH 356
+#define DMP_MNMTML 358
+#define DMP_MNMTMTHRH 360
+#define DMP_MNMTMTHRL 362
+#define DMP_MNMTHRH 364
+#define DMP_MNMTHRL 366
+#define DMP_ACCQD4H 368
+#define DMP_ACCQD4L 370
+#define DMP_ACCQD5H 372
+#define DMP_ACCQD5L 374
+#define DMP_ACCQD6H 376
+#define DMP_ACCQD6L 378
+#define DMP_ACCQD7H 380
+#define DMP_ACCQD7L 382
+#define DMP_ACCQD0H 384
+#define DMP_ACCQD0L 386
+#define DMP_ACCQD1H 388
+#define DMP_ACCQD1L 390
+#define DMP_ACCQD2H 392
+#define DMP_ACCQD2L 394
+#define DMP_ACCQD3H 396
+#define DMP_ACCQD3L 398
+#define DMP_XN2H 400
+#define DMP_XN2L 402
+#define DMP_XN1H 404
+#define DMP_XN1L 406
+#define DMP_YN2H 408
+#define DMP_YN2L 410
+#define DMP_YN1H 412
+#define DMP_YN1L 414
+#define DMP_YH 416
+#define DMP_YL 418
+#define DMP_B0H 420
+#define DMP_B0L 422
+#define DMP_A1H 424
+#define DMP_A1L 426
+#define DMP_A2H 428
+#define DMP_A2L 430
+#define DMP_SEM1 432
+#define DMP_FIFOCNT 434
+#define DMP_SH_TH_X 436
+#define DMP_PACKET 438
+#define DMP_SH_TH_Y 440
+#define DMP_FOOTER 442
+#define DMP_SH_TH_Z 444
+#define DMP_TEMP29 448
+#define DMP_TEMP30 450
+#define DMP_XACCB_PRE 452
+#define DMP_XACCB_PREL 454
+#define DMP_YACCB_PRE 456
+#define DMP_YACCB_PREL 458
+#define DMP_ZACCB_PRE 460
+#define DMP_ZACCB_PREL 462
+#define DMP_TMP22 464
+#define DMP_TAP_TIMER 466
+#define DMP_TAP_THX 468
+#define DMP_TAP_THY 472
+#define DMP_TAP_THZ 476
+#define DMP_TAPW_MIN 478
+#define DMP_TMP25 480
+#define DMP_TMP26 482
+#define DMP_TMP27 484
+#define DMP_TMP28 486
+#define DMP_ORIENT 488
+#define DMP_THRSH 490
+#define DMP_ENDIANH 492
+#define DMP_ENDIANL 494
+#define DMP_BLPFNMTCH 496
+#define DMP_BLPFNMTCL 498
+#define DMP_BLPFNMXH 500
+#define DMP_BLPFNMXL 502
+#define DMP_BLPFNMYH 504
+#define DMP_BLPFNMYL 506
+#define DMP_BLPFNMZH 508
+#define DMP_BLPFNMZL 510
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/drivers/iio/imu/inv_mpu/inv_counters.h b/drivers/iio/imu/inv_mpu/inv_counters.h
new file mode 100644
index 00000000..35a802b0
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_counters.h
@@ -0,0 +1,72 @@
+/*
+ * @file inv_counters.h
+ * @brief Debug file to keep track of various counters for the InvenSense
+ * sensor drivers.
+ *
+ * @version 0.1
+ */
+
+#ifndef _INV_COUNTERS_H_
+#define _INV_COUNTERS_H_
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+
+enum irqtype {
+ MPU,
+ ACCEL,
+ COMPASS
+};
+
+#ifdef CONFIG_INV_TESTING
+
+#define INV_I2C_INC_MPUREAD(x) inv_iio_counters_mpuread(x)
+#define INV_I2C_INC_MPUWRITE(x) inv_iio_counters_mpuwrite(x)
+#define INV_I2C_INC_ACCELREAD(x) inv_iio_counters_accelread(x)
+#define INV_I2C_INC_ACCELWRITE(x) inv_iio_counters_accelwrite(x)
+#define INV_I2C_INC_COMPASSREAD(x) inv_iio_counters_compassread(x)
+#define INV_I2C_INC_COMPASSWRITE(x) inv_iio_counters_compasswrite(x)
+
+#define INV_I2C_INC_TEMPREAD(x) inv_iio_counters_tempread(x)
+
+#define INV_I2C_SETIRQ(type, irq) inv_iio_counters_set_i2cirq(type, irq)
+#define INV_I2C_INC_COMPASSIRQ() inv_iio_counters_compassirq()
+#define INV_I2C_INC_ACCELIRQ() inv_iio_counters_accelirq()
+
+void inv_iio_counters_mpuread(int count);
+void inv_iio_counters_mpuwrite(int count);
+void inv_iio_counters_accelread(int count);
+void inv_iio_counters_accelwrite(int count);
+void inv_iio_counters_compassread(int count);
+void inv_iio_counters_compasswrite(int count);
+
+void inv_iio_counters_tempread(int count);
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq);
+void inv_iio_counters_compassirq(void);
+void inv_iio_counters_accelirq(void);
+
+#else
+
+#define INV_I2C_INC_MPUREAD(x)
+#define INV_I2C_INC_MPUWRITE(x)
+#define INV_I2C_INC_ACCELREAD(x)
+#define INV_I2C_INC_ACCELWRITE(x)
+#define INV_I2C_INC_COMPASSREAD(x)
+#define INV_I2C_INC_COMPASSWRITE(x)
+
+#define INV_I2C_INC_TEMPREAD(x)
+
+#define INV_I2C_SETIRQ(type, irq)
+#define INV_I2C_INC_COMPASSIRQ()
+#define INV_I2C_INC_ACCELIRQ()
+
+#endif /* CONFIG_INV_TESTING */
+
+#endif /* _INV_COUNTERS_H_ */
+
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu3050_iio.c b/drivers/iio/imu/inv_mpu/inv_mpu3050_iio.c
new file mode 100644
index 00000000..920c9dde
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu3050_iio.c
@@ -0,0 +1,292 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_mpu3050_iio.c
+ * @brief A sysfs device driver for Invensense devices
+ * @details This file is part of invensense mpu driver code
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#define MPU3050_NACK_MIN_TIME (2 * 1000)
+#define MPU3050_NACK_MAX_TIME (3 * 1000)
+
+#define MPU3050_ONE_MPU_TIME 20
+#define MPU3050_BOGUS_ADDR 0x7F
+int __attribute__((weak)) inv_register_mpu3050_slave(struct inv_mpu_iio_s *st)
+{
+ return 0;
+}
+
+int set_3050_bypass(struct inv_mpu_iio_s *st, bool enable)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ u8 b;
+
+ reg = &st->reg;
+ result = inv_i2c_read(st, reg->user_ctrl, 1, &b);
+ if (result)
+ return result;
+ if (((b & BIT_3050_AUX_IF_EN) == 0) && enable)
+ return 0;
+ if ((b & BIT_3050_AUX_IF_EN) && (enable == 0))
+ return 0;
+ b &= ~BIT_3050_AUX_IF_EN;
+ if (!enable) {
+ b |= BIT_3050_AUX_IF_EN;
+ result = inv_i2c_single_write(st, reg->user_ctrl, b);
+ return result;
+ } else {
+ /* Coming out of I2C is tricky due to several erratta. Do not
+ * modify this algorithm
+ */
+ /*
+ * 1) wait for the right time and send the command to change
+ * the aux i2c slave address to an invalid address that will
+ * get nack'ed
+ *
+ * 0x00 is broadcast. 0x7F is unlikely to be used by any aux.
+ */
+ result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+ MPU3050_BOGUS_ADDR);
+ if (result)
+ return result;
+ /*
+ * 2) wait enough time for a nack to occur, then go into
+ * bypass mode:
+ */
+ usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+ result = inv_i2c_single_write(st, reg->user_ctrl, b);
+ if (result)
+ return result;
+ /*
+ * 3) wait for up to one MPU cycle then restore the slave
+ * address
+ */
+ msleep(MPU3050_ONE_MPU_TIME);
+
+ result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+ st->plat_data.secondary_i2c_addr);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, b);
+ if (result)
+ return result;
+ usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+ }
+ return 0;
+}
+
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg)
+{
+ reg->fifo_en = REG_3050_FIFO_EN;
+ reg->sample_rate_div = REG_3050_SAMPLE_RATE_DIV;
+ reg->lpf = REG_3050_LPF;
+ reg->fifo_count_h = REG_3050_FIFO_COUNT_H;
+ reg->fifo_r_w = REG_3050_FIFO_R_W;
+ reg->user_ctrl = REG_3050_USER_CTRL;
+ reg->pwr_mgmt_1 = REG_3050_PWR_MGMT_1;
+ reg->raw_gyro = REG_3050_RAW_GYRO;
+ reg->raw_accl = REG_3050_AUX_XOUT_H;
+ reg->temperature = REG_3050_TEMPERATURE;
+ reg->int_enable = REG_3050_INT_ENABLE;
+ reg->int_status = REG_3050_INT_STATUS;
+}
+
+int inv_switch_3050_gyro_engine(struct inv_mpu_iio_s *st, bool en)
+{
+ struct inv_reg_map_s *reg;
+ u8 data, p;
+ int result;
+ reg = &st->reg;
+ if (en) {
+ data = INV_CLK_PLL;
+ p = (BITS_3050_POWER1 | data);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ if (result)
+ return result;
+ p = (BITS_3050_POWER2 | data);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ if (result)
+ return result;
+ p = data;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ msleep(SENSOR_UP_TIME);
+ } else {
+ p = BITS_3050_GYRO_STANDBY;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ }
+
+ return result;
+}
+
+int inv_switch_3050_accl_engine(struct inv_mpu_iio_s *st, bool en)
+{
+ int result;
+ if (NULL == st->mpu_slave)
+ return -EPERM;
+ if (en)
+ result = st->mpu_slave->resume(st);
+ else
+ result = st->mpu_slave->suspend(st);
+
+ return result;
+}
+
+/**
+ * inv_init_config_mpu3050() - Initialize hardware, disable FIFO.
+ * @st: Device driver instance.
+ * Initial configuration:
+ * FSR: +/- 2000DPS
+ * DLPF: 42Hz
+ * FIFO rate: 50Hz
+ * Clock source: Gyro PLL
+ */
+int inv_init_config_mpu3050(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ u8 data;
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+ if (st->chip_config.is_asleep)
+ return -EPERM;
+ /*reading AUX VDDIO register */
+ result = inv_i2c_read(st, REG_3050_AUX_VDDIO, 1, &data);
+ if (result)
+ return result;
+ data &= ~BIT_3050_VDDIO;
+ if (st->plat_data.level_shifter)
+ data |= BIT_3050_VDDIO;
+ result = inv_i2c_single_write(st, REG_3050_AUX_VDDIO, data);
+ if (result)
+ return result;
+
+ reg = &st->reg;
+ result = set_inv_enable(indio_dev, false);
+ if (result)
+ return result;
+ /*2000dps full scale range*/
+ result = inv_i2c_single_write(st, reg->lpf,
+ (INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT)
+ | INV_FILTER_42HZ);
+ if (result)
+ return result;
+ st->chip_config.fsr = INV_FSR_2000DPS;
+ st->chip_config.lpf = INV_FILTER_42HZ;
+ result = inv_i2c_single_write(st, reg->sample_rate_div,
+ ONE_K_HZ/INIT_FIFO_RATE - 1);
+ if (result)
+ return result;
+ st->chip_config.fifo_rate = INIT_FIFO_RATE;
+ st->irq_dur_ns = INIT_DUR_TIME;
+ st->chip_config.prog_start_addr = DMP_START_ADDR;
+ st->chip_config.gyro_enable = 1;
+ st->chip_config.gyro_fifo_enable = 1;
+ if ((SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) &&
+ st->mpu_slave) {
+ result = st->mpu_slave->setup(st);
+ if (result)
+ return result;
+ result = st->mpu_slave->set_fs(st, INV_FS_02G);
+ if (result)
+ return result;
+ result = st->mpu_slave->set_lpf(st, INIT_FIFO_RATE);
+ if (result)
+ return result;
+ st->chip_config.accl_enable = 1;
+ st->chip_config.accl_fifo_enable = 1;
+ }
+
+ return 0;
+}
+
+/**
+ * set_power_mpu3050() - set power of mpu3050.
+ * @st: Device driver instance.
+ * @power_on: on/off
+ */
+int set_power_mpu3050(struct inv_mpu_iio_s *st, bool power_on)
+{
+ struct inv_reg_map_s *reg;
+ u8 data, p;
+ int result;
+ reg = &st->reg;
+ if (power_on) {
+ data = 0;
+ } else {
+ if (st->mpu_slave) {
+ result = st->mpu_slave->suspend(st);
+ if (result)
+ return result;
+ }
+ data = BIT_SLEEP;
+ }
+ if (st->chip_config.gyro_enable) {
+ p = (BITS_3050_POWER1 | INV_CLK_PLL);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+ if (result)
+ return result;
+
+ p = (BITS_3050_POWER2 | INV_CLK_PLL);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+ if (result)
+ return result;
+
+ p = INV_CLK_PLL;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+ if (result)
+ return result;
+ } else {
+ data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+ if (result)
+ return result;
+ }
+ if (power_on) {
+ msleep(POWER_UP_TIME);
+ if (st->mpu_slave) {
+ result = st->mpu_slave->resume(st);
+ if (result)
+ return result;
+ }
+ }
+ st->chip_config.is_asleep = !power_on;
+
+ return 0;
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_core.c b/drivers/iio/imu/inv_mpu/inv_mpu_core.c
new file mode 100644
index 00000000..e547ab77
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_core.c
@@ -0,0 +1,2078 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_mpu_core.c
+ * @brief A sysfs device driver for Invensense devices
+ * @details This driver currently works for the
+ * MPU3050/MPU6050/MPU9150/MPU6500/MPU9250 devices.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/iio/sysfs.h>
+
+#include "inv_mpu_iio.h"
+#include "inv_counters.h"
+
+#include <linux/regulator/consumer.h>
+#include <mach/regulator.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+s64 get_time_ns(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+ return timespec_to_ns(&ts);
+}
+
+static const short AKM8975_ST_Lower[3] = {-100, -100, -1000};
+static const short AKM8975_ST_Upper[3] = {100, 100, -300};
+
+static const short AKM8972_ST_Lower[3] = {-50, -50, -500};
+static const short AKM8972_ST_Upper[3] = {50, 50, -100};
+
+static const short AKM8963_ST_Lower[3] = {-200, -200, -3200};
+static const short AKM8963_ST_Upper[3] = {200, 200, -800};
+
+static const struct inv_hw_s hw_info[INV_NUM_PARTS] = {
+ {119, "ITG3500"},
+ { 63, "MPU3050"},
+ {117, "MPU6050"},
+ {118, "MPU9150"},
+ {119, "MPU6500"},
+ {118, "MPU9250"},
+};
+
+static void inv_setup_reg(struct inv_reg_map_s *reg)
+{
+ reg->sample_rate_div = REG_SAMPLE_RATE_DIV;
+ reg->lpf = REG_CONFIG;
+ reg->bank_sel = REG_BANK_SEL;
+ reg->user_ctrl = REG_USER_CTRL;
+ reg->fifo_en = REG_FIFO_EN;
+ reg->gyro_config = REG_GYRO_CONFIG;
+ reg->accl_config = REG_ACCEL_CONFIG;
+ reg->fifo_count_h = REG_FIFO_COUNT_H;
+ reg->fifo_r_w = REG_FIFO_R_W;
+ reg->raw_gyro = REG_RAW_GYRO;
+ reg->raw_accl = REG_RAW_ACCEL;
+ reg->temperature = REG_TEMPERATURE;
+ reg->int_enable = REG_INT_ENABLE;
+ reg->int_status = REG_INT_STATUS;
+ reg->pwr_mgmt_1 = REG_PWR_MGMT_1;
+ reg->pwr_mgmt_2 = REG_PWR_MGMT_2;
+ reg->mem_start_addr = REG_MEM_START_ADDR;
+ reg->mem_r_w = REG_MEM_RW;
+ reg->prgm_strt_addrh = REG_PRGM_STRT_ADDRH;
+};
+
+/**
+ * inv_i2c_read() - Read one or more bytes from the device registers.
+ * @st: Device driver instance.
+ * @reg: First device register to be read from.
+ * @length: Number of bytes to read.
+ * @data: Data read from device.
+ * NOTE:This is not re-implementation of i2c_smbus_read because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_read_base(struct inv_mpu_iio_s *st, u16 i2c_addr,
+ u8 reg, u16 length, u8 *data)
+{
+ struct i2c_msg msgs[2];
+ int res;
+
+ if (!data)
+ return -EINVAL;
+
+ msgs[0].addr = i2c_addr;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = &reg;
+ msgs[0].len = 1;
+
+ msgs[1].addr = i2c_addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].buf = data;
+ msgs[1].len = length;
+
+ res = i2c_transfer(st->sl_handle, msgs, 2);
+
+ if (res < 2) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+
+ INV_I2C_INC_MPUWRITE(3);
+ INV_I2C_INC_MPUREAD(length);
+#if CONFIG_DYNAMIC_DEBUG
+ {
+ char *read = 0;
+ pr_debug("%s RD%02X%02X%02X -> %s%s\n", st->hw->name,
+ i2c_addr, reg, length,
+ wr_pr_debug_begin(data, length, read),
+ wr_pr_debug_end(read));
+ }
+#endif
+ return res;
+}
+
+/**
+ * inv_i2c_single_write() - Write a byte to a device register.
+ * @st: Device driver instance.
+ * @reg: Device register to be written to.
+ * @data: Byte to write to device.
+ * NOTE:This is not re-implementation of i2c_smbus_write because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
+ u16 i2c_addr, u8 reg, u8 data)
+{
+ u8 tmp[2];
+ struct i2c_msg msg;
+ int res;
+
+ tmp[0] = reg;
+ tmp[1] = data;
+
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 2;
+
+ pr_debug("%s WR%02X%02X%02X\n", st->hw->name, i2c_addr, reg, data);
+ INV_I2C_INC_MPUWRITE(3);
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int inv_switch_engine(struct inv_mpu_iio_s *st, bool en, u32 mask)
+{
+ struct inv_reg_map_s *reg;
+ u8 data, mgmt_1;
+ int result;
+ reg = &st->reg;
+ /* switch clock needs to be careful. Only when gyro is on, can
+ clock source be switched to gyro. Otherwise, it must be set to
+ internal clock */
+ if (BIT_PWR_GYRO_STBY == mask) {
+ result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &mgmt_1);
+ if (result)
+ return result;
+
+ mgmt_1 &= ~BIT_CLK_MASK;
+ }
+
+ if ((BIT_PWR_GYRO_STBY == mask) && (!en)) {
+ /* turning off gyro requires switch to internal clock first.
+ Then turn off gyro engine */
+ mgmt_1 |= INV_CLK_INTERNAL;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1,
+ mgmt_1);
+ if (result)
+ return result;
+ }
+
+ result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &data);
+ if (result)
+ return result;
+ if (en)
+ data &= (~mask);
+ else
+ data |= mask;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data);
+ if (result)
+ return result;
+
+ if ((BIT_PWR_GYRO_STBY == mask) && en) {
+ /* only gyro on needs sensor up time */
+ msleep(SENSOR_UP_TIME);
+ /* after gyro is on & stable, switch internal clock to PLL */
+ mgmt_1 |= INV_CLK_PLL;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1,
+ mgmt_1);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * inv_lpa_freq() - store current low power frequency setting.
+ */
+static int inv_lpa_freq(struct inv_mpu_iio_s *st, int lpa_freq)
+{
+ unsigned long result;
+ u8 d;
+ struct inv_reg_map_s *reg;
+ /* this mapping makes 6500 and 6050 setting close */
+ /* 2, 4, 6, 7 corresponds to 0.98, 3.91, 15.63, 31.25 */
+ const u8 mpu6500_lpa_mapping[] = {2, 4, 6, 7};
+
+ if (lpa_freq > MAX_LPA_FREQ_PARAM)
+ return -EINVAL;
+
+ if (INV_MPU6500 == st->chip_type) {
+ d = mpu6500_lpa_mapping[lpa_freq];
+ result = inv_i2c_single_write(st, REG_6500_LP_ACCEL_ODR, d);
+ if (result)
+ return result;
+ } else {
+ reg = &st->reg;
+ result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &d);
+ if (result)
+ return result;
+ d &= ~BIT_LPA_FREQ;
+ d |= (u8)(lpa_freq << LPA_FREQ_SHIFT);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_2, d);
+ if (result)
+ return result;
+ }
+ st->chip_config.lpa_freq = lpa_freq;
+
+ return 0;
+}
+
+static int set_power_itg(struct inv_mpu_iio_s *st, bool power_on)
+{
+ struct inv_reg_map_s *reg;
+ u8 data;
+ int result;
+
+ reg = &st->reg;
+ if (power_on)
+ data = 0;
+ else
+ data = BIT_SLEEP;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+ if (result)
+ return result;
+
+ if (power_on) {
+ msleep(POWER_UP_TIME);
+ result = inv_switch_engine(st, st->chip_config.gyro_enable,
+ BIT_PWR_GYRO_STBY);
+ if (result)
+ return result;
+ result = inv_switch_engine(st, st->chip_config.accl_enable,
+ BIT_PWR_ACCL_STBY);
+ if (result)
+ return result;
+ result = inv_lpa_freq(st, st->chip_config.lpa_freq);
+ if (result)
+ return result;
+ }
+ st->chip_config.is_asleep = !power_on;
+
+ return 0;
+}
+
+/**
+ * inv_init_config() - Initialize hardware, disable FIFO.
+ * @indio_dev: Device driver instance.
+ * Initial configuration:
+ * FSR: +/- 2000DPS
+ * DLPF: 42Hz
+ * FIFO rate: 50Hz
+ */
+static int inv_init_config(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+ if (st->chip_config.is_asleep)
+ return -EPERM;
+
+ reg = &st->reg;
+ result = set_inv_enable(indio_dev, false);
+ if (result)
+ return result;
+
+ result = inv_i2c_single_write(st, reg->gyro_config,
+ INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT);
+ if (result)
+ return result;
+
+ st->chip_config.fsr = INV_FSR_2000DPS;
+
+ result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ);
+ if (result)
+ return result;
+ st->chip_config.lpf = INV_FILTER_42HZ;
+
+ result = inv_i2c_single_write(st, reg->sample_rate_div,
+ ONE_K_HZ / INIT_FIFO_RATE - 1);
+ if (result)
+ return result;
+ st->chip_config.fifo_rate = INIT_FIFO_RATE;
+ st->irq_dur_ns = INIT_DUR_TIME;
+ st->chip_config.prog_start_addr = DMP_START_ADDR;
+ st->chip_config.gyro_enable = 1;
+ st->chip_config.gyro_fifo_enable = 1;
+ st->chip_config.dmp_output_rate = INIT_DMP_OUTPUT_RATE;
+ if (INV_ITG3500 != st->chip_type) {
+ st->chip_config.accl_enable = 1;
+ st->chip_config.accl_fifo_enable = 1;
+ st->chip_config.accl_fs = INV_FS_02G;
+ result = inv_i2c_single_write(st, reg->accl_config,
+ (INV_FS_02G << ACCL_CONFIG_FSR_SHIFT));
+ if (result)
+ return result;
+ st->tap.time = INIT_TAP_TIME;
+ st->tap.thresh = INIT_TAP_THRESHOLD;
+ st->tap.min_count = INIT_TAP_MIN_COUNT;
+
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_DUR,
+ INIT_MOT_DUR);
+ if (result)
+ return result;
+ st->mot_int.mot_dur = INIT_MOT_DUR;
+
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR,
+ INIT_MOT_THR);
+ if (result)
+ return result;
+ st->mot_int.mot_thr = INIT_MOT_THR;
+ }
+
+ return 0;
+}
+
+/**
+ * inv_compass_scale_show() - show compass scale.
+ */
+static int inv_compass_scale_show(struct inv_mpu_iio_s *st, int *scale)
+{
+ if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id)
+ *scale = DATA_AKM8975_SCALE;
+ else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id)
+ *scale = DATA_AKM8972_SCALE;
+ else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id)
+ if (st->compass_scale)
+ *scale = DATA_AKM8963_SCALE1;
+ else
+ *scale = DATA_AKM8963_SCALE0;
+ else
+ return -EINVAL;
+
+ return IIO_VAL_INT;
+}
+
+/**
+ * mpu_read_raw() - read raw method.
+ */
+static int mpu_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ int result;
+ if (st->chip_config.is_asleep)
+ return -EINVAL;
+ switch (mask) {
+ case 0:
+ if (!st->chip_config.enable)
+ return -EPERM;
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ if (!st->chip_config.gyro_enable)
+ return -EPERM;
+ *val = st->raw_gyro[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ case IIO_ACCEL:
+ if (!st->chip_config.accl_enable)
+ return -EPERM;
+ *val = st->raw_accel[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ case IIO_MAGN:
+ if (!st->chip_config.compass_enable)
+ return -EPERM;
+ *val = st->raw_compass[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ case IIO_QUATERNION:
+ if (!(st->chip_config.dmp_on
+ && st->chip_config.quaternion_on))
+ return -EPERM;
+ if (IIO_MOD_R == chan->channel2)
+ *val = st->raw_quaternion[0];
+ else
+ *val = st->raw_quaternion[chan->channel2 -
+ IIO_MOD_X + 1];
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ {
+ const s16 gyro_scale[] = {250, 500, 1000, 2000};
+
+ *val = gyro_scale[st->chip_config.fsr];
+
+ return IIO_VAL_INT;
+ }
+ case IIO_ACCEL:
+ {
+ const s16 accel_scale[] = {2, 4, 8, 16};
+ *val = accel_scale[st->chip_config.accl_fs];
+ return IIO_VAL_INT;
+ }
+ case IIO_MAGN:
+ return inv_compass_scale_show(st, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ /* return bias=0 for both accel and gyro for MPU6500;
+ self test not supported yet */
+ if (INV_MPU6500 == st->chip_type) {
+ *val = 0;
+ return IIO_VAL_INT;
+ }
+ if (st->chip_config.self_test_run_once == 0) {
+ result = inv_do_test(st, 0, st->gyro_bias,
+ st->accel_bias);
+ /* Reset Accel and Gyro full scale range
+ back to default value */
+ inv_recover_setting(st);
+ if (result)
+ return result;
+ st->chip_config.self_test_run_once = 1;
+ }
+
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val = st->gyro_bias[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ case IIO_ACCEL:
+ *val = st->accel_bias[chan->channel2 - IIO_MOD_X] *
+ st->chip_info.multi;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ *val = st->input_accel_bias[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * inv_write_fsr() - Configure the gyro's scale range.
+ */
+static int inv_write_fsr(struct inv_mpu_iio_s *st, int fsr)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ reg = &st->reg;
+ if ((fsr < 0) || (fsr > MAX_GYRO_FS_PARAM))
+ return -EINVAL;
+ if (fsr == st->chip_config.fsr)
+ return 0;
+
+ if (INV_MPU3050 == st->chip_type)
+ result = inv_i2c_single_write(st, reg->lpf,
+ (fsr << GYRO_CONFIG_FSR_SHIFT) | st->chip_config.lpf);
+ else
+ result = inv_i2c_single_write(st, reg->gyro_config,
+ fsr << GYRO_CONFIG_FSR_SHIFT);
+
+ if (result)
+ return result;
+ st->chip_config.fsr = fsr;
+
+ return 0;
+}
+
+/**
+ * inv_write_accel_fs() - Configure the accelerometer's scale range.
+ */
+static int inv_write_accel_fs(struct inv_mpu_iio_s *st, int fs)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+ reg = &st->reg;
+
+ if (fs < 0 || fs > MAX_ACCL_FS_PARAM)
+ return -EINVAL;
+ if (fs == st->chip_config.accl_fs)
+ return 0;
+ if (INV_MPU3050 == st->chip_type)
+ result = st->mpu_slave->set_fs(st, fs);
+ else
+ result = inv_i2c_single_write(st, reg->accl_config,
+ (fs << ACCL_CONFIG_FSR_SHIFT));
+ if (result)
+ return result;
+
+ st->chip_config.accl_fs = fs;
+
+ return 0;
+}
+
+/**
+ * inv_write_compass_scale() - Configure the compass's scale range.
+ */
+static int inv_write_compass_scale(struct inv_mpu_iio_s *st, int data)
+{
+ char d, en;
+ int result;
+ if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id)
+ return 0;
+ en = !!data;
+ if (st->compass_scale == en)
+ return 0;
+ d = (DATA_AKM_MODE_SM | (st->compass_scale << AKM8963_SCALE_SHIFT));
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, d);
+ if (result)
+ return result;
+ st->compass_scale = en;
+
+ return 0;
+}
+
+static inline int check_enable(struct inv_mpu_iio_s *st)
+{
+ return st->chip_config.is_asleep | st->chip_config.enable;
+}
+
+static inline int check_dmp_on(struct inv_mpu_iio_s *st)
+{
+ return (!st->chip_config.is_asleep) &&
+ st->chip_config.enable && st->chip_config.dmp_on;
+}
+
+/**
+ * mpu_write_raw() - write raw method.
+ */
+static int mpu_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask) {
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ int result;
+ if (check_enable(st))
+ return -EPERM;
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ return inv_write_fsr(st, val);
+ case IIO_ACCEL:
+ return inv_write_accel_fs(st, val);
+ case IIO_MAGN:
+ return inv_write_compass_scale(st, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ if (!st->chip_config.firmware_loaded)
+ return -EPERM;
+ result = inv_set_accel_bias_dmp(st);
+ if (result)
+ return result;
+ st->input_accel_bias[chan->channel2 - IIO_MOD_X] = val;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * inv_set_lpf() - set low pass filer based on fifo rate.
+ */
+static int inv_set_lpf(struct inv_mpu_iio_s *st, int rate)
+{
+ const short hz[] = {188, 98, 42, 20, 10, 5};
+ const int d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ,
+ INV_FILTER_42HZ, INV_FILTER_20HZ,
+ INV_FILTER_10HZ, INV_FILTER_5HZ};
+ int i, h, data, result;
+ struct inv_reg_map_s *reg;
+ reg = &st->reg;
+ h = (rate >> 1);
+ i = 0;
+ while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
+ i++;
+ data = d[i];
+ if (INV_MPU3050 == st->chip_type) {
+ if (st->mpu_slave != NULL) {
+ result = st->mpu_slave->set_lpf(st, rate);
+ if (result)
+ return result;
+ }
+ result = inv_i2c_single_write(st, reg->lpf, data |
+ (st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT));
+ } else {
+ result = inv_i2c_single_write(st, reg->lpf, data);
+ }
+ if (result)
+ return result;
+ st->chip_config.lpf = data;
+
+ return 0;
+}
+
+/**
+ * inv_fifo_rate_store() - Set fifo rate.
+ */
+static ssize_t inv_fifo_rate_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ u32 fifo_rate;
+ u8 data;
+ int result;
+ struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+ struct inv_reg_map_s *reg;
+ reg = &st->reg;
+
+ if (check_enable(st))
+ return -EPERM;
+ if (kstrtouint(buf, 10, &fifo_rate))
+ return -EINVAL;
+ if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE))
+ return -EINVAL;
+ if (fifo_rate == st->chip_config.fifo_rate)
+ return count;
+ if (st->chip_config.has_compass) {
+ st->compass_divider = COMPASS_RATE_SCALE * fifo_rate /
+ ONE_K_HZ;
+ if (st->compass_divider > 0)
+ st->compass_divider -= 1;
+ st->compass_counter = 0;
+ }
+ data = ONE_K_HZ / fifo_rate - 1;
+ result = inv_i2c_single_write(st, reg->sample_rate_div, data);
+ if (result)
+ return result;
+ st->chip_config.fifo_rate = fifo_rate;
+ result = inv_set_lpf(st, fifo_rate);
+ if (result)
+ return result;
+ st->irq_dur_ns = (data + 1) * NSEC_PER_MSEC;
+
+ return count;
+}
+
+/**
+ * inv_fifo_rate_show() - Get the current sampling rate.
+ */
+static ssize_t inv_fifo_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+
+ return sprintf(buf, "%d\n", st->chip_config.fifo_rate);
+}
+
+/**
+ * inv_power_state_store() - Turn device on/off.
+ */
+static ssize_t inv_power_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int result;
+ u32 power_state;
+ struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+ if (kstrtouint(buf, 10, &power_state))
+ return -EINVAL;
+ if ((!power_state) == st->chip_config.is_asleep)
+ return count;
+ result = st->set_power_state(st, power_state);
+
+ return count;
+}
+
+/**
+ * inv_reg_dump_show() - Register dump for testing.
+ */
+static ssize_t inv_reg_dump_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ii;
+ char data;
+ ssize_t bytes_printed = 0;
+ struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+
+ for (ii = 0; ii < st->hw->num_reg; ii++) {
+ /* don't read fifo r/w register */
+ if (ii == st->reg.fifo_r_w)
+ data = 0;
+ else
+ inv_i2c_read(st, ii, 1, &data);
+ bytes_printed += sprintf(buf + bytes_printed, "%#2x: %#2x\n",
+ ii, data);
+ }
+
+ return bytes_printed;
+}
+
+int write_be32_key_to_mem(struct inv_mpu_iio_s *st,
+ u32 data, int key)
+{
+ cpu_to_be32s(&data);
+ return mem_w_key(key, sizeof(data), (u8 *)&data);
+}
+
+/**
+ * inv_dmp_attr_store() - calling this function will store current
+ * dmp parameter settings
+ */
+static ssize_t inv_dmp_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.is_asleep | (!st->chip_config.firmware_loaded) |
+ st->chip_config.enable) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto dmp_attr_store_fail;
+ switch (this_attr->address) {
+ case ATTR_DMP_PEDOMETER_STEPS:
+ result = write_be32_key_to_mem(st, data, KEY_D_PEDSTD_STEPCTR);
+ if (result)
+ goto dmp_attr_store_fail;
+ break;
+ case ATTR_DMP_PEDOMETER_TIME:
+ result = write_be32_key_to_mem(st, data, KEY_D_PEDSTD_TIMECTR);
+ if (result)
+ goto dmp_attr_store_fail;
+ break;
+ case ATTR_DMP_TAP_THRESHOLD: {
+ const char ax[] = {INV_TAP_AXIS_X, INV_TAP_AXIS_Y,
+ INV_TAP_AXIS_Z};
+ int i;
+ if (data < 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ for (i = 0; i < ARRAY_SIZE(ax); i++) {
+ result = inv_set_tap_threshold_dmp(st, ax[i], data);
+ if (result)
+ goto dmp_attr_store_fail;
+ }
+ st->tap.thresh = data;
+ break;
+ }
+ case ATTR_DMP_TAP_MIN_COUNT:
+ if (data < 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ result = inv_set_min_taps_dmp(st, data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->tap.min_count = data;
+ break;
+ case ATTR_DMP_TAP_ON:
+ result = inv_enable_tap_dmp(st, !!data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->chip_config.tap_on = !!data;
+ break;
+ case ATTR_DMP_TAP_TIME:
+ if (data < 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ result = inv_set_tap_time_dmp(st, data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->tap.time = data;
+ break;
+ case ATTR_DMP_ON:
+ st->chip_config.dmp_on = !!data;
+ break;
+ case ATTR_DMP_INT_ON:
+ st->chip_config.dmp_int_on = !!data;
+ break;
+ case ATTR_DMP_EVENT_INT_ON:
+ result = inv_set_interrupt_on_gesture_event(st, !!data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->chip_config.dmp_event_int_on = !!data;
+ break;
+ case ATTR_DMP_OUTPUT_RATE:
+ if (data <= 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ result = inv_set_fifo_rate(st, data);
+ if (result)
+ goto dmp_attr_store_fail;
+ if (st->chip_config.has_compass) {
+ st->compass_dmp_divider = COMPASS_RATE_SCALE * data /
+ ONE_K_HZ;
+ if (st->compass_dmp_divider > 0)
+ st->compass_dmp_divider -= 1;
+ st->compass_counter = 0;
+ }
+ st->chip_config.dmp_output_rate = data;
+ break;
+ case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+ result = inv_set_display_orient_interrupt_dmp(st, !!data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->chip_config.display_orient_on = !!data;
+ break;
+ default:
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ result = count;
+
+dmp_attr_store_fail:
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+/**
+ * inv_attr_show() - calling this function will show current
+ * dmp parameters.
+ */
+static ssize_t inv_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ s8 d[4];
+ int result, data;
+ s8 *m;
+
+ switch (this_attr->address) {
+ case ATTR_DMP_PEDOMETER_STEPS:
+ if (!check_dmp_on(st))
+ return -EPERM;
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_D_PEDSTD_STEPCTR), 4, d);
+ if (result)
+ return result;
+ data = be32_to_cpup((int *)d);
+ return sprintf(buf, "%d\n", data);
+ case ATTR_DMP_PEDOMETER_TIME:
+ if (!check_dmp_on(st))
+ return -EPERM;
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_D_PEDSTD_TIMECTR), 4, d);
+ if (result)
+ return result;
+ data = be32_to_cpup((int *)d);
+ return sprintf(buf, "%d\n", data * MS_PER_DMP_TICK);
+ case ATTR_DMP_TAP_THRESHOLD:
+ return sprintf(buf, "%d\n", st->tap.thresh);
+ case ATTR_DMP_TAP_MIN_COUNT:
+ return sprintf(buf, "%d\n", st->tap.min_count);
+ case ATTR_DMP_TAP_ON:
+ return sprintf(buf, "%d\n", st->chip_config.tap_on);
+ case ATTR_DMP_TAP_TIME:
+ return sprintf(buf, "%d\n", st->tap.time);
+ case ATTR_DMP_ON:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_on);
+ case ATTR_DMP_INT_ON:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_int_on);
+ case ATTR_DMP_EVENT_INT_ON:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_event_int_on);
+ case ATTR_DMP_OUTPUT_RATE:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_output_rate);
+ case ATTR_DMP_QUATERNION_ON:
+ return sprintf(buf, "%d\n", st->chip_config.quaternion_on);
+ case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+ return sprintf(buf, "%d\n",
+ st->chip_config.display_orient_on);
+ case ATTR_LPA_FREQ:{
+ const char *f[] = {"1.25", "5", "20", "40"};
+ return sprintf(buf, "%s\n", f[st->chip_config.lpa_freq]);
+ }
+ case ATTR_SELF_TEST:
+ if (INV_MPU3050 == st->chip_type)
+ result = 1;
+ else if (INV_MPU6500 == st->chip_type)
+ result = inv_hw_self_test_6500(st);
+ else
+ result = inv_hw_self_test(st);
+ return sprintf(buf, "%d\n", result);
+ case ATTR_GYRO_MATRIX:
+ m = st->plat_data.orientation;
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ case ATTR_ACCL_MATRIX:
+ if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_ACCEL)
+ m = st->plat_data.secondary_orientation;
+ else
+ m = st->plat_data.orientation;
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ case ATTR_COMPASS_MATRIX:
+ if (st->plat_data.sec_slave_type ==
+ SECONDARY_SLAVE_TYPE_COMPASS)
+ m = st->plat_data.secondary_orientation;
+ else
+ return -ENODEV;
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ case ATTR_GYRO_ENABLE:
+ return sprintf(buf, "%d\n", st->chip_config.gyro_enable);
+ case ATTR_ACCL_ENABLE:
+ return sprintf(buf, "%d\n", st->chip_config.accl_enable);
+ case ATTR_COMPASS_ENABLE:
+ return sprintf(buf, "%d\n", st->chip_config.compass_enable);
+ case ATTR_POWER_STATE:
+ return sprintf(buf, "%d\n", !st->chip_config.is_asleep);
+ case ATTR_FIRMWARE_LOADED:
+ return sprintf(buf, "%d\n", st->chip_config.firmware_loaded);
+ case ATTR_MOTION_ON:
+ return sprintf(buf, "%d\n", st->mot_int.mot_on);
+ case ATTR_MOTION_DURATION:
+ return sprintf(buf, "%d\n", st->mot_int.mot_dur);
+ case ATTR_MOTION_THRESHOLD:
+ return sprintf(buf, "%d\n", st->mot_int.mot_thr);
+#ifdef CONFIG_INV_TESTING
+ case ATTR_REG_WRITE:
+ return sprintf(buf, "1\n");
+#endif
+ default:
+ return -EPERM;
+ }
+}
+
+/**
+ * inv_dmp_display_orient_show() - calling this function will
+ * show orientation This event must use poll.
+ */
+static ssize_t inv_dmp_display_orient_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+ return sprintf(buf, "%d\n", st->display_orient_data);
+}
+
+/**
+ * inv_accel_motion_show() - calling this function showes motion interrupt.
+ * This event must use poll.
+ */
+static ssize_t inv_accel_motion_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "1\n");
+}
+
+/**
+ * inv_dmp_tap_show() - calling this function will show tap
+ * This event must use poll.
+ */
+static ssize_t inv_dmp_tap_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+ return sprintf(buf, "%d\n", st->tap_data);
+}
+
+/**
+ * inv_temperature_show() - Read temperature data directly from registers.
+ */
+static ssize_t inv_temperature_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+ struct inv_reg_map_s *reg;
+ int result;
+ short temp;
+ long scale_t;
+ u8 data[2];
+ reg = &st->reg;
+
+ if (st->chip_config.is_asleep)
+ return -EPERM;
+ result = inv_i2c_read(st, reg->temperature, 2, data);
+ if (result) {
+ pr_err("Could not read temperature register.\n");
+ return result;
+ }
+ temp = (signed short)(be16_to_cpup((short *)&data[0]));
+
+ if (INV_MPU3050 == st->chip_type)
+ scale_t = MPU3050_TEMP_OFFSET +
+ inv_q30_mult((int)temp << MPU_TEMP_SHIFT,
+ MPU3050_TEMP_SCALE);
+ else
+ scale_t = MPU6050_TEMP_OFFSET +
+ inv_q30_mult((int)temp << MPU_TEMP_SHIFT,
+ MPU6050_TEMP_SCALE);
+
+ INV_I2C_INC_TEMPREAD(1);
+
+ return sprintf(buf, "%ld %lld\n", scale_t, get_time_ns());
+}
+
+/**
+ * inv_firmware_loaded() - calling this function will change
+ * firmware load
+ */
+static int inv_firmware_loaded(struct inv_mpu_iio_s *st, int data)
+{
+ if (data)
+ return -EINVAL;
+ st->chip_config.firmware_loaded = 0;
+ st->chip_config.dmp_on = 0;
+ st->chip_config.quaternion_on = 0;
+
+ return 0;
+}
+
+/**
+ * inv_quaternion_on() - calling this function will store
+ * current quaternion on
+ */
+static int inv_quaternion_on(struct inv_mpu_iio_s *st,
+ struct iio_buffer *ring, bool en)
+{
+ st->chip_config.quaternion_on = en;
+ if (!en) {
+ clear_bit(INV_MPU_SCAN_QUAT_R, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_QUAT_X, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_QUAT_Y, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_QUAT_Z, ring->scan_mask);
+ }
+
+ return 0;
+}
+
+static int inv_switch_gyro_engine(struct inv_mpu_iio_s *st, bool en)
+{
+ return inv_switch_engine(st, en, BIT_PWR_GYRO_STBY);
+}
+
+static int inv_switch_accl_engine(struct inv_mpu_iio_s *st, bool en)
+{
+ return inv_switch_engine(st, en, BIT_PWR_ACCL_STBY);
+}
+
+/**
+ * inv_gyro_enable() - Enable/disable gyro.
+ */
+static int inv_gyro_enable(struct inv_mpu_iio_s *st,
+ struct iio_buffer *ring, bool en)
+{
+ int result;
+ if (en == st->chip_config.gyro_enable)
+ return 0;
+ result = st->switch_gyro_engine(st, en);
+ if (result)
+ return result;
+
+ if (!en) {
+ st->chip_config.gyro_fifo_enable = 0;
+ clear_bit(INV_MPU_SCAN_GYRO_X, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_GYRO_Y, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_GYRO_Z, ring->scan_mask);
+ }
+ st->chip_config.gyro_enable = en;
+
+ return 0;
+}
+
+/**
+ * inv_accl_enable() - Enable/disable accl.
+ */
+static ssize_t inv_accl_enable(struct inv_mpu_iio_s *st,
+ struct iio_buffer *ring, bool en)
+{
+ int result;
+ if (en == st->chip_config.accl_enable)
+ return 0;
+ result = st->switch_accl_engine(st, en);
+ if (result)
+ return result;
+ st->chip_config.accl_enable = en;
+ if (!en) {
+ st->chip_config.accl_fifo_enable = 0;
+ clear_bit(INV_MPU_SCAN_ACCL_X, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_ACCL_Y, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_ACCL_Z, ring->scan_mask);
+ }
+
+ return 0;
+}
+
+/**
+ * inv_compass_enable() - calling this function will store compass
+ * enable
+ */
+static ssize_t inv_compass_enable(struct inv_mpu_iio_s *st,
+ struct iio_buffer *ring, bool en)
+{
+ if (en == st->chip_config.compass_enable)
+ return 0;
+ st->chip_config.compass_enable = en;
+ if (!en) {
+ st->chip_config.compass_fifo_enable = 0;
+ clear_bit(INV_MPU_SCAN_MAGN_X, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_MAGN_Y, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_MAGN_Z, ring->scan_mask);
+ }
+
+ return 0;
+}
+
+/**
+ * inv_attr_store() - calling this function will store current
+ * non-dmp parameter settings
+ */
+static ssize_t inv_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data;
+ u8 d;
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ if (check_enable(st)) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto attr_store_fail;
+ switch (this_attr->address) {
+ case ATTR_GYRO_ENABLE:
+ result = inv_gyro_enable(st, ring, !!data);
+ break;
+ case ATTR_ACCL_ENABLE:
+ result = inv_accl_enable(st, ring, !!data);
+ break;
+ case ATTR_COMPASS_ENABLE:
+ result = inv_compass_enable(st, ring, !!data);
+ break;
+ case ATTR_DMP_QUATERNION_ON:
+ result = inv_quaternion_on(st, ring, !!data);
+ break;
+ case ATTR_LPA_FREQ:
+ result = inv_lpa_freq(st, data);
+ break;
+ case ATTR_FIRMWARE_LOADED:
+ result = inv_firmware_loaded(st, data);
+ break;
+ case ATTR_MOTION_ON:
+ if (INV_MPU6500 == st->chip_type) {
+ if (data)
+ /* enable and put in MPU6500 mode */
+ d = BIT_ACCEL_INTEL_ENABLE
+ | BIT_ACCEL_INTEL_MODE;
+ else
+ d = 0;
+ result = inv_i2c_single_write(st,
+ REG_6500_ACCEL_INTEL_CTRL, d);
+ if (result)
+ goto attr_store_fail;
+ }
+ st->mot_int.mot_on = !!data;
+ st->chip_config.lpa_mode = !!data;
+ break;
+ case ATTR_MOTION_DURATION:
+ if (INV_MPU6500 != st->chip_type) {
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_DUR,
+ MPU6050_MOTION_DUR_DEFAULT);
+ if (result)
+ goto attr_store_fail;
+ }
+ st->mot_int.mot_dur = data;
+ break;
+ case ATTR_MOTION_THRESHOLD:
+ if ((data > MPU6XXX_MAX_MOTION_THRESH) || (data < 0)) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ d = (u8)(data >> MPU6XXX_MOTION_THRESH_SHIFT);
+ data = (d << MPU6XXX_MOTION_THRESH_SHIFT);
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, d);
+ if (result)
+ goto attr_store_fail;
+ st->mot_int.mot_thr = data;
+ break;
+ default:
+ result = -EINVAL;
+ goto attr_store_fail;
+ };
+ if (result)
+ goto attr_store_fail;
+ result = count;
+
+attr_store_fail:
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+#ifdef CONFIG_INV_TESTING
+/**
+ * inv_reg_write_store() - register write command for testing.
+ * Format: WSRRDD, where RR is the register in hex,
+ * and DD is the data in hex.
+ */
+static ssize_t inv_reg_write_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ u32 result;
+ u8 wreg, wval;
+ int temp;
+ char local_buf[10];
+
+ if ((buf[0] != 'W' && buf[0] != 'w') ||
+ (buf[1] != 'S' && buf[1] != 's'))
+ return -EINVAL;
+ if (strlen(buf) < 6)
+ return -EINVAL;
+
+ strncpy(local_buf, buf, 7);
+ local_buf[6] = 0;
+ result = sscanf(&local_buf[4], "%x", &temp);
+ if (result == 0)
+ return -EINVAL;
+ wval = temp;
+ local_buf[4] = 0;
+ sscanf(&local_buf[2], "%x", &temp);
+ if (result == 0)
+ return -EINVAL;
+ wreg = temp;
+
+ result = inv_i2c_single_write(st, wreg, wval);
+ if (result)
+ return result;
+
+ return count;
+}
+#endif /* CONFIG_INV_TESTING */
+
+#define INV_MPU_CHAN(_type, _channel2, _index) \
+ { \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = _channel2, \
+ .info_mask = (IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
+ IIO_CHAN_INFO_SCALE_SHARED_BIT), \
+ .scan_index = _index, \
+ .scan_type = IIO_ST('s', 16, 16, 0) \
+ }
+
+#define INV_ACCL_CHAN(_type, _channel2, _index) \
+ { \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = _channel2, \
+ .info_mask = (IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
+ IIO_CHAN_INFO_SCALE_SHARED_BIT | \
+ IIO_CHAN_INFO_OFFSET_SEPARATE_BIT), \
+ .scan_index = _index, \
+ .scan_type = IIO_ST('s', 16, 16, 0) \
+ }
+
+#define INV_MPU_QUATERNION_CHAN(_channel2, _index) \
+ { \
+ .type = IIO_QUATERNION, \
+ .modified = 1, \
+ .channel2 = _channel2, \
+ .scan_index = _index, \
+ .scan_type = IIO_ST('s', 32, 32, 0) \
+ }
+
+#define INV_MPU_MAGN_CHAN(_channel2, _index) \
+ { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = _channel2, \
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, \
+ .scan_index = _index, \
+ .scan_type = IIO_ST('s', 16, 16, 0) \
+ }
+
+static const struct iio_chan_spec inv_mpu_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP),
+
+ INV_MPU_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU_SCAN_GYRO_X),
+ INV_MPU_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU_SCAN_GYRO_Y),
+ INV_MPU_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU_SCAN_GYRO_Z),
+
+ INV_ACCL_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU_SCAN_ACCL_X),
+ INV_ACCL_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU_SCAN_ACCL_Y),
+ INV_ACCL_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU_SCAN_ACCL_Z),
+
+ INV_MPU_QUATERNION_CHAN(IIO_MOD_R, INV_MPU_SCAN_QUAT_R),
+ INV_MPU_QUATERNION_CHAN(IIO_MOD_X, INV_MPU_SCAN_QUAT_X),
+ INV_MPU_QUATERNION_CHAN(IIO_MOD_Y, INV_MPU_SCAN_QUAT_Y),
+ INV_MPU_QUATERNION_CHAN(IIO_MOD_Z, INV_MPU_SCAN_QUAT_Z),
+
+ INV_MPU_MAGN_CHAN(IIO_MOD_X, INV_MPU_SCAN_MAGN_X),
+ INV_MPU_MAGN_CHAN(IIO_MOD_Y, INV_MPU_SCAN_MAGN_Y),
+ INV_MPU_MAGN_CHAN(IIO_MOD_Z, INV_MPU_SCAN_MAGN_Z),
+};
+
+/*constant IIO attribute */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
+static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show,
+ inv_fifo_rate_store);
+static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL);
+static IIO_DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_power_state_store, ATTR_POWER_STATE);
+static IIO_DEVICE_ATTR(firmware_loaded, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_FIRMWARE_LOADED);
+static IIO_DEVICE_ATTR(motion_lpa_freq, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_LPA_FREQ);
+static IIO_DEVICE_ATTR(motion_lpa_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_MOTION_ON);
+static IIO_DEVICE_ATTR(motion_lpa_duration, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_MOTION_DURATION);
+static IIO_DEVICE_ATTR(motion_lpa_threshold, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_MOTION_THRESHOLD);
+static DEVICE_ATTR(reg_dump, S_IRUGO, inv_reg_dump_show, NULL);
+static IIO_DEVICE_ATTR(self_test, S_IRUGO, inv_attr_show, NULL,
+ ATTR_SELF_TEST);
+static IIO_DEVICE_ATTR(gyro_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_GYRO_MATRIX);
+static IIO_DEVICE_ATTR(accl_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_ACCL_MATRIX);
+static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_COMPASS_MATRIX);
+static IIO_DEVICE_ATTR(dmp_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_ON);
+static IIO_DEVICE_ATTR(dmp_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_INT_ON);
+static IIO_DEVICE_ATTR(dmp_event_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_EVENT_INT_ON);
+static IIO_DEVICE_ATTR(dmp_output_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_OUTPUT_RATE);
+static IIO_DEVICE_ATTR(quaternion_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_DMP_QUATERNION_ON);
+static IIO_DEVICE_ATTR(display_orientation_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_DISPLAY_ORIENTATION_ON);
+static IIO_DEVICE_ATTR(tap_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_TAP_ON);
+static IIO_DEVICE_ATTR(tap_time, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_TAP_TIME);
+static IIO_DEVICE_ATTR(tap_min_count, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_TAP_MIN_COUNT);
+static IIO_DEVICE_ATTR(tap_threshold, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_TAP_THRESHOLD);
+static IIO_DEVICE_ATTR(pedometer_time, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_PEDOMETER_TIME);
+static IIO_DEVICE_ATTR(pedometer_steps, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_PEDOMETER_STEPS);
+static DEVICE_ATTR(event_tap, S_IRUGO, inv_dmp_tap_show, NULL);
+static DEVICE_ATTR(event_display_orientation, S_IRUGO,
+ inv_dmp_display_orient_show, NULL);
+static DEVICE_ATTR(event_accel_motion, S_IRUGO, inv_accel_motion_show, NULL);
+static IIO_DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_ENABLE);
+static IIO_DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCL_ENABLE);
+static IIO_DEVICE_ATTR(compass_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_ENABLE);
+#ifdef CONFIG_INV_TESTING
+static IIO_DEVICE_ATTR(reg_write, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_reg_write_store, ATTR_REG_WRITE);
+#endif
+
+static const struct attribute *inv_gyro_attributes[] = {
+ &iio_dev_attr_gyro_enable.dev_attr.attr,
+ &dev_attr_temperature.attr,
+ &iio_dev_attr_power_state.dev_attr.attr,
+ &dev_attr_reg_dump.attr,
+ &iio_dev_attr_self_test.dev_attr.attr,
+ &iio_dev_attr_gyro_matrix.dev_attr.attr,
+#ifdef CONFIG_INV_TESTING
+ &iio_dev_attr_reg_write.dev_attr.attr,
+#endif
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+};
+
+static const struct attribute *inv_mpu6050_attributes[] = {
+ &iio_dev_attr_accl_enable.dev_attr.attr,
+ &iio_dev_attr_accl_matrix.dev_attr.attr,
+ &iio_dev_attr_firmware_loaded.dev_attr.attr,
+ &iio_dev_attr_motion_lpa_freq.dev_attr.attr,
+ &iio_dev_attr_motion_lpa_on.dev_attr.attr,
+ &iio_dev_attr_motion_lpa_duration.dev_attr.attr,
+ &iio_dev_attr_motion_lpa_threshold.dev_attr.attr,
+ &iio_dev_attr_dmp_on.dev_attr.attr,
+ &iio_dev_attr_dmp_int_on.dev_attr.attr,
+ &iio_dev_attr_dmp_event_int_on.dev_attr.attr,
+ &iio_dev_attr_dmp_output_rate.dev_attr.attr,
+ &iio_dev_attr_quaternion_on.dev_attr.attr,
+ &iio_dev_attr_display_orientation_on.dev_attr.attr,
+ &iio_dev_attr_tap_on.dev_attr.attr,
+ &iio_dev_attr_tap_time.dev_attr.attr,
+ &iio_dev_attr_tap_min_count.dev_attr.attr,
+ &iio_dev_attr_tap_threshold.dev_attr.attr,
+ &iio_dev_attr_pedometer_time.dev_attr.attr,
+ &iio_dev_attr_pedometer_steps.dev_attr.attr,
+ &dev_attr_event_display_orientation.attr,
+ &dev_attr_event_tap.attr,
+ &dev_attr_event_accel_motion.attr,
+};
+
+static const struct attribute *inv_compass_attributes[] = {
+ &iio_dev_attr_compass_matrix.dev_attr.attr,
+ &iio_dev_attr_compass_enable.dev_attr.attr,
+};
+
+static const struct attribute *inv_mpu3050_attributes[] = {
+ &iio_dev_attr_accl_matrix.dev_attr.attr,
+ &iio_dev_attr_accl_enable.dev_attr.attr,
+};
+
+static struct attribute *inv_attributes[ARRAY_SIZE(inv_gyro_attributes) +
+ ARRAY_SIZE(inv_mpu6050_attributes) +
+ ARRAY_SIZE(inv_compass_attributes) + 1];
+
+static const struct attribute_group inv_attribute_group = {
+ .name = "mpu",
+ .attrs = inv_attributes
+};
+
+static const struct iio_info mpu_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &mpu_read_raw,
+ .write_raw = &mpu_write_raw,
+ .attrs = &inv_attribute_group,
+};
+
+/**
+ * inv_setup_compass() - Configure compass.
+ */
+static int inv_setup_compass(struct inv_mpu_iio_s *st)
+{
+ int result;
+ u8 data[4];
+
+ result = inv_i2c_read(st, REG_YGOFFS_TC, 1, data);
+ if (result)
+ return result;
+ data[0] &= ~BIT_I2C_MST_VDDIO;
+ if (st->plat_data.level_shifter)
+ data[0] |= BIT_I2C_MST_VDDIO;
+ /*set up VDDIO register */
+ result = inv_i2c_single_write(st, REG_YGOFFS_TC, data[0]);
+ if (result)
+ return result;
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result)
+ return result;
+ /*read secondary i2c ID register */
+ result = inv_secondary_read(REG_AKM_ID, 1, data);
+ if (result)
+ return result;
+ if (data[0] != DATA_AKM_ID)
+ return -ENXIO;
+ /*set AKM to Fuse ROM access mode */
+ result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_FR);
+ if (result)
+ return result;
+ result = inv_secondary_read(REG_AKM_SENSITIVITY, THREE_AXIS,
+ st->chip_info.compass_sens);
+ if (result)
+ return result;
+ /*revert to power down mode */
+ result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PD);
+ if (result)
+ return result;
+ pr_debug("%s senx=%d, seny=%d, senz=%d\n",
+ st->hw->name,
+ st->chip_info.compass_sens[0],
+ st->chip_info.compass_sens[1],
+ st->chip_info.compass_sens[2]);
+ /*restore to non-bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ if (result)
+ return result;
+
+ /*setup master mode and master clock and ES bit*/
+ result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES);
+ if (result)
+ return result;
+ /* slave 0 is used to read data from compass */
+ /*read mode */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, BIT_I2C_READ|
+ st->plat_data.secondary_i2c_addr);
+ if (result)
+ return result;
+ /* AKM status register address is 2 */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_REG, REG_AKM_STATUS);
+ if (result)
+ return result;
+ /* slave 0 is enabled at the beginning, read 8 bytes from here */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, BIT_SLV_EN |
+ NUM_BYTES_COMPASS_SLAVE);
+ if (result)
+ return result;
+ /*slave 1 is used for AKM mode change only*/
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR,
+ st->plat_data.secondary_i2c_addr);
+ if (result)
+ return result;
+ /* AKM mode register address is 0x0A */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_REG, REG_AKM_MODE);
+ if (result)
+ return result;
+ /* slave 1 is enabled, byte length is 1 */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, BIT_SLV_EN | 1);
+ if (result)
+ return result;
+ /* output data for slave 1 is fixed, single measure mode*/
+ st->compass_scale = 1;
+ if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) {
+ st->compass_st_upper = AKM8975_ST_Upper;
+ st->compass_st_lower = AKM8975_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM;
+ } else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) {
+ st->compass_st_upper = AKM8972_ST_Upper;
+ st->compass_st_lower = AKM8972_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM;
+ } else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+ st->compass_st_upper = AKM8963_ST_Upper;
+ st->compass_st_lower = AKM8963_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM |
+ (st->compass_scale << AKM8963_SCALE_SHIFT);
+ }
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, data[0]);
+ if (result)
+ return result;
+ /* slave 0 and 1 timer action is enabled every sample*/
+ result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL,
+ BIT_SLV0_DLY_EN | BIT_SLV1_DLY_EN);
+ return result;
+}
+
+static void inv_setup_func_ptr(struct inv_mpu_iio_s *st)
+{
+ if (st->chip_type == INV_MPU3050) {
+ st->set_power_state = set_power_mpu3050;
+ st->switch_gyro_engine = inv_switch_3050_gyro_engine;
+ st->switch_accl_engine = inv_switch_3050_accl_engine;
+ st->init_config = inv_init_config_mpu3050;
+ st->setup_reg = inv_setup_reg_mpu3050;
+ } else {
+ st->set_power_state = set_power_itg;
+ st->switch_gyro_engine = inv_switch_gyro_engine;
+ st->switch_accl_engine = inv_switch_accl_engine;
+ st->init_config = inv_init_config;
+ st->setup_reg = inv_setup_reg;
+ /*MPU6XXX special functions */
+ st->compass_en = inv_compass_enable;
+ st->quaternion_en = inv_quaternion_on;
+ st->gyro_en = inv_gyro_enable;
+ st->accl_en = inv_accl_enable;
+ }
+}
+
+static int inv_detect_6xxx(struct inv_mpu_iio_s *st)
+{
+ int result;
+ u8 d;
+
+ result = inv_i2c_read(st, REG_WHOAMI, 1, &d);
+ if (result)
+ return result;
+ if (d == MPU6500_ID) {
+ st->chip_type = INV_MPU6500;
+ strcpy(st->name, "mpu6500");
+ } else {
+ strcpy(st->name, "mpu6050");
+ }
+
+ return 0;
+}
+
+/**
+ * inv_check_chip_type() - check and setup chip type.
+ */
+static int inv_check_chip_type(struct inv_mpu_iio_s *st,
+ const struct i2c_device_id *id)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ int t_ind;
+
+ if (!strcmp(id->name, "itg3500"))
+ st->chip_type = INV_ITG3500;
+ else if (!strcmp(id->name, "mpu3050"))
+ st->chip_type = INV_MPU3050;
+ else if (!strcmp(id->name, "mpu6050"))
+ st->chip_type = INV_MPU6050;
+ else if (!strcmp(id->name, "mpu9150"))
+ st->chip_type = INV_MPU6050;
+ else if (!strcmp(id->name, "mpu6500"))
+ st->chip_type = INV_MPU6500;
+ else if (!strcmp(id->name, "mpu9250"))
+ st->chip_type = INV_MPU6500;
+ else if (!strcmp(id->name, "mpu6xxx"))
+ st->chip_type = INV_MPU6050;
+ else
+ return -EPERM;
+ inv_setup_func_ptr(st);
+ st->hw = &hw_info[st->chip_type];
+ st->mpu_slave = NULL;
+ reg = &st->reg;
+ st->setup_reg(reg);
+ st->chip_config.gyro_enable = 1;
+ /* reset to make sure previous state are not there */
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+ if (result)
+ return result;
+ msleep(POWER_UP_TIME);
+
+ /* turn off and turn on power to ensure gyro engine is on */
+ result = st->set_power_state(st, false);
+ if (result)
+ return result;
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+
+ if (!strcmp(id->name, "mpu6xxx")) {
+ result = inv_detect_6xxx(st);
+ if (result)
+ return result;
+ }
+
+ switch (st->chip_type) {
+ case INV_ITG3500:
+ st->num_channels = INV_CHANNEL_NUM_GYRO;
+ break;
+ case INV_MPU6050:
+ case INV_MPU6500:
+ if (SECONDARY_SLAVE_TYPE_COMPASS ==
+ st->plat_data.sec_slave_type) {
+ st->chip_config.has_compass = 1;
+ st->num_channels =
+ INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION_MAGN;
+ } else {
+ st->chip_config.has_compass = 0;
+ st->num_channels =
+ INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION;
+ }
+ break;
+ case INV_MPU3050:
+ if (SECONDARY_SLAVE_TYPE_ACCEL ==
+ st->plat_data.sec_slave_type) {
+ if (ACCEL_ID_BMA250 == st->plat_data.sec_slave_id)
+ inv_register_mpu3050_slave(st);
+ st->num_channels = INV_CHANNEL_NUM_GYRO_ACCL;
+ } else {
+ st->num_channels = INV_CHANNEL_NUM_GYRO;
+ }
+ break;
+ default:
+ result = st->set_power_state(st, false);
+ return -ENODEV;
+ }
+
+ switch (st->chip_type) {
+ case INV_MPU6050:
+ result = inv_get_silicon_rev_mpu6050(st);
+ break;
+ case INV_MPU6500:
+ result = inv_get_silicon_rev_mpu6500(st);
+ break;
+ default:
+ result = 0;
+ break;
+ }
+ if (result) {
+ pr_err("read silicon rev error\n");
+ st->set_power_state(st, false);
+ return result;
+ }
+
+//wlg bypass i2c slave
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result)
+ return result;
+
+ if (st->chip_config.has_compass) {
+ result = inv_setup_compass(st);
+ if (result) {
+ pr_err("compass setup failed\n");
+ st->set_power_state(st, false);
+ return result;
+ }
+ }
+
+ t_ind = 0;
+ memcpy(&inv_attributes[t_ind], inv_gyro_attributes,
+ sizeof(inv_gyro_attributes));
+ t_ind += ARRAY_SIZE(inv_gyro_attributes);
+
+ if (INV_MPU3050 == st->chip_type && st->mpu_slave != NULL) {
+ memcpy(&inv_attributes[t_ind], inv_mpu3050_attributes,
+ sizeof(inv_mpu3050_attributes));
+ t_ind += ARRAY_SIZE(inv_mpu3050_attributes);
+ inv_attributes[t_ind] = NULL;
+ return 0;
+ }
+
+ if ((INV_MPU6050 == st->chip_type) || (INV_MPU6500 == st->chip_type)) {
+ memcpy(&inv_attributes[t_ind], inv_mpu6050_attributes,
+ sizeof(inv_mpu6050_attributes));
+ t_ind += ARRAY_SIZE(inv_mpu6050_attributes);
+ }
+
+ if (st->chip_config.has_compass) {
+ memcpy(&inv_attributes[t_ind], inv_compass_attributes,
+ sizeof(inv_compass_attributes));
+ t_ind += ARRAY_SIZE(inv_compass_attributes);
+ }
+ inv_attributes[t_ind] = NULL;
+
+ return 0;
+}
+
+/**
+ * inv_create_dmp_sysfs() - create binary sysfs dmp entry.
+ */
+static const struct bin_attribute dmp_firmware = {
+ .attr = {
+ .name = "dmp_firmware",
+ .mode = S_IRUGO | S_IWUSR
+ },
+ .size = 4096,
+ .read = inv_dmp_firmware_read,
+ .write = inv_dmp_firmware_write,
+};
+
+static int inv_create_dmp_sysfs(struct iio_dev *ind)
+{
+ int result;
+ result = sysfs_create_bin_file(&ind->dev.kobj, &dmp_firmware);
+
+ return result;
+}
+static void inv_mpu_hw_init(struct i2c_client *client)
+{
+ struct regulator *reg_vdd;
+ struct mpu_platform_data *pdata = client->dev.platform_data;
+ reg_vdd = regulator_get(&client->dev, pdata->vdd_name);
+ if (!WARN(IS_ERR(reg_vdd), "inv_mpu regulator: failed to get %s.\n", pdata->vdd_name)) {
+ regulator_set_voltage(reg_vdd, 1800000, 1800000);
+ regulator_enable(reg_vdd);
+ }
+ msleep(100);
+}
+
+/**
+ * inv_mpu_probe() - probe function.
+ */
+static int inv_mpu_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_mpu_iio_s *st;
+ struct iio_dev *indio_dev;
+ int result;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENOSYS;
+ pr_err("I2c function error\n");
+ goto out_no_free;
+ }
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->client = client;
+ st->sl_handle = client->adapter;
+ st->i2c_addr = client->addr;
+ st->plat_data =
+ *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+ inv_mpu_hw_init(client);
+
+ /* power is turned on inside check chip type*/
+ result = inv_check_chip_type(st, id);
+ if (result)
+ goto out_free;
+
+ result = st->init_config(indio_dev);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Could not initialize device.\n");
+ goto out_free;
+ }
+ result = st->set_power_state(st, false);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "%s could not be turned off.\n", st->hw->name);
+ goto out_free;
+ }
+ /* Make state variables available to all _show and _store functions. */
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->dev.parent = &client->dev;
+ if (!strcmp(id->name, "mpu6xxx"))
+ indio_dev->name = st->name;
+ else
+ indio_dev->name = id->name;
+ indio_dev->channels = inv_mpu_channels;
+ indio_dev->num_channels = st->num_channels;
+
+ indio_dev->info = &mpu_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ result = inv_mpu_configure_ring(indio_dev);
+ if (result) {
+ pr_err("configure ring buffer fail\n");
+ goto out_free;
+ }
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result) {
+ pr_err("ring buffer register fail\n");
+ goto out_unreg_ring;
+ }
+ st->irq = gpio_to_irq(client->irq);
+ result = inv_mpu_probe_trigger(indio_dev);
+ if (result) {
+ pr_err("trigger probe fail\n");
+ goto out_remove_ring;
+ }
+
+ /* Tell the i2c counter, we have an IRQ */
+ INV_I2C_SETIRQ(MPU, gpio_to_irq(client->irq));
+
+ result = iio_device_register(indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_remove_trigger;
+ }
+
+ if (INV_MPU6050 == st->chip_type ||
+ INV_MPU6500 == st->chip_type) {
+ result = inv_create_dmp_sysfs(indio_dev);
+ if (result) {
+ pr_err("create dmp sysfs failed\n");
+ goto out_unreg_iio;
+ }
+ }
+
+ INIT_KFIFO(st->timestamps);
+ spin_lock_init(&st->time_stamp_lock);
+ dev_info(&client->adapter->dev, "%s is ready to go!\n",
+ indio_dev->name);
+
+ return 0;
+out_unreg_iio:
+ iio_device_unregister(indio_dev);
+out_remove_trigger:
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_mpu_remove_trigger(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ iio_device_free(indio_dev);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+
+ return -EIO;
+}
+
+static void inv_mpu_shutdown(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ int result;
+
+ reg = &st->reg;
+ dev_dbg(&client->adapter->dev, "Shutting down %s...\n", st->hw->name);
+
+ /* reset to make sure previous state are not there */
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+ if (result)
+ dev_err(&client->adapter->dev, "Failed to reset %s\n",
+ st->hw->name);
+ msleep(POWER_UP_TIME);
+ /* turn off power to ensure gyro engine is off */
+ result = st->set_power_state(st, false);
+ if (result)
+ dev_err(&client->adapter->dev, "Failed to turn off %s\n",
+ st->hw->name);
+}
+
+/**
+ * inv_mpu_remove() - remove function.
+ */
+static int inv_mpu_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ kfifo_free(&st->timestamps);
+ iio_device_unregister(indio_dev);
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_mpu_remove_trigger(indio_dev);
+ iio_buffer_unregister(indio_dev);
+ inv_mpu_unconfigure_ring(indio_dev);
+ iio_device_free(indio_dev);
+
+ dev_info(&client->adapter->dev, "inv-mpu-iio module removed.\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int inv_mpu_resume(struct device *dev)
+{
+ struct inv_mpu_iio_s *st =
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+ pr_debug("%s inv_mpu_resume\n", st->hw->name);
+ return st->set_power_state(st, true);
+}
+
+static int inv_mpu_suspend(struct device *dev)
+{
+ struct inv_mpu_iio_s *st =
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+ pr_debug("%s inv_mpu_suspend\n", st->hw->name);
+ return st->set_power_state(st, false);
+}
+static const struct dev_pm_ops inv_mpu_pmops = {
+ SET_SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume)
+};
+#define INV_MPU_PMOPS (&inv_mpu_pmops)
+#else
+#define INV_MPU_PMOPS NULL
+#endif /* CONFIG_PM */
+
+static const u16 normal_i2c[] = { I2C_CLIENT_END };
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_mpu_id[] = {
+ {"itg3500", INV_ITG3500},
+ {"mpu3050", INV_MPU3050},
+ {"mpu6050", INV_MPU6050},
+ {"mpu9150", INV_MPU9150},
+ {"mpu6500", INV_MPU6500},
+ {"mpu9250", INV_MPU9250},
+ {"mpu6xxx", INV_MPU6XXX},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+
+static struct i2c_driver inv_mpu_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = inv_mpu_probe,
+ .remove = inv_mpu_remove,
+ .shutdown = inv_mpu_shutdown,
+ .id_table = inv_mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-mpu-iio",
+ .pm = INV_MPU_PMOPS,
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init inv_mpu_init(void)
+{
+ int result = i2c_add_driver(&inv_mpu_driver);
+ if (result) {
+ pr_err("failed\n");
+ return result;
+ }
+ return 0;
+}
+
+static void __exit inv_mpu_exit(void)
+{
+ i2c_del_driver(&inv_mpu_driver);
+}
+
+module_init(inv_mpu_init);
+module_exit(inv_mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv-mpu-iio");
+
+/**
+ * @}
+ */
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu/inv_mpu_iio.h
new file mode 100644
index 00000000..28c541ea
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_iio.h
@@ -0,0 +1,861 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_mpu_iio.h
+ * @brief Struct definitions for the Invensense mpu driver.
+ */
+
+#ifndef _INV_MPU_IIO_H_
+#define _INV_MPU_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/mpu.h>
+#include <linux/iio/types.h>
+#include <linux/input.h>
+
+#include "dmpKey.h"
+
+/**
+ * struct inv_reg_map_s - Notable slave registers.
+ * @sample_rate_div: Divider applied to gyro output rate.
+ * @lpf: Configures internal LPF.
+ * @bank_sel: Selects between memory banks.
+ * @user_ctrl: Enables/resets the FIFO.
+ * @fifo_en: Determines which data will appear in FIFO.
+ * @gyro_config: gyro config register.
+ * @accl_config: accel config register
+ * @fifo_count_h: Upper byte of FIFO count.
+ * @fifo_r_w: FIFO register.
+ * @raw_gyro Address of first gyro register.
+ * @raw_accl Address of first accel register.
+ * @temperature temperature register
+ * @int_enable: Interrupt enable register.
+ * @int_status: Interrupt flags.
+ * @pwr_mgmt_1: Controls chip's power state and clock source.
+ * @pwr_mgmt_2: Controls power state of individual sensors.
+ * @mem_start_addr: Address of first memory read.
+ * @mem_r_w: Access to memory.
+ * @prgm_strt_addrh firmware program start address register
+ */
+struct inv_reg_map_s {
+ u8 sample_rate_div;
+ u8 lpf;
+ u8 bank_sel;
+ u8 user_ctrl;
+ u8 fifo_en;
+ u8 gyro_config;
+ u8 accl_config;
+ u8 fifo_count_h;
+ u8 fifo_r_w;
+ u8 raw_gyro;
+ u8 raw_accl;
+ u8 temperature;
+ u8 int_enable;
+ u8 int_status;
+ u8 pwr_mgmt_1;
+ u8 pwr_mgmt_2;
+ u8 mem_start_addr;
+ u8 mem_r_w;
+ u8 prgm_strt_addrh;
+};
+/*device enum */
+enum inv_devices {
+ INV_ITG3500,
+ INV_MPU3050,
+ INV_MPU6050,
+ INV_MPU9150,
+ INV_MPU6500,
+ INV_MPU9250,
+ INV_MPU6XXX,
+ INV_NUM_PARTS
+};
+
+/**
+ * struct test_setup_t - set up parameters for self test.
+ * @gyro_sens: sensitity for gyro.
+ * @sample_rate: sample rate, i.e, fifo rate.
+ * @lpf: low pass filter.
+ * @fsr: full scale range.
+ * @accl_fs: accel full scale range.
+ * @accl_sens: accel sensitivity
+ */
+struct test_setup_t {
+ int gyro_sens;
+ int sample_rate;
+ int lpf;
+ int fsr;
+ int accl_fs;
+ u32 accl_sens[3];
+};
+
+/**
+ * struct inv_hw_s - Other important hardware information.
+ * @num_reg: Number of registers on device.
+ * @name: name of the chip
+ */
+struct inv_hw_s {
+ u8 num_reg;
+ u8 *name;
+};
+
+/**
+ * struct inv_chip_config_s - Cached chip configuration data.
+ * @fsr: Full scale range.
+ * @lpf: Digital low pass filter frequency.
+ * @accl_fs: accel full scale range.
+ * @self_test_run_once flag for self test run ever.
+ * @has_footer: MPU3050 specific work around.
+ * @has_compass: has compass or not.
+ * @enable: master enable to enable output
+ * @accl_enable: enable accel functionality
+ * @accl_fifo_enable: enable accel data output
+ * @gyro_enable: enable gyro functionality
+ * @gyro_fifo_enable: enable gyro data output
+ * @compass_enable: enable compass
+ * @compass_fifo_enable: enable compass data output
+ * @is_asleep: 1 if chip is powered down.
+ * @dmp_on: dmp is on/off.
+ * @dmp_int_on: dmp interrupt on/off.
+ * @dmp_event_int_on: dmp event interrupt on/off.
+ * @firmware_loaded: flag indicate firmware loaded or not.
+ * @lpa_mod: low power mode.
+ * @tap_on: tap on/off.
+ * @flick_int_on: flick interrupt on/off.
+ * @quaternion_on: send quaternion data on/off.
+ * @display_orient_on: display orientation on/off.
+ * @normal_compass_measure: discard first compass data after reset.
+ * @lpa_freq: low power frequency
+ * @prog_start_addr: firmware program start address.
+ * @dmp_output_rate: dmp output rate.
+ * @fifo_rate: FIFO update rate.
+ */
+struct inv_chip_config_s {
+ u32 fsr:2;
+ u32 lpf:3;
+ u32 accl_fs:2;
+ u32 self_test_run_once:1;
+ u32 has_footer:1;
+ u32 has_compass:1;
+ u32 enable:1;
+ u32 accl_enable:1;
+ u32 accl_fifo_enable:1;
+ u32 gyro_enable:1;
+ u32 gyro_fifo_enable:1;
+ u32 compass_enable:1;
+ u32 compass_fifo_enable:1;
+ u32 is_asleep:1;
+ u32 dmp_on:1;
+ u32 dmp_int_on:1;
+ u32 dmp_event_int_on:1;
+ u32 firmware_loaded:1;
+ u32 lpa_mode:1;
+ u32 tap_on:1;
+ u32 flick_int_on:1;
+ u32 quaternion_on:1;
+ u32 display_orient_on:1;
+ u32 normal_compass_measure:1;
+ u16 lpa_freq;
+ u16 prog_start_addr;
+ u16 fifo_rate;
+ u16 dmp_output_rate;
+};
+
+/**
+ * struct inv_chip_info_s - Chip related information.
+ * @product_id: Product id.
+ * @product_revision: Product revision.
+ * @silicon_revision: Silicon revision.
+ * @software_revision: software revision.
+ * @multi: accel specific multiplier.
+ * @compass_sens: compass sensitivity.
+ * @gyro_sens_trim: Gyro sensitivity trim factor.
+ * @accl_sens_trim: accel sensitivity trim factor.
+ */
+struct inv_chip_info_s {
+ u8 product_id;
+ u8 product_revision;
+ u8 silicon_revision;
+ u8 software_revision;
+ u8 multi;
+ u8 compass_sens[3];
+ u32 gyro_sens_trim;
+ u32 accl_sens_trim;
+};
+
+/**
+ * struct inv_flick_s structure to store flick data.
+ * @lower: lower bound of flick.
+ * @upper: upper bound of flick.
+ * @counter: counter of flick.
+ * @msg_on; message to carry flick
+ * @axis: axis of flick
+ */
+struct inv_flick_s {
+ int lower;
+ int upper;
+ int counter;
+ int msg_on;
+ int axis;
+};
+
+enum inv_channel_num {
+ INV_CHANNEL_NUM_GYRO = 4,
+ INV_CHANNEL_NUM_GYRO_ACCL = 7,
+ INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION = 11,
+ INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION_MAGN = 14,
+};
+
+/**
+ * struct inv_tap_s structure to store tap data.
+ * @min_count: minimum taps counted.
+ * @thresh: tap threshold.
+ * @time: tap time.
+ */
+struct inv_tap_s {
+ u16 min_count;
+ u16 thresh;
+ u16 time;
+};
+
+/**
+ * struct accel_mot_int_s structure to store motion interrupt data
+ * @mot_thr: motion threshold.
+ * @mot_dur: motion duration.
+ * @mot_on: flag to indicate motion detection on;
+ */
+struct accel_mot_int_s {
+ u16 mot_thr;
+ u32 mot_dur;
+ u8 mot_on:1;
+};
+
+struct inv_mpu_slave;
+/**
+ * struct inv_mpu_iio_s - Driver state variables.
+ * @chip_config: Cached attribute information.
+ * @chip_info: Chip information from read-only registers.
+ * @trig; iio trigger.
+ * @flick: flick data structure
+ * @tap: tap data structure
+ * @reg: Map of important registers.
+ * @hw: Other hardware-specific information.
+ * @chip_type: chip type.
+ * @time_stamp_lock: spin lock to time stamp.
+ * @client: i2c client handle.
+ * @plat_data: platform data.
+ * @mpu_slave: mpu slave handle.
+ * (*set_power_state)(struct inv_mpu_iio_s *, int on): function ptr
+ * (*switch_gyro_engine)(struct inv_mpu_iio_s *, int on): function ptr
+ * (*switch_accl_engine)(struct inv_mpu_iio_s *, int on): function ptr
+ * (*compass_en)(struct inv_mpu_iio_s *, struct iio_buffer *, bool);
+ * (*quaternion_en)(struct inv_mpu_iio_s *, struct iio_buffer *, bool)
+ * (*gyro_en)(struct inv_mpu_iio_s *, struct iio_buffer *, bool): func ptr.
+ * (*accl_en)(struct inv_mpu_iio_s *, struct iio_buffer *, bool): func ptr.
+ * (*init_config)(struct iio_dev *indio_dev): function ptr
+ * void (*setup_reg)(struct inv_reg_map_s *reg): function ptr
+ * @timestamps: kfifo queue to store time stamp.
+ * @compass_st_upper: compass self test upper limit.
+ * @compass_st_lower: compass self test lower limit.
+ * @irq: irq number store.
+ * @accel_bias: accel bias store.
+ * @gyro_bias: gyro bias store.
+ * @raw_gyro: raw gyro data.
+ * @raw_accel: raw accel data.
+ * @raw_compass: raw compass.
+ * @raw_quaternion raw quaternion data.
+ * @compass_scale: compass scale.
+ * @i2c_addr: i2c address.
+ * @compass_divider: slow down compass rate.
+ * @compass_dmp_divider: slow down compass rate for dmp.
+ * @compass_counter: slow down compass rate.
+ * @sample_divider: sample divider for dmp.
+ * @fifo_divider: fifo divider for dmp.
+ * @display_orient_data:display orient data.
+ * @tap_data: tap data.
+ * @num_channels: number of channels for current chip.
+ * @sl_handle: Handle to I2C port.
+ * @irq_dur_ns: duration between each irq.
+ * @last_isr_time: last isr time.
+ * @mpu6500_last_motion_time: MPU6500 last real motion interrupt time.
+ * @name: name for distiguish MPU6050 and MPU6500 in MPU6XXX.
+ */
+struct inv_mpu_iio_s {
+#define TIMESTAMP_FIFO_SIZE 16
+ struct inv_chip_config_s chip_config;
+ struct inv_chip_info_s chip_info;
+ struct iio_trigger *trig;
+ struct inv_flick_s flick;
+ struct inv_tap_s tap;
+ struct inv_reg_map_s reg;
+ const struct inv_hw_s *hw;
+ enum inv_devices chip_type;
+ spinlock_t time_stamp_lock;
+ struct i2c_client *client;
+ struct mpu_platform_data plat_data;
+ struct inv_mpu_slave *mpu_slave;
+ struct accel_mot_int_s mot_int;
+ int (*set_power_state)(struct inv_mpu_iio_s *, bool on);
+ int (*switch_gyro_engine)(struct inv_mpu_iio_s *, bool on);
+ int (*switch_accl_engine)(struct inv_mpu_iio_s *, bool on);
+ int (*compass_en)(struct inv_mpu_iio_s *,
+ struct iio_buffer *ring, bool on);
+ int (*quaternion_en)(struct inv_mpu_iio_s *,
+ struct iio_buffer *ring, bool on);
+ int (*gyro_en)(struct inv_mpu_iio_s *,
+ struct iio_buffer *ring, bool on);
+ int (*accl_en)(struct inv_mpu_iio_s *,
+ struct iio_buffer *ring, bool on);
+ int (*init_config)(struct iio_dev *indio_dev);
+ void (*setup_reg)(struct inv_reg_map_s *reg);
+ DECLARE_KFIFO(timestamps, u64, TIMESTAMP_FIFO_SIZE);
+ const short *compass_st_upper;
+ const short *compass_st_lower;
+ short irq;
+ int accel_bias[3];
+ int gyro_bias[3];
+ short raw_gyro[3];
+ short raw_accel[3];
+ short raw_compass[3];
+ int raw_quaternion[4];
+ int input_accel_bias[3];
+ u8 compass_scale;
+ u8 i2c_addr;
+ u8 compass_divider;
+ u8 compass_counter;
+ u8 compass_dmp_divider;
+ u8 sample_divider;
+ u8 fifo_divider;
+ u8 display_orient_data;
+ u8 tap_data;
+ enum inv_channel_num num_channels;
+ void *sl_handle;
+ u32 irq_dur_ns;
+ u64 last_isr_time;
+ u64 mpu6500_last_motion_time;
+ u8 name[20];
+};
+
+/* produces an unique identifier for each device based on the
+ combination of product version and product revision */
+struct prod_rev_map_t {
+ u16 mpl_product_key;
+ u8 silicon_rev;
+ u16 gyro_trim;
+ u16 accel_trim;
+};
+
+/**
+ * struct inv_mpu_slave - MPU slave structure.
+ * @suspend: suspend operation.
+ * @resume: resume operation.
+ * @setup: setup chip. initialization.
+ * @combine_data: combine raw data into meaningful data.
+ * @get_mode: get current chip mode.
+ * @set_lpf set low pass filter.
+ * @set_fs set full scale
+ */
+struct inv_mpu_slave {
+ int (*suspend)(struct inv_mpu_iio_s *);
+ int (*resume)(struct inv_mpu_iio_s *);
+ int (*setup)(struct inv_mpu_iio_s *);
+ int (*combine_data)(u8 *in, short *out);
+ int (*get_mode)(void);
+ int (*set_lpf)(struct inv_mpu_iio_s *, int rate);
+ int (*set_fs)(struct inv_mpu_iio_s *, int fs);
+};
+
+/* AKM definitions */
+#define REG_AKM_ID 0x00
+#define REG_AKM_STATUS 0x02
+#define REG_AKM_MEASURE_DATA 0x03
+#define REG_AKM_MODE 0x0A
+#define REG_AKM_ST_CTRL 0x0C
+#define REG_AKM_SENSITIVITY 0x10
+#define REG_AKM8963_CNTL1 0x0A
+
+#define DATA_AKM_ID 0x48
+#define DATA_AKM_MODE_PD 0x00
+#define DATA_AKM_MODE_SM 0x01
+#define DATA_AKM_MODE_ST 0x08
+#define DATA_AKM_MODE_FR 0x0F
+#define DATA_AKM_SELF_TEST 0x40
+#define DATA_AKM_DRDY 0x01
+#define DATA_AKM8963_BIT 0x10
+#define DATA_AKM_STAT_MASK 0x0C
+
+#define DATA_AKM8975_SCALE (9830 * (1L << 15))
+#define DATA_AKM8972_SCALE (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE0 (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE1 (4915 * (1L << 15))
+#define AKM8963_SCALE_SHIFT 4
+#define NUM_BYTES_COMPASS_SLAVE 8
+
+/*register and associated bit definition*/
+#define REG_3050_FIFO_EN 0x12
+#define BITS_3050_ACCL_OUT 0x0E
+
+#define REG_3050_AUX_VDDIO 0x13
+#define BIT_3050_VDDIO 0x04
+
+#define REG_3050_SLAVE_ADDR 0x14
+#define REG_3050_SAMPLE_RATE_DIV 0x15
+#define REG_3050_LPF 0x16
+#define REG_3050_INT_ENABLE 0x17
+#define REG_3050_AUX_BST_ADDR 0x18
+#define REG_3050_INT_STATUS 0x1A
+#define REG_3050_TEMPERATURE 0x1B
+#define REG_3050_RAW_GYRO 0x1D
+#define REG_3050_AUX_XOUT_H 0x23
+#define REG_3050_FIFO_COUNT_H 0x3A
+#define REG_3050_FIFO_R_W 0x3C
+
+#define REG_3050_USER_CTRL 0x3D
+#define BIT_3050_AUX_IF_EN 0x20
+#define BIT_3050_FIFO_RST 0x02
+
+#define REG_3050_PWR_MGMT_1 0x3E
+#define BITS_3050_POWER1 0x30
+#define BITS_3050_POWER2 0x10
+#define BITS_3050_GYRO_STANDBY 0x38
+
+#define REG_3500_OTP 0x0
+
+#define REG_YGOFFS_TC 0x1
+#define BIT_I2C_MST_VDDIO 0x80
+
+#define REG_XA_OFFS_L_TC 0x7
+#define REG_PRODUCT_ID 0xC
+#define REG_ST_GCT_X 0xD
+#define REG_SAMPLE_RATE_DIV 0x19
+#define REG_CONFIG 0x1A
+
+#define REG_GYRO_CONFIG 0x1B
+#define BITS_SELF_TEST_EN 0xE0
+
+#define REG_ACCEL_CONFIG 0x1C
+#define REG_ACCEL_MOT_THR 0x1F
+#define REG_ACCEL_MOT_DUR 0x20
+
+#define REG_FIFO_EN 0x23
+#define BIT_ACCEL_OUT 0x08
+#define BITS_GYRO_OUT 0x70
+
+
+#define REG_I2C_MST_CTRL 0x24
+#define BIT_WAIT_FOR_ES 0x40
+
+#define REG_I2C_SLV0_ADDR 0x25
+#define BIT_I2C_READ 0x80
+
+#define REG_I2C_SLV0_REG 0x26
+
+#define REG_I2C_SLV0_CTRL 0x27
+#define BIT_SLV_EN 0x80
+
+#define REG_I2C_SLV1_ADDR 0x28
+#define REG_I2C_SLV1_REG 0x29
+#define REG_I2C_SLV1_CTRL 0x2A
+#define REG_I2C_SLV4_CTRL 0x34
+
+#define REG_INT_PIN_CFG 0x37
+#define BIT_BYPASS_EN 0x2
+
+#define REG_INT_ENABLE 0x38
+#define BIT_DATA_RDY_EN 0x01
+#define BIT_DMP_INT_EN 0x02
+#define BIT_ZMOT_EN 0x20
+#define BIT_MOT_EN 0x40
+#define BIT_6500_WOM_EN 0x40
+
+#define REG_DMP_INT_STATUS 0x39
+
+#define REG_INT_STATUS 0x3A
+#define BIT_MOT_INT 0x40
+#define BIT_ZMOT_INT 0x20
+
+#define REG_RAW_ACCEL 0x3B
+#define REG_TEMPERATURE 0x41
+#define REG_RAW_GYRO 0x43
+#define REG_EXT_SENS_DATA_00 0x49
+
+#define REG_ACCEL_INTEL_STATUS 0x61
+
+#define REG_I2C_SLV1_DO 0x64
+
+#define REG_I2C_MST_DELAY_CTRL 0x67
+#define BIT_SLV0_DLY_EN 0x01
+#define BIT_SLV1_DLY_EN 0x02
+
+#define REG_USER_CTRL 0x6A
+#define BIT_FIFO_RST 0x04
+#define BIT_DMP_RST 0x08
+#define BIT_I2C_MST_EN 0x20
+#define BIT_FIFO_EN 0x40
+#define BIT_DMP_EN 0x80
+
+#define REG_PWR_MGMT_1 0x6B
+#define BIT_H_RESET 0x80
+#define BIT_SLEEP 0x40
+#define BIT_CYCLE 0x20
+#define BIT_CLK_MASK 0x7
+
+#define REG_PWR_MGMT_2 0x6C
+#define BIT_PWR_ACCL_STBY 0x38
+#define BIT_PWR_GYRO_STBY 0x07
+#define BIT_LPA_FREQ 0xC0
+
+#define REG_BANK_SEL 0x6D
+#define REG_MEM_START_ADDR 0x6E
+#define REG_MEM_RW 0x6F
+#define REG_PRGM_STRT_ADDRH 0x70
+#define REG_FIFO_COUNT_H 0x72
+#define REG_FIFO_R_W 0x74
+#define REG_WHOAMI 0x75
+
+#define REG_6500_ACCEL_CONFIG2 0x1D
+#define BIT_ACCEL_FCHOCIE_B 0x08
+
+#define REG_6500_LP_ACCEL_ODR 0x1E
+#define REG_6500_ACCEL_WOM_THR 0x1F
+
+#define REG_6500_ACCEL_INTEL_CTRL 0x69
+#define BIT_ACCEL_INTEL_ENABLE 0x80
+#define BIT_ACCEL_INTEL_MODE 0x40
+
+/* data definitions */
+#define DMP_START_ADDR 0x400
+#define DMP_MASK_TAP 0x3f
+#define DMP_MASK_DIS_ORIEN 0xC0
+#define DMP_DIS_ORIEN_SHIFT 6
+
+#define BYTES_FOR_DMP 16
+#define BYTES_FOR_EVENTS 4
+#define QUATERNION_BYTES 16
+#define BYTES_PER_SENSOR 6
+#define MPU3050_FOOTER_SIZE 2
+#define FIFO_COUNT_BYTE 2
+#define FIFO_THRESHOLD 500
+#define POWER_UP_TIME 100
+#define SENSOR_UP_TIME 30
+#define MPU_MEM_BANK_SIZE 256
+#define MPU3050_TEMP_OFFSET 5383314L
+#define MPU3050_TEMP_SCALE 3834792L
+#define MPU6050_TEMP_OFFSET 2462307L
+#define MPU6050_TEMP_SCALE 2977653L
+#define MPU6XXX_MAX_MOTION_THRESH (255*4)
+#define MPU6XXX_MOTION_THRESH_SHIFT 5
+#define MPU6050_MOTION_DUR_DEFAULT 1
+#define MPU6050_ID 0x68
+#define MPU6050_MAX_MOTION_DUR 255
+#define MPU_TEMP_SHIFT 16
+#define LPA_FREQ_SHIFT 6
+#define COMPASS_RATE_SCALE 10
+#define MAX_GYRO_FS_PARAM 3
+#define MAX_ACCL_FS_PARAM 3
+#define MAX_LPA_FREQ_PARAM 3
+
+#define INIT_MOT_DUR 128
+#define INIT_MOT_THR 128
+#define INIT_ZMOT_DUR 128
+#define INIT_ZMOT_THR 128
+
+/*---- MPU6500 ----*/
+#define MPU6500_ID 0x70 /* unique WHOAMI */
+#define MPU6500_PRODUCT_REVISION 1
+#define MPU6500_MEM_REV_ADDR 0x17
+#define MPU6500_REV 2
+
+/*---- MPU9250 ----*/
+#define MPU9250_ID 0x71 /* unique WHOAMI */
+
+#define THREE_AXIS 3
+#define GYRO_CONFIG_FSR_SHIFT 3
+#define ACCL_CONFIG_FSR_SHIFT 3
+#define GYRO_DPS_SCALE 250
+#define MEM_ADDR_PROD_REV 0x6
+#define SOFT_PROD_VER_BYTES 5
+#define CRC_FIRMWARE_SEED 0
+#define SELF_TEST_SUCCESS 1
+#define MS_PER_DMP_TICK 20
+
+/* init parameters */
+#define INIT_FIFO_RATE 50
+#define INIT_DMP_OUTPUT_RATE 25
+#define INIT_DUR_TIME ((1000 / INIT_FIFO_RATE) * 1000 * 1000)
+#define INIT_TAP_THRESHOLD 100
+#define INIT_TAP_TIME 100
+#define INIT_TAP_MIN_COUNT 2
+#define MPL_PROD_KEY(ver, rev) (ver * 100 + rev)
+#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map))
+/*---- MPU6050 Silicon Revisions ----*/
+#define MPU_SILICON_REV_A2 1 /* MPU6050A2 Device */
+#define MPU_SILICON_REV_B1 2 /* MPU6050B1 Device */
+
+#define BIT_PRFTCH_EN 0x40
+#define BIT_CFG_USER_BANK 0x20
+#define BITS_MEM_SEL 0x1f
+
+#define TIME_STAMP_TOR 5
+#define MAX_CATCH_UP 5
+#define DEFAULT_ACCL_TRIM 16384
+#define DEFAULT_GYRO_TRIM 131
+#define MAX_FIFO_RATE 1000
+#define MIN_FIFO_RATE 4
+#define ONE_K_HZ 1000
+#define NS_PER_MS_SHIFT 20
+
+/* flick related defines */
+#define DATA_INT 2097
+#define DATA_MSG_ON 262144
+#define FLICK_INT_STATUS 8
+
+/*tap related defines */
+#define INV_TAP 0x08
+#define INV_NUM_TAP_AXES 3
+
+#define INV_TAP_AXIS_X_POS 0x20
+#define INV_TAP_AXIS_X_NEG 0x10
+#define INV_TAP_AXIS_Y_POS 0x08
+#define INV_TAP_AXIS_Y_NEG 0x04
+#define INV_TAP_AXIS_Z_POS 0x02
+#define INV_TAP_AXIS_Z_NEG 0x01
+#define INV_TAP_ALL_DIRECTIONS 0x3f
+
+#define INV_TAP_AXIS_X 0x1
+#define INV_TAP_AXIS_Y 0x2
+#define INV_TAP_AXIS_Z 0x4
+
+#define INV_TAP_AXIS_ALL \
+ (INV_TAP_AXIS_X | \
+ INV_TAP_AXIS_Y | \
+ INV_TAP_AXIS_Z)
+
+#define INT_SRC_TAP 0x01
+#define INT_SRC_DISPLAY_ORIENT 0x08
+#define INT_SRC_SHAKE 0x10
+
+#define INV_X_AXIS_INDEX 0x00
+#define INV_Y_AXIS_INDEX 0x01
+#define INV_Z_AXIS_INDEX 0x02
+
+#define INV_ELEMENT_1 0x0001
+#define INV_ELEMENT_2 0x0002
+#define INV_ELEMENT_3 0x0004
+#define INV_ELEMENT_4 0x0008
+#define INV_ELEMENT_5 0x0010
+#define INV_ELEMENT_6 0x0020
+#define INV_ELEMENT_7 0x0040
+#define INV_ELEMENT_8 0x0080
+#define INV_ALL 0xFFFF
+#define INV_ELEMENT_MASK 0x00FF
+#define INV_GYRO_ACC_MASK 0x007E
+#define INV_ACCL_MASK 0x70
+#define INV_GYRO_MASK 0xE
+/* scan element definition */
+enum inv_mpu_scan {
+ INV_MPU_SCAN_QUAT_R = 0,
+ INV_MPU_SCAN_QUAT_X,
+ INV_MPU_SCAN_QUAT_Y,
+ INV_MPU_SCAN_QUAT_Z,
+ INV_MPU_SCAN_GYRO_X,
+ INV_MPU_SCAN_GYRO_Y,
+ INV_MPU_SCAN_GYRO_Z,
+ INV_MPU_SCAN_ACCL_X,
+ INV_MPU_SCAN_ACCL_Y,
+ INV_MPU_SCAN_ACCL_Z,
+ INV_MPU_SCAN_MAGN_X,
+ INV_MPU_SCAN_MAGN_Y,
+ INV_MPU_SCAN_MAGN_Z,
+ INV_MPU_SCAN_TIMESTAMP,
+};
+
+enum inv_filter_e {
+ INV_FILTER_256HZ_NOLPF2 = 0,
+ INV_FILTER_188HZ,
+ INV_FILTER_98HZ,
+ INV_FILTER_42HZ,
+ INV_FILTER_20HZ,
+ INV_FILTER_10HZ,
+ INV_FILTER_5HZ,
+ INV_FILTER_2100HZ_NOLPF,
+ NUM_FILTER
+};
+
+enum inv_slave_mode {
+ INV_MODE_SUSPEND,
+ INV_MODE_NORMAL,
+};
+
+/*==== MPU6050B1 MEMORY ====*/
+enum MPU_MEMORY_BANKS {
+ MEM_RAM_BANK_0 = 0,
+ MEM_RAM_BANK_1,
+ MEM_RAM_BANK_2,
+ MEM_RAM_BANK_3,
+ MEM_RAM_BANK_4,
+ MEM_RAM_BANK_5,
+ MEM_RAM_BANK_6,
+ MEM_RAM_BANK_7,
+ MEM_RAM_BANK_8,
+ MEM_RAM_BANK_9,
+ MEM_RAM_BANK_10,
+ MEM_RAM_BANK_11,
+ MPU_MEM_NUM_RAM_BANKS,
+ MPU_MEM_OTP_BANK_0 = 16
+};
+
+/* IIO attribute address */
+enum MPU_IIO_ATTR_ADDR {
+ ATTR_DMP_FLICK_LOWER,
+ ATTR_DMP_FLICK_UPPER,
+ ATTR_DMP_FLICK_COUNTER,
+ ATTR_DMP_FLICK_INT_ON,
+ ATTR_DMP_FLICK_AXIS,
+ ATTR_DMP_FLICK_MSG_ON,
+ ATTR_DMP_PEDOMETER_STEPS,
+ ATTR_DMP_PEDOMETER_TIME,
+ ATTR_DMP_TAP_THRESHOLD,
+ ATTR_DMP_TAP_MIN_COUNT,
+ ATTR_DMP_TAP_ON,
+ ATTR_DMP_TAP_TIME,
+ ATTR_DMP_ON,
+ ATTR_DMP_INT_ON,
+ ATTR_DMP_EVENT_INT_ON,
+ ATTR_DMP_OUTPUT_RATE,
+ ATTR_DMP_QUATERNION_ON,
+ ATTR_DMP_DISPLAY_ORIENTATION_ON,
+ ATTR_LPA_FREQ,
+ ATTR_SELF_TEST,
+ ATTR_KEY,
+ ATTR_GYRO_MATRIX,
+ ATTR_ACCL_MATRIX,
+ ATTR_COMPASS_MATRIX,
+ ATTR_GYRO_ENABLE,
+ ATTR_ACCL_ENABLE,
+ ATTR_COMPASS_ENABLE,
+ ATTR_POWER_STATE,
+ ATTR_FIRMWARE_LOADED,
+ ATTR_MOTION_ON,
+ ATTR_MOTION_DURATION,
+ ATTR_MOTION_THRESHOLD,
+#ifdef CONFIG_INV_TESTING
+ ATTR_I2C_COUNTERS,
+ ATTR_REG_WRITE,
+#endif
+};
+
+enum inv_accl_fs_e {
+ INV_FS_02G = 0,
+ INV_FS_04G,
+ INV_FS_08G,
+ INV_FS_16G,
+ NUM_ACCL_FSR
+};
+
+enum inv_fsr_e {
+ INV_FSR_250DPS = 0,
+ INV_FSR_500DPS,
+ INV_FSR_1000DPS,
+ INV_FSR_2000DPS,
+ NUM_FSR
+};
+
+enum inv_clock_sel_e {
+ INV_CLK_INTERNAL = 0,
+ INV_CLK_PLL,
+ NUM_CLK
+};
+
+ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t pos, size_t size);
+ssize_t inv_dmp_firmware_read(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count);
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev);
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev);
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev);
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev);
+int inv_init_config_mpu3050(struct iio_dev *indio_dev);
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_iio_s *st);
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_iio_s *st);
+int set_3050_bypass(struct inv_mpu_iio_s *st, bool enable);
+int inv_register_mpu3050_slave(struct inv_mpu_iio_s *st);
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg);
+int inv_switch_3050_gyro_engine(struct inv_mpu_iio_s *st, bool en);
+int inv_switch_3050_accl_engine(struct inv_mpu_iio_s *st, bool en);
+int set_power_mpu3050(struct inv_mpu_iio_s *st, bool power_on);
+int set_inv_enable(struct iio_dev *indio_dev, bool enable);
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_iio_s *st, bool on);
+int inv_send_quaternion(struct inv_mpu_iio_s *st, bool on);
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_iio_s *st, bool on);
+int inv_set_fifo_rate(struct inv_mpu_iio_s *st, u16 fifo_rate);
+u16 inv_dmp_get_address(u16 key);
+int inv_q30_mult(int a, int b);
+int inv_set_tap_threshold_dmp(struct inv_mpu_iio_s *st,
+ u32 axis, u16 threshold);
+int inv_set_min_taps_dmp(struct inv_mpu_iio_s *st, u16 min_taps);
+int inv_set_tap_time_dmp(struct inv_mpu_iio_s *st, u16 time);
+int inv_enable_tap_dmp(struct inv_mpu_iio_s *st, bool on);
+int inv_i2c_read_base(struct inv_mpu_iio_s *st, u16 i2c_addr,
+ u8 reg, u16 length, u8 *data);
+int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
+ u16 i2c_addr, u8 reg, u8 data);
+int inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
+ int *gyro_result, int *accl_result);
+int inv_hw_self_test(struct inv_mpu_iio_s *st);
+int inv_hw_self_test_6500(struct inv_mpu_iio_s *st);
+void inv_recover_setting(struct inv_mpu_iio_s *st);
+s64 get_time_ns(void);
+int write_be32_key_to_mem(struct inv_mpu_iio_s *st,
+ u32 data, int key);
+int inv_set_accel_bias_dmp(struct inv_mpu_iio_s *st);
+int inv_send_sensor_data(struct inv_mpu_iio_s *st, u16 elements);
+int inv_send_interrupt_word(struct inv_mpu_iio_s *st, bool on);
+int mpu_memory_write(struct inv_mpu_iio_s *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data);
+int mpu_memory_read(struct inv_mpu_iio_s *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data);
+int mpu_memory_write_unaligned(struct inv_mpu_iio_s *st, u16 key, int len,
+ u8 const *d);
+/* used to print i2c data using pr_debug */
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string);
+char *wr_pr_debug_end(char *string);
+
+#define mem_w(a, b, c) \
+ mpu_memory_write(st, st->i2c_addr, a, b, c)
+#define mem_w_key(key, b, c) mpu_memory_write_unaligned(st, key, b, c)
+#define inv_i2c_read(st, reg, len, data) \
+ inv_i2c_read_base(st, st->i2c_addr, reg, len, data)
+#define inv_i2c_single_write(st, reg, data) \
+ inv_i2c_single_write_base(st, st->i2c_addr, reg, data)
+#define inv_secondary_read(reg, len, data) \
+ inv_i2c_read_base(st, st->plat_data.secondary_i2c_addr, reg, len, data)
+#define inv_secondary_write(reg, data) \
+ inv_i2c_single_write_base(st, st->plat_data.secondary_i2c_addr, \
+ reg, data)
+
+#endif /* #ifndef _INV_MPU_IIO_H_ */
+
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_misc.c b/drivers/iio/imu/inv_mpu/inv_mpu_misc.c
new file mode 100644
index 00000000..22351018
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_misc.c
@@ -0,0 +1,1874 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_mpu_misc.c
+ * @brief A sysfs device driver for Invensense mpu.
+ * @details This file is part of invensense mpu driver code
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/crc32.h>
+
+#include "inv_mpu_iio.h"
+#include "inv_counters.h"
+
+/* DMP defines */
+#define DMP_ORIENTATION_TIME 500
+#define DMP_ORIENTATION_ANGLE 60
+#define DMP_DEFAULT_FIFO_RATE 200
+#define DMP_TAP_SCALE (767603923 / 5)
+#define DMP_MULTI_SHIFT 30
+#define DMP_MULTI_TAP_TIME 500
+#define DMP_SHAKE_REJECT_THRESH 100
+#define DMP_SHAKE_REJECT_TIME 10
+#define DMP_SHAKE_REJECT_TIMEOUT 10
+#define DMP_ANGLE_SCALE 15
+#define DMP_PRECISION 1000
+#define DMP_MAX_DIVIDER 4
+#define DMP_MAX_MIN_TAPS 4
+#define DMP_IMAGE_CRC_VALUE 0xa87fd63c
+#define DMP_IMAGE_SIZE 3058
+
+/*--- Test parameters defaults --- */
+#define DEF_OLDEST_SUPP_PROD_REV 8
+#define DEF_OLDEST_SUPP_SW_REV 2
+
+/* sample rate */
+#define DEF_SELFTEST_SAMPLE_RATE 0
+/* LPF parameter */
+#define DEF_SELFTEST_LPF_PARA 1
+/* full scale setting dps */
+#define DEF_SELFTEST_GYRO_FULL_SCALE (0 << 3)
+#define DEF_SELFTEST_ACCL_FULL_SCALE (2 << 3)
+#define DEF_SELFTEST_GYRO_SENS (32768 / 250)
+/* wait time before collecting data */
+#define DEF_GYRO_WAIT_TIME 50
+#define DEF_ST_STABLE_TIME 200
+#define DEF_GYRO_PACKET_THRESH DEF_GYRO_WAIT_TIME
+#define DEF_GYRO_THRESH 10
+#define DEF_GYRO_SCALE 131
+#define DEF_ST_PRECISION 1000
+#define DEF_ST_ACCL_FULL_SCALE 8000UL
+#define DEF_ST_SCALE (1L << 15)
+#define DEF_ST_TRY_TIMES 2
+#define DEF_ST_COMPASS_RESULT_SHIFT 2
+#define DEF_ST_ACCEL_RESULT_SHIFT 1
+#define DEF_ST_OTP0_THRESH 60
+#define DEF_ST_ABS_THRESH 20
+#define DEF_ST_TOR 2
+
+#define DEF_ST_COMPASS_WAIT_MIN (10 * 1000)
+#define DEF_ST_COMPASS_WAIT_MAX (15 * 1000)
+#define DEF_ST_COMPASS_TRY_TIMES 10
+#define DEF_ST_COMPASS_8963_SHIFT 2
+
+#define X 0
+#define Y 1
+#define Z 2
+/*---- MPU6050 notable product revisions ----*/
+#define MPU_PRODUCT_KEY_B1_E1_5 105
+#define MPU_PRODUCT_KEY_B2_F1 431
+/* accelerometer Hw self test min and max bias shift (mg) */
+#define DEF_ACCEL_ST_SHIFT_MIN 300
+#define DEF_ACCEL_ST_SHIFT_MAX 950
+
+#define DEF_ACCEL_ST_SHIFT_DELTA 140
+#define DEF_GYRO_CT_SHIFT_DELTA 140
+/* gyroscope Coriolis self test min and max bias shift (dps) */
+#define DEF_GYRO_CT_SHIFT_MIN 10
+#define DEF_GYRO_CT_SHIFT_MAX 105
+
+static struct test_setup_t test_setup = {
+ .gyro_sens = DEF_SELFTEST_GYRO_SENS,
+ .sample_rate = DEF_SELFTEST_SAMPLE_RATE,
+ .lpf = DEF_SELFTEST_LPF_PARA,
+ .fsr = DEF_SELFTEST_GYRO_FULL_SCALE,
+ .accl_fs = DEF_SELFTEST_ACCL_FULL_SCALE
+};
+
+/* NOTE: product entries are in chronological order */
+static const struct prod_rev_map_t prod_rev_map[] = {
+ /* prod_ver = 0 */
+ {MPL_PROD_KEY(0, 1), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 2), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 3), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 4), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 5), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 6), MPU_SILICON_REV_A2, 131, 16384},
+ /* prod_ver = 1 */
+ {MPL_PROD_KEY(0, 7), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 8), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 9), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 10), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 11), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 12), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 13), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 14), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 15), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 27), MPU_SILICON_REV_A2, 131, 16384},
+ /* prod_ver = 1 */
+ {MPL_PROD_KEY(1, 16), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 17), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 18), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 19), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 20), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 28), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 1), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 2), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 3), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 4), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 5), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 6), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 2 */
+ {MPL_PROD_KEY(2, 7), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 8), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 9), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 10), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 11), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 12), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 29), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 3 */
+ {MPL_PROD_KEY(3, 30), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 4 */
+ {MPL_PROD_KEY(4, 31), MPU_SILICON_REV_B1, 131, 8192},
+ {MPL_PROD_KEY(4, 1), MPU_SILICON_REV_B1, 131, 8192},
+ {MPL_PROD_KEY(4, 3), MPU_SILICON_REV_B1, 131, 8192},
+ /* prod_ver = 5 */
+ {MPL_PROD_KEY(5, 3), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 6 */
+ {MPL_PROD_KEY(6, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 7 */
+ {MPL_PROD_KEY(7, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 8 */
+ {MPL_PROD_KEY(8, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 9 */
+ {MPL_PROD_KEY(9, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 10 */
+ {MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384}
+};
+
+/*
+* List of product software revisions
+*
+* NOTE :
+* software revision 0 falls back to the old detection method
+* based off the product version and product revision per the
+* table above
+*/
+static const struct prod_rev_map_t sw_rev_map[] = {
+ {0, 0, 0, 0},
+ {1, MPU_SILICON_REV_B1, 131, 8192}, /* rev C */
+ {2, MPU_SILICON_REV_B1, 131, 16384} /* rev D */
+};
+
+static const int accl_st_tb[31] = {
+ 340, 351, 363, 375, 388, 401, 414, 428,
+ 443, 458, 473, 489, 506, 523, 541, 559,
+ 578, 597, 617, 638, 660, 682, 705, 729,
+ 753, 779, 805, 832, 860, 889, 919};
+static const int gyro_6050_st_tb[31] = {
+ 3275, 3425, 3583, 3748, 3920, 4100, 4289, 4486,
+ 4693, 4909, 5134, 5371, 5618, 5876, 6146, 6429,
+ 6725, 7034, 7358, 7696, 8050, 8421, 8808, 9213,
+ 9637, 10080, 10544, 11029, 11537, 12067, 12622};
+static const int gyro_3500_st_tb[255] = {
+ 2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+ 2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+ 3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+ 3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+ 3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+ 3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+ 4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+ 4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+ 4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+ 5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+ 5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+ 6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+ 6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+ 7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+ 7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+ 8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+ 9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+ 10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+ 10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+ 11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+ 12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+ 13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+ 15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+ 16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+ 17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+ 19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+ 20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+ 22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+ 24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+ 26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+ 28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+ 30903, 31212, 31524, 31839, 32157, 32479, 32804};
+
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string)
+{
+ int ii;
+ string = kmalloc(len * 2 + 1, GFP_KERNEL);
+ for (ii = 0; ii < len; ii++)
+ sprintf(&string[ii * 2], "%02X", data[ii]);
+ string[len * 2] = 0;
+ return string;
+}
+
+char *wr_pr_debug_end(char *string)
+{
+ kfree(string);
+ return "";
+}
+
+int mpu_memory_write(struct inv_mpu_iio_s *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf[513];
+
+ struct i2c_msg msgs[3];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ if (len >= (sizeof(buf) - 1))
+ return -ENOMEM;
+
+ bank[0] = REG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf[0] = REG_MEM_RW;
+ memcpy(buf + 1, data, len);
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = (u8 *)buf;
+ msgs[2].len = len + 1;
+
+ INV_I2C_INC_MPUWRITE(3 + 3 + (2 + len));
+#if CONFIG_DYNAMIC_DEBUG
+ {
+ char *write = 0;
+ pr_debug("%s WM%02X%02X%02X%s%s - %d\n", st->hw->name,
+ mpu_addr, bank[1], addr[1],
+ wr_pr_debug_begin(data, len, write),
+ wr_pr_debug_end(write),
+ len);
+ }
+#endif
+
+ res = i2c_transfer(st->sl_handle, msgs, 3);
+ if (res != 3) {
+ if (res >= 0)
+ res = -EIO;
+ return res;
+ } else {
+ return 0;
+ }
+}
+
+int mpu_memory_read(struct inv_mpu_iio_s *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf;
+
+ struct i2c_msg msgs[4];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ bank[0] = REG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf = REG_MEM_RW;
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = &buf;
+ msgs[2].len = 1;
+
+ msgs[3].addr = mpu_addr;
+ msgs[3].flags = I2C_M_RD;
+ msgs[3].buf = data;
+ msgs[3].len = len;
+
+ res = i2c_transfer(st->sl_handle, msgs, 4);
+ if (res != 4) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+
+ INV_I2C_INC_MPUWRITE(3 + 3 + 3);
+ INV_I2C_INC_MPUREAD(len);
+#if CONFIG_DYNAMIC_DEBUG
+ {
+ char *read = 0;
+ pr_debug("%s RM%02X%02X%02X%02X - %s%s\n", st->hw->name,
+ mpu_addr, bank[1], addr[1], len,
+ wr_pr_debug_begin(data, len, read),
+ wr_pr_debug_end(read));
+ }
+#endif
+
+ return res;
+}
+
+int mpu_memory_write_unaligned(struct inv_mpu_iio_s *st, u16 key, int len,
+ u8 const *d)
+{
+ int addr;
+ int start, end;
+ int len1, len2;
+ int result = 0;
+ if (len > MPU_MEM_BANK_SIZE)
+ return -EINVAL;
+ addr = inv_dmp_get_address(key);
+ start = (addr >> 8);
+ end = ((addr + len - 1) >> 8);
+ if (start == end) {
+ result = mpu_memory_write(st, st->i2c_addr, addr, len, d);
+ } else {
+ end <<= 8;
+ len1 = end - addr;
+ len2 = len - len1;
+ result = mpu_memory_write(st, st->i2c_addr, addr, len1, d);
+ result |= mpu_memory_write(st, st->i2c_addr, end, len2,
+ d + len1);
+ }
+
+ return result;
+}
+
+/**
+ * index_of_key()- Inverse lookup of the index of an MPL product key .
+ * @key: the MPL product indentifier also referred to as 'key'.
+ */
+static short index_of_key(u16 key)
+{
+ int i;
+ for (i = 0; i < NUM_OF_PROD_REVS; i++)
+ if (prod_rev_map[i].mpl_product_key == key)
+ return (short)i;
+ return -EINVAL;
+}
+
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_iio_s *st)
+{
+ struct inv_chip_info_s *chip_info = &st->chip_info;
+ int result;
+ u8 whoami, sw_rev;
+
+ result = inv_i2c_read(st, REG_WHOAMI, 1, &whoami);
+ if (result)
+ return result;
+ if (whoami != MPU6500_ID && whoami != MPU9250_ID)
+ return -EINVAL;
+
+ /*memory read need more time after power up */
+ msleep(POWER_UP_TIME);
+ result = mpu_memory_read(st, st->i2c_addr,
+ MPU6500_MEM_REV_ADDR, 1, &sw_rev);
+ if (sw_rev == 0) {
+ pr_warning("Rev 0 of MPU6500\n");
+ pr_warning("can't sit with other devices in same I2C bus\n");
+ }
+ if (result)
+ return result;
+ if (sw_rev > MPU6500_REV)
+ return -EINVAL;
+
+ /* these values are place holders and not real values */
+ chip_info->product_id = MPU6500_PRODUCT_REVISION;
+ chip_info->product_revision = MPU6500_PRODUCT_REVISION;
+ chip_info->silicon_revision = MPU6500_PRODUCT_REVISION;
+ chip_info->software_revision = sw_rev;
+ chip_info->gyro_sens_trim = DEFAULT_GYRO_TRIM;
+ chip_info->accl_sens_trim = DEFAULT_ACCL_TRIM;
+ chip_info->multi = 1;
+
+ return 0;
+}
+
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_iio_s *st)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+ u8 prod_ver = 0x00, prod_rev = 0x00;
+ struct prod_rev_map_t *p_rev;
+ u8 bank =
+ (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
+ u16 mem_addr = ((bank << 8) | MEM_ADDR_PROD_REV);
+ u16 key;
+ u8 regs[5];
+ u16 sw_rev;
+ short index;
+ struct inv_chip_info_s *chip_info = &st->chip_info;
+ reg = &st->reg;
+
+ result = inv_i2c_read(st, REG_PRODUCT_ID, 1, &prod_ver);
+ if (result)
+ return result;
+ prod_ver &= 0xf;
+ /*memory read need more time after power up */
+ msleep(POWER_UP_TIME);
+ result = mpu_memory_read(st, st->i2c_addr, mem_addr,
+ 1, &prod_rev);
+ if (result)
+ return result;
+ prod_rev >>= 2;
+ /* clean the prefetch and cfg user bank bits */
+ result = inv_i2c_single_write(st, reg->bank_sel, 0);
+ if (result)
+ return result;
+ /* get the software-product version, read from XA_OFFS_L */
+ result = inv_i2c_read(st, REG_XA_OFFS_L_TC,
+ SOFT_PROD_VER_BYTES, regs);
+ if (result)
+ return result;
+
+ sw_rev = (regs[4] & 0x01) << 2 | /* 0x0b, bit 0 */
+ (regs[2] & 0x01) << 1 | /* 0x09, bit 0 */
+ (regs[0] & 0x01); /* 0x07, bit 0 */
+ /* if 0, use the product key to determine the type of part */
+ if (sw_rev == 0) {
+ key = MPL_PROD_KEY(prod_ver, prod_rev);
+ if (key == 0)
+ return -EINVAL;
+ index = index_of_key(key);
+ if (index < 0 || index >= NUM_OF_PROD_REVS)
+ return -EINVAL;
+ /* check MPL is compiled for this device */
+ if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1)
+ return -EINVAL;
+ p_rev = (struct prod_rev_map_t *)&prod_rev_map[index];
+ /* if valid, use the software product key */
+ } else if (sw_rev < ARRAY_SIZE(sw_rev_map)) {
+ p_rev = (struct prod_rev_map_t *)&sw_rev_map[sw_rev];
+ } else {
+ return -EINVAL;
+ }
+ chip_info->product_id = prod_ver;
+ chip_info->product_revision = prod_rev;
+ chip_info->silicon_revision = p_rev->silicon_rev;
+ chip_info->software_revision = sw_rev;
+ chip_info->gyro_sens_trim = p_rev->gyro_trim;
+ chip_info->accl_sens_trim = p_rev->accel_trim;
+ if (chip_info->accl_sens_trim == 0)
+ chip_info->accl_sens_trim = DEFAULT_ACCL_TRIM;
+ chip_info->multi = DEFAULT_ACCL_TRIM / chip_info->accl_sens_trim;
+ if (chip_info->multi != 1)
+ pr_info("multi is %d\n", chip_info->multi);
+ return result;
+}
+
+/**
+ * read_accel_hw_self_test_prod_shift()- read the accelerometer hardware
+ * self-test bias shift calculated
+ * during final production test and
+ * stored in chip non-volatile memory.
+ * @st: main data structure.
+ * @st_prod: A pointer to an array of 3 elements to hold the values
+ * for production hardware self-test bias shifts returned to the
+ * user.
+ */
+static int read_accel_hw_self_test_prod_shift(struct inv_mpu_iio_s *st,
+ int *st_prod)
+{
+ u8 regs[4];
+ u8 shift_code[3];
+ int result, i;
+ st_prod[0] = 0;
+ st_prod[1] = 0;
+ st_prod[2] = 0;
+ result = inv_i2c_read(st, REG_ST_GCT_X, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+ if ((0 == regs[0]) && (0 == regs[1]) &&
+ (0 == regs[2]) && (0 == regs[3]))
+ return -EINVAL;
+ shift_code[X] = ((regs[0] & 0xE0) >> 3) | ((regs[3] & 0x30) >> 4);
+ shift_code[Y] = ((regs[1] & 0xE0) >> 3) | ((regs[3] & 0x0C) >> 2);
+ shift_code[Z] = ((regs[2] & 0xE0) >> 3) | (regs[3] & 0x03);
+ for (i = 0; i < 3; i++) {
+ if (shift_code[i] != 0)
+ st_prod[i] = test_setup.accl_sens[i]*
+ accl_st_tb[shift_code[i] - 1];
+ }
+
+ return 0;
+}
+/**
+* inv_check_accl_self_test()- check accel self test. this function returns
+* zero as success. A non-zero return value
+* indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_accl_self_test(struct inv_mpu_iio_s *st,
+ int *reg_avg, int *st_avg){
+ int gravity, reg_z_avg, g_z_sign, fs, j, ret_val;
+ int tmp1;
+ int st_shift_prod[THREE_AXIS], st_shift_cust[THREE_AXIS];
+ int st_shift_ratio[THREE_AXIS];
+ if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+ st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+ return 0;
+ fs = DEF_ST_ACCL_FULL_SCALE; /* assume +/- 2 mg as typical */
+ g_z_sign = 1;
+ ret_val = 0;
+ test_setup.accl_sens[X] = (u32)(DEF_ST_SCALE *
+ DEF_ST_PRECISION / fs);
+ test_setup.accl_sens[Y] = (u32)(DEF_ST_SCALE *
+ DEF_ST_PRECISION / fs);
+ test_setup.accl_sens[Z] = (u32)(DEF_ST_SCALE *
+ DEF_ST_PRECISION / fs);
+
+ if (MPL_PROD_KEY(st->chip_info.product_id,
+ st->chip_info.product_revision) ==
+ MPU_PRODUCT_KEY_B1_E1_5) {
+ /* half sensitivity Z accelerometer parts */
+ test_setup.accl_sens[Z] /= 2;
+ } else {
+ /* half sensitivity X, Y, Z accelerometer parts */
+ test_setup.accl_sens[X] /= st->chip_info.multi;
+ test_setup.accl_sens[Y] /= st->chip_info.multi;
+ test_setup.accl_sens[Z] /= st->chip_info.multi;
+ }
+ gravity = test_setup.accl_sens[Z];
+ reg_z_avg = reg_avg[Z] - g_z_sign * gravity*DEF_ST_PRECISION;
+ read_accel_hw_self_test_prod_shift(st, st_shift_prod);
+ for (j = 0; j < 3; j++) {
+ st_shift_cust[j] = abs(reg_avg[j] - st_avg[j]);
+ if (st_shift_prod[j]) {
+ tmp1 = st_shift_prod[j]/DEF_ST_PRECISION;
+ st_shift_ratio[j] = st_shift_cust[j]/tmp1
+ - DEF_ST_PRECISION;
+ if (st_shift_ratio[j] > DEF_ACCEL_ST_SHIFT_DELTA)
+ ret_val |= 1 << j;
+ if (st_shift_ratio[j] < -DEF_ACCEL_ST_SHIFT_DELTA)
+ ret_val |= 1 << j;
+ } else {
+ if (st_shift_cust[j] <
+ DEF_ACCEL_ST_SHIFT_MIN*gravity)
+ ret_val |= 1 << j;
+ if (st_shift_cust[j] >
+ DEF_ACCEL_ST_SHIFT_MAX*gravity)
+ ret_val |= 1 << j;
+ }
+ }
+
+ return ret_val;
+}
+/**
+* inv_check_3500_gyro_self_test() check gyro self test. this function returns
+* zero as success. A non-zero return value
+* indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+
+static int inv_check_3500_gyro_self_test(struct inv_mpu_iio_s *st,
+ int *reg_avg, int *st_avg){
+ int result;
+ int gst[3], ret_val;
+ int gst_otp[3], i;
+ u8 st_code[THREE_AXIS];
+ ret_val = 0;
+
+ for (i = 0; i < 3; i++)
+ gst[i] = st_avg[i] - reg_avg[i];
+ result = inv_i2c_read(st, REG_3500_OTP, THREE_AXIS, st_code);
+ if (result)
+ return result;
+ gst_otp[0] = 0;
+ gst_otp[1] = 0;
+ gst_otp[2] = 0;
+ for (i = 0; i < 3; i++) {
+ if (st_code[i] != 0)
+ gst_otp[i] = gyro_3500_st_tb[st_code[i] - 1];
+ }
+ /* check self test value passing criterion. Using the DEF_ST_TOR
+ * for certain degree of tolerance */
+ for (i = 0; i < 3; i++) {
+ if (gst_otp[i] == 0) {
+ if (abs(gst[i]) * DEF_ST_TOR < DEF_ST_OTP0_THRESH *
+ DEF_ST_PRECISION *
+ DEF_GYRO_SCALE)
+ ret_val |= (1 << i);
+ } else {
+ if (abs(gst[i]/gst_otp[i] - DEF_ST_PRECISION) >
+ DEF_GYRO_CT_SHIFT_DELTA)
+ ret_val |= (1 << i);
+ }
+ }
+ /* check for absolute value passing criterion. Using DEF_ST_TOR
+ * for certain degree of tolerance */
+ for (i = 0; i < 3; i++) {
+ if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH *
+ DEF_ST_PRECISION * DEF_GYRO_SCALE)
+ ret_val |= (1 << i);
+ }
+
+ return ret_val;
+}
+
+/**
+* inv_check_6050_gyro_self_test() - check 6050 gyro self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_6050_gyro_self_test(struct inv_mpu_iio_s *st,
+ int *reg_avg, int *st_avg){
+ int result;
+ int ret_val;
+ int ct_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+ u8 regs[3];
+ if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+ st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+ return 0;
+
+ ret_val = 0;
+ result = inv_i2c_read(st, REG_ST_GCT_X, 3, regs);
+ if (result)
+ return result;
+ regs[X] &= 0x1f;
+ regs[Y] &= 0x1f;
+ regs[Z] &= 0x1f;
+
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0)
+ ct_shift_prod[i] = gyro_6050_st_tb[regs[i] - 1];
+ else
+ ct_shift_prod[i] = 0;
+ }
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = abs(reg_avg[i] - st_avg[i]);
+ if (ct_shift_prod[i]) {
+ st_shift_ratio[i] = st_shift_cust[i] /
+ ct_shift_prod[i] - DEF_ST_PRECISION;
+ if (st_shift_ratio[i] > DEF_GYRO_CT_SHIFT_DELTA)
+ ret_val |= 1 << i;
+ if (st_shift_ratio[i] < -DEF_GYRO_CT_SHIFT_DELTA)
+ ret_val |= 1 << i;
+ } else {
+ if (st_shift_cust[i] < DEF_ST_PRECISION *
+ DEF_GYRO_CT_SHIFT_MIN * test_setup.gyro_sens)
+ ret_val |= 1 << i;
+ if (st_shift_cust[i] > DEF_ST_PRECISION *
+ DEF_GYRO_CT_SHIFT_MAX * test_setup.gyro_sens)
+ ret_val |= 1 << i;
+ }
+ }
+ /* check for absolute value passing criterion. Using DEF_ST_TOR
+ * for certain degree of tolerance */
+ for (i = 0; i < 3; i++) {
+ if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH *
+ DEF_ST_PRECISION * DEF_GYRO_SCALE)
+ ret_val |= (1 << i);
+ }
+
+ return ret_val;
+}
+
+/**
+ * inv_do_test() - do the actual test of self testing
+ */
+int inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
+ int *gyro_result, int *accl_result)
+{
+ struct inv_reg_map_s *reg;
+ int result, i, j, packet_size;
+ u8 data[BYTES_PER_SENSOR * 2], has_accl;
+ int fifo_count, packet_count, ind;
+
+ reg = &st->reg;
+ has_accl = (st->chip_type != INV_ITG3500);
+ packet_size = BYTES_PER_SENSOR*(1 + has_accl);
+
+ result = inv_i2c_single_write(st, reg->int_enable, 0);
+ if (result)
+ return result;
+ /* disable the sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ return result;
+ /* disable fifo reading */
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ if (result)
+ return result;
+ /* clear FIFO */
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_RST);
+ if (result)
+ return result;
+ /* setup parameters */
+ result = inv_i2c_single_write(st, reg->lpf, test_setup.lpf);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->sample_rate_div,
+ test_setup.sample_rate);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->gyro_config,
+ self_test_flag | test_setup.fsr);
+ if (result)
+ return result;
+ if (has_accl) {
+ result = inv_i2c_single_write(st, reg->accl_config,
+ self_test_flag | test_setup.accl_fs);
+ if (result)
+ return result;
+ }
+ /* wait for the output to get stable */
+ if (self_test_flag)
+ msleep(DEF_ST_STABLE_TIME);
+
+ /* enable FIFO reading */
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_EN);
+ if (result)
+ return result;
+ /* enable sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, BITS_GYRO_OUT
+ | (has_accl << 3));
+ if (result)
+ return result;
+ mdelay(DEF_GYRO_WAIT_TIME);
+ /* stop sending data to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ return result;
+ result = inv_i2c_read(st, reg->fifo_count_h, FIFO_COUNT_BYTE, data);
+ if (result)
+ return result;
+ fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+ packet_count = fifo_count / packet_size;
+ for (i = 0; i < 3; i++) {
+ gyro_result[i] = 0;
+ accl_result[i] = 0;
+ }
+ if (abs(packet_count - DEF_GYRO_PACKET_THRESH) > DEF_GYRO_THRESH)
+ return -EAGAIN;
+
+ for (i = 0; i < packet_count; i++) {
+ /* getting FIFO data */
+ result = inv_i2c_read(st, reg->fifo_r_w,
+ packet_size, data);
+ if (result)
+ return result;
+ ind = 0;
+ if (has_accl) {
+ for (j = 0; j < THREE_AXIS; j++)
+ accl_result[j] +=
+ (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ ind += BYTES_PER_SENSOR;
+ }
+ for (j = 0; j < THREE_AXIS; j++)
+ gyro_result[j] +=
+ (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ }
+
+ gyro_result[0] = gyro_result[0] * DEF_ST_PRECISION / packet_count;
+ gyro_result[1] = gyro_result[1] * DEF_ST_PRECISION / packet_count;
+ gyro_result[2] = gyro_result[2] * DEF_ST_PRECISION / packet_count;
+ if (has_accl) {
+ accl_result[0] =
+ accl_result[0] * DEF_ST_PRECISION / packet_count;
+ accl_result[1] =
+ accl_result[1] * DEF_ST_PRECISION / packet_count;
+ accl_result[2] =
+ accl_result[2] * DEF_ST_PRECISION / packet_count;
+ }
+
+ return 0;
+}
+
+/**
+ * inv_recover_setting() recover the old settings after everything is done
+ */
+
+void inv_recover_setting(struct inv_mpu_iio_s *st)
+{
+ struct inv_reg_map_s *reg;
+ int data;
+ struct iio_dev *indio = iio_priv_to_dev(st);
+
+ reg = &st->reg;
+ set_inv_enable(indio, st->chip_config.enable);
+ inv_i2c_single_write(st, reg->gyro_config,
+ st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT);
+ inv_i2c_single_write(st, reg->lpf, st->chip_config.lpf);
+ data = ONE_K_HZ/st->chip_config.fifo_rate - 1;
+ inv_i2c_single_write(st, reg->sample_rate_div, data);
+ if (INV_ITG3500 != st->chip_type) {
+ inv_i2c_single_write(st, reg->accl_config,
+ (st->chip_config.accl_fs <<
+ ACCL_CONFIG_FSR_SHIFT));
+ }
+ st->set_power_state(st, !st->chip_config.is_asleep);
+}
+
+static int inv_check_compass_self_test(struct inv_mpu_iio_s *st)
+{
+ int result;
+ u8 data[6];
+ u8 counter, cntl;
+ short x, y, z;
+ u8 *sens;
+ sens = st->chip_info.compass_sens;
+
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result) {
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ return result;
+ }
+ /* set to power down mode */
+ result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PD);
+ if (result)
+ goto AKM_fail;
+
+ /* write 1 to ASTC register */
+ result = inv_secondary_write(REG_AKM_ST_CTRL, DATA_AKM_SELF_TEST);
+ if (result)
+ goto AKM_fail;
+ /* set self test mode */
+ result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_ST);
+ if (result)
+ goto AKM_fail;
+ counter = DEF_ST_COMPASS_TRY_TIMES;
+ while (counter > 0) {
+ usleep_range(DEF_ST_COMPASS_WAIT_MIN, DEF_ST_COMPASS_WAIT_MAX);
+ result = inv_secondary_read(REG_AKM_STATUS, 1, data);
+ if (result)
+ goto AKM_fail;
+ if ((data[0] & DATA_AKM_DRDY) == 0)
+ counter--;
+ else
+ counter = 0;
+ }
+ if ((data[0] & DATA_AKM_DRDY) == 0) {
+ result = -EINVAL;
+ goto AKM_fail;
+ }
+ result = inv_secondary_read(REG_AKM_MEASURE_DATA,
+ BYTES_PER_SENSOR, data);
+ if (result)
+ goto AKM_fail;
+
+ x = le16_to_cpup((__le16 *)(&data[0]));
+ y = le16_to_cpup((__le16 *)(&data[2]));
+ z = le16_to_cpup((__le16 *)(&data[4]));
+ x = ((x * (sens[0] + 128)) >> 8);
+ y = ((y * (sens[1] + 128)) >> 8);
+ z = ((z * (sens[2] + 128)) >> 8);
+ if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+ result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl);
+ if (result)
+ goto AKM_fail;
+ if (0 == (cntl & DATA_AKM8963_BIT)) {
+ x <<= DEF_ST_COMPASS_8963_SHIFT;
+ y <<= DEF_ST_COMPASS_8963_SHIFT;
+ z <<= DEF_ST_COMPASS_8963_SHIFT;
+ }
+ }
+ result = -EINVAL;
+ if (x > st->compass_st_upper[X] || x < st->compass_st_lower[X])
+ goto AKM_fail;
+ if (y > st->compass_st_upper[Y] || y < st->compass_st_lower[Y])
+ goto AKM_fail;
+ if (z > st->compass_st_upper[Z] || z < st->compass_st_lower[Z])
+ goto AKM_fail;
+ result = 0;
+AKM_fail:
+ /*write 0 to ASTC register */
+ result |= inv_secondary_write(REG_AKM_ST_CTRL, 0);
+ /*set to power down mode */
+ result |= inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PD);
+ /*restore to non-bypass mode */
+ result |= inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ return result;
+}
+
+static int inv_power_up_self_test(struct inv_mpu_iio_s *st)
+{
+ int result;
+ result = inv_i2c_single_write(st, st->reg.pwr_mgmt_1, INV_CLK_PLL);
+ if (result)
+ return result;
+ msleep(POWER_UP_TIME);
+ result = inv_i2c_single_write(st, st->reg.pwr_mgmt_2, 0);
+ if (result)
+ return result;
+ msleep(SENSOR_UP_TIME);
+
+ return 0;
+}
+
+/**
+ * inv_hw_self_test() - main function to do hardware self test
+ */
+int inv_hw_self_test(struct inv_mpu_iio_s *st)
+{
+ int result;
+ int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS];
+ int accl_bias_st[THREE_AXIS], accl_bias_regular[THREE_AXIS];
+ int test_times;
+ char compass_result, accel_result, gyro_result;
+ if (st->chip_config.is_asleep ||
+ st->chip_config.lpa_mode ||
+ (!st->chip_config.gyro_enable) ||
+ (!st->chip_config.accl_enable)) {
+ result = inv_power_up_self_test(st);
+ if (result)
+ return result;
+ }
+ compass_result = 0;
+ accel_result = 0;
+ gyro_result = 0;
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, 0, gyro_bias_regular,
+ accl_bias_regular);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ test_times = 0;
+ }
+ if (result)
+ goto test_fail;
+
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st,
+ accl_bias_st);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ break;
+ }
+ if (result)
+ goto test_fail;
+ if (st->chip_type == INV_ITG3500) {
+ gyro_result = !inv_check_3500_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+ } else {
+ if (st->chip_config.has_compass)
+ compass_result = !inv_check_compass_self_test(st);
+ accel_result = !inv_check_accl_self_test(st,
+ accl_bias_regular, accl_bias_st);
+ gyro_result = !inv_check_6050_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+ }
+test_fail:
+ inv_recover_setting(st);
+
+ return (compass_result << DEF_ST_COMPASS_RESULT_SHIFT) |
+ (accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result;
+}
+
+/**
+ * inv_hw_self_test_6500() - main function to do hardware self test for 6500
+ */
+int inv_hw_self_test_6500(struct inv_mpu_iio_s *st)
+{
+ int compass_result;
+ compass_result = !inv_check_compass_self_test(st);
+ return compass_result << DEF_ST_COMPASS_RESULT_SHIFT;
+}
+
+static int inv_load_firmware(struct inv_mpu_iio_s *st,
+ u8 *data, int size)
+{
+ int bank, write_size;
+ int result;
+ u16 memaddr;
+
+ /* Write and verify memory */
+ for (bank = 0; size > 0; bank++,
+ size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ memaddr = ((bank << 8) | 0x00);
+
+ result = mem_w(memaddr, write_size, data);
+ if (result)
+ return result;
+ }
+ return 0;
+}
+
+static int inv_verify_firmware(struct inv_mpu_iio_s *st,
+ u8 *data, int size)
+{
+ int bank, write_size;
+ int result;
+ u16 memaddr;
+ u8 firmware[MPU_MEM_BANK_SIZE];
+
+ /* Write and verify memory */
+ for (bank = 0; size > 0; bank++,
+ size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ memaddr = ((bank << 8) | 0x00);
+ result = mpu_memory_read(st,
+ st->i2c_addr, memaddr, write_size, firmware);
+ if (result)
+ return result;
+ if (0 != memcmp(firmware, data, write_size))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int inv_set_fifo_div(struct inv_mpu_iio_s *st,
+ u16 fifoRate)
+{
+ u8 regs[2];
+ int result = 0;
+ /*For some reason DINAC4 is defined as 0xb8, but DINBC4 is not*/
+ const u8 regs_end[12] = {DINAFE, DINAF2, DINAAB, 0xc4,
+ DINAAA, DINAF1, DINADF, DINADF,
+ 0xbb, 0xaf, DINADF, DINADF};
+
+ regs[0] = (u8)((fifoRate >> 8) & 0xff);
+ regs[1] = (u8)(fifoRate & 0xff);
+ result = mem_w_key(KEY_D_0_22, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+
+ /*Modify the FIFO handler to reset the tap/orient interrupt flags*/
+ /* each time the FIFO handler runs*/
+ result = mem_w_key(KEY_CFG_6, ARRAY_SIZE(regs_end), regs_end);
+
+ return result;
+}
+
+int inv_send_quaternion(struct inv_mpu_iio_s *st, bool on)
+{
+ const u8 regs_on[] = {DINBC0, DINBC2,
+ DINBC4, DINBC6};
+ const u8 regs_off[] = {DINA80, DINA80,
+ DINA80, DINA80};
+ const u8 *regs;
+ u8 result;
+ if (on)
+ regs = regs_on;
+ else
+ regs = regs_off;
+ result = mem_w_key(KEY_CFG_LP_QUAT, ARRAY_SIZE(regs_on), regs);
+
+ return result;
+}
+
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_iio_s *st,
+ bool on)
+{
+ /*Turn on the display orientation interrupt in the DMP*/
+ int result;
+ u8 regs[] = {0xd8};
+
+ if (on)
+ regs[0] = 0xd9;
+ result = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, 1, regs);
+ return result;
+}
+
+int inv_set_fifo_rate(struct inv_mpu_iio_s *st, u16 fifo_rate)
+{
+ u8 divider;
+ int result;
+
+ divider = (u8)(ONE_K_HZ / fifo_rate) - 1;
+ if (divider > DMP_MAX_DIVIDER) {
+ st->sample_divider = DMP_MAX_DIVIDER;
+ st->fifo_divider =
+ (u8)(DMP_DEFAULT_FIFO_RATE / fifo_rate) - 1;
+ } else {
+ st->sample_divider = divider;
+ st->fifo_divider = 0;
+ }
+
+ result = inv_set_fifo_div(st, st->fifo_divider);
+ return result;
+}
+
+static int inv_set_tap_interrupt_dmp(struct inv_mpu_iio_s *st,
+ u8 on)
+{
+ int result;
+ u8 regs[] = {0};
+
+ if (on)
+ regs[0] = 0xf8;
+ else
+ regs[0] = DINAD8;
+ result = mem_w_key(KEY_CFG_20, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+ return result;
+}
+
+int inv_set_tap_threshold_dmp(struct inv_mpu_iio_s *st,
+ u32 axis, u16 threshold)
+{
+ /* Sets the tap threshold in the dmp
+ Simultaneously sets secondary tap threshold to help correct the tap
+ direction for soft taps */
+ int result;
+ /* DMP Algorithm */
+ u8 data[2];
+ int sampleDivider;
+ int scaledThreshold;
+ u32 dmpThreshold;
+ u8 sample_div;
+ const u32 accel_sens = (0x20000000 / 0x00010000);
+
+ if ((axis & ~(INV_TAP_AXIS_ALL)) || (threshold > (1 << 15)))
+ return -EINVAL;
+ sample_div = st->sample_divider;
+
+ sampleDivider = (1 + sample_div);
+ /* Scale factor corresponds linearly using
+ * 0 : 0
+ * 25 : 0.0250 g/ms
+ * 50 : 0.0500 g/ms
+ * 100: 1.0000 g/ms
+ * 200: 2.0000 g/ms
+ * 400: 4.0000 g/ms
+ * 800: 8.0000 g/ms
+ */
+ /*multiply by 1000 to avoid floating point 1000/1000*/
+ scaledThreshold = threshold;
+ /* Convert to per sample */
+ scaledThreshold *= sampleDivider;
+
+ /* Scale to DMP 16 bit value */
+ if (accel_sens != 0)
+ dmpThreshold = (u32)(scaledThreshold * accel_sens);
+ else
+ return -EINVAL;
+ dmpThreshold = dmpThreshold / DMP_PRECISION;
+
+ data[0] = dmpThreshold >> 8;
+ data[1] = dmpThreshold & 0xFF;
+
+ /* MPL algorithm */
+ if (axis & INV_TAP_AXIS_X) {
+ result = mem_w_key(KEY_DMP_TAP_THR_X, ARRAY_SIZE(data), data);
+ if (result)
+ return result;
+
+ /*Also set additional threshold for correcting the direction
+ of taps that were very near the threshold. */
+ data[0] = (dmpThreshold * 3 / 4) >> 8;
+ data[1] = (dmpThreshold * 3 / 4) & 0xFF;
+ result = mem_w_key(KEY_D_1_36, ARRAY_SIZE(data), data);
+ if (result)
+ return result;
+ }
+ if (axis & INV_TAP_AXIS_Y) {
+ result = mem_w_key(KEY_DMP_TAP_THR_Y, 2, data);
+ if (result)
+ return result;
+ data[0] = (dmpThreshold * 3 / 4) >> 8;
+ data[1] = (dmpThreshold * 3 / 4) & 0xFF;
+
+ result = mem_w_key(KEY_D_1_40, ARRAY_SIZE(data), data);
+ if (result)
+ return result;
+ }
+ if (axis & INV_TAP_AXIS_Z) {
+ result = mem_w_key(KEY_DMP_TAP_THR_Z, ARRAY_SIZE(data), data);
+ if (result)
+ return result;
+ data[0] = (dmpThreshold * 3 / 4) >> 8;
+ data[1] = (dmpThreshold * 3 / 4) & 0xFF;
+
+ result = mem_w_key(KEY_D_1_44, ARRAY_SIZE(data), data);
+ if (result)
+ return result;
+ }
+ return 0;
+}
+
+static int inv_set_tap_axes_dmp(struct inv_mpu_iio_s *st,
+ u32 axes)
+{
+ /* Sets a mask in the DMP that indicates what tap events
+ should result in an interrupt */
+ u8 regs[4];
+ u8 result;
+
+ /* check if any spurious bit other the ones expected are set */
+ if (axes & (~(INV_TAP_ALL_DIRECTIONS)))
+ return -EINVAL;
+
+ regs[0] = (u8)axes;
+ result = mem_w_key(KEY_D_1_72, 1, regs);
+
+ return result;
+}
+
+int inv_set_min_taps_dmp(struct inv_mpu_iio_s *st,
+ u16 min_taps) {
+ /*Indicates the minimum number of consecutive taps required
+ before the DMP will generate an interrupt */
+ u8 regs[1];
+ u8 result;
+ /* check if any spurious bit other the ones expected are set */
+ if ((min_taps > DMP_MAX_MIN_TAPS) || (min_taps < 1))
+ return -EINVAL;
+ regs[0] = (u8)(min_taps-1);
+ result = mem_w_key(KEY_D_1_79, ARRAY_SIZE(regs), regs);
+
+ return result;
+}
+
+int inv_set_tap_time_dmp(struct inv_mpu_iio_s *st, u16 time)
+{
+ /* Determines how long after a tap the DMP requires before
+ another tap can be registered*/
+ int result;
+ /* DMP Algorithm */
+ u16 dmpTime;
+ u8 data[2];
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ data[0] = dmpTime >> 8;
+ data[1] = dmpTime & 0xFF;
+
+ result = mem_w_key(KEY_DMP_TAPW_MIN, ARRAY_SIZE(data), data);
+
+ return result;
+}
+
+static int inv_set_multiple_tap_time_dmp(struct inv_mpu_iio_s *st,
+ u32 time)
+{
+ /*Determines how close together consecutive taps must occur
+ to be considered double/triple taps*/
+ int result;
+ /* DMP Algorithm */
+ u16 dmpTime;
+ u8 data[2];
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ data[0] = dmpTime >> 8;
+ data[1] = dmpTime & 0xFF;
+ result = mem_w_key(KEY_D_1_218, ARRAY_SIZE(data), data);
+
+ return result;
+}
+
+int inv_q30_mult(int a, int b)
+{
+ u64 temp;
+ int result;
+ temp = (u64)a * b;
+ result = (int)(temp >> DMP_MULTI_SHIFT);
+
+ return result;
+}
+
+static u16 inv_row_2_scale(const s8 *row)
+{
+ u16 b;
+
+ if (row[0] > 0)
+ b = 0;
+ else if (row[0] < 0)
+ b = 4;
+ else if (row[1] > 0)
+ b = 1;
+ else if (row[1] < 0)
+ b = 5;
+ else if (row[2] > 0)
+ b = 2;
+ else if (row[2] < 0)
+ b = 6;
+ else
+ b = 7;
+
+ return b;
+}
+
+/** Converts an orientation matrix made up of 0,+1,and -1 to a scalar
+* representation.
+* @param[in] mtx Orientation matrix to convert to a scalar.
+* @return Description of orientation matrix. The lowest 2 bits (0 and 1)
+* represent the column the one is on for the
+* first row, with the bit number 2 being the sign. The next 2 bits
+* (3 and 4) represent
+* the column the one is on for the second row with bit number 5 being
+* the sign.
+* The next 2 bits (6 and 7) represent the column the one is on for the
+* third row with
+* bit number 8 being the sign. In binary the identity matrix would therefor
+* be: 010_001_000 or 0x88 in hex.
+*/
+static u16 inv_orientation_matrix_to_scaler(const signed char *mtx)
+{
+
+ u16 scalar;
+ scalar = inv_row_2_scale(mtx);
+ scalar |= inv_row_2_scale(mtx + 3) << 3;
+ scalar |= inv_row_2_scale(mtx + 6) << 6;
+
+ return scalar;
+}
+
+static int inv_gyro_dmp_cal(struct inv_mpu_iio_s *st)
+{
+ int inv_gyro_orient;
+ u8 regs[3];
+ int result;
+
+ u8 tmpD = DINA4C;
+ u8 tmpE = DINACD;
+ u8 tmpF = DINA6C;
+
+ inv_gyro_orient =
+ inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+ if ((inv_gyro_orient & 3) == 0)
+ regs[0] = tmpD;
+ else if ((inv_gyro_orient & 3) == 1)
+ regs[0] = tmpE;
+ else if ((inv_gyro_orient & 3) == 2)
+ regs[0] = tmpF;
+ if ((inv_gyro_orient & 0x18) == 0)
+ regs[1] = tmpD;
+ else if ((inv_gyro_orient & 0x18) == 0x8)
+ regs[1] = tmpE;
+ else if ((inv_gyro_orient & 0x18) == 0x10)
+ regs[1] = tmpF;
+ if ((inv_gyro_orient & 0xc0) == 0)
+ regs[2] = tmpD;
+ else if ((inv_gyro_orient & 0xc0) == 0x40)
+ regs[2] = tmpE;
+ else if ((inv_gyro_orient & 0xc0) == 0x80)
+ regs[2] = tmpF;
+
+ result = mem_w_key(KEY_FCFG_1, 3, regs);
+ if (result)
+ return result;
+
+ if (inv_gyro_orient & 4)
+ regs[0] = DINA36 | 1;
+ else
+ regs[0] = DINA36;
+ if (inv_gyro_orient & 0x20)
+ regs[1] = DINA56 | 1;
+ else
+ regs[1] = DINA56;
+ if (inv_gyro_orient & 0x100)
+ regs[2] = DINA76 | 1;
+ else
+ regs[2] = DINA76;
+
+ result = mem_w_key(KEY_FCFG_3, ARRAY_SIZE(regs), regs);
+
+ return result;
+}
+
+static int inv_accel_dmp_cal(struct inv_mpu_iio_s *st)
+{
+ int inv_accel_orient;
+ int result;
+ u8 regs[3];
+ const u8 tmp[3] = { DINA0C, DINAC9, DINA2C };
+ inv_accel_orient =
+ inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+ regs[0] = tmp[inv_accel_orient & 3];
+ regs[1] = tmp[(inv_accel_orient >> 3) & 3];
+ regs[2] = tmp[(inv_accel_orient >> 6) & 3];
+ result = mem_w_key(KEY_FCFG_2, 3, regs);
+ if (result)
+ return result;
+
+ regs[0] = DINA26;
+ regs[1] = DINA46;
+ regs[2] = DINA66;
+ if (inv_accel_orient & 4)
+ regs[0] |= 1;
+ if (inv_accel_orient & 0x20)
+ regs[1] |= 1;
+ if (inv_accel_orient & 0x100)
+ regs[2] |= 1;
+ result = mem_w_key(KEY_FCFG_7, ARRAY_SIZE(regs), regs);
+
+ return result;
+}
+
+static u16 inv_orientation_matrix_to_scalar(const s8 *mtx)
+{
+
+ u16 scalar;
+
+ /*
+ XYZ 010_001_000 Identity Matrix
+ XZY 001_010_000
+ YXZ 010_000_001
+ YZX 000_010_001
+ ZXY 001_000_010
+ ZYX 000_001_010
+ */
+
+ scalar = inv_row_2_scale(mtx);
+ scalar |= inv_row_2_scale(mtx + 3) << 3;
+ scalar |= inv_row_2_scale(mtx + 6) << 6;
+
+ return scalar;
+}
+
+int inv_set_accel_bias_dmp(struct inv_mpu_iio_s *st)
+{
+ int inv_accel_orient, result, i, accel_bias_body[3], out[3];
+ int tmp[] = {1, 1, 1};
+ int mask[] = {4, 0x20, 0x100};
+ int accel_sf = 0x20000000;/* 536870912 */
+ u8 *regs;
+
+ inv_accel_orient =
+ inv_orientation_matrix_to_scalar(st->plat_data.orientation);
+
+ for (i = 0; i < 3; i++)
+ if (inv_accel_orient & mask[i])
+ tmp[i] = -1;
+
+ for (i = 0; i < 3; i++)
+ accel_bias_body[i] = st->input_accel_bias[(inv_accel_orient >>
+ (i * 3)) & 3] * tmp[i];
+ for (i = 0; i < 3; i++)
+ accel_bias_body[i] = inv_q30_mult(accel_sf,
+ accel_bias_body[i]);
+ for (i = 0; i < 3; i++)
+ out[i] = cpu_to_be32p(&accel_bias_body[i]);
+ regs = (u8 *)out;
+ result = mem_w_key(KEY_D_ACCEL_BIAS, sizeof(out), regs);
+
+ return result;
+}
+
+static int inv_set_gyro_sf_dmp(struct inv_mpu_iio_s *st)
+{
+ /*The gyro threshold, in dps, above which taps will be rejected*/
+ int result;
+ /* DMP Algorithm */
+ u8 sampleDivider;
+ u32 gyro_sf;
+ const u32 gyro_sens = 0x03e80000;
+
+ sampleDivider = st->sample_divider;
+ gyro_sf = inv_q30_mult(gyro_sens,
+ (int)(DMP_TAP_SCALE * (sampleDivider + 1)));
+ result = write_be32_key_to_mem(st, gyro_sf, KEY_D_0_104);
+
+ return result;
+}
+
+static int inv_set_shake_reject_thresh_dmp(struct inv_mpu_iio_s *st,
+ int thresh)
+{ /*THIS FUNCTION FAILS MEM_W*/
+ /*The gyro threshold, in dps, above which taps will be rejected */
+ int result;
+ /* DMP Algorithm */
+ u8 sampleDivider;
+ int thresh_scaled;
+ u32 gyro_sf;
+ const u32 gyro_sens = 0x03e80000;
+ sampleDivider = st->sample_divider;
+ gyro_sf = inv_q30_mult(gyro_sens, (int)(DMP_TAP_SCALE *
+ (sampleDivider + 1)));
+ /* We're in units of DPS, convert it back to chip units*/
+ /*split the operation to aviod overflow of integer*/
+ thresh_scaled = gyro_sens / (1L << 16);
+ thresh_scaled = thresh_scaled / thresh;
+ thresh_scaled = gyro_sf / thresh_scaled;
+ result = write_be32_key_to_mem(st, thresh_scaled, KEY_D_1_92);
+
+ return result;
+}
+
+static int inv_set_shake_reject_time_dmp(struct inv_mpu_iio_s *st,
+ u32 time)
+{
+ /* How long a gyro axis must remain above its threshold
+ before taps are rejected */
+ int result;
+ /* DMP Algorithm */
+ u16 dmpTime;
+ u8 data[2];
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ data[0] = dmpTime >> 8;
+ data[1] = dmpTime & 0xFF;
+
+ result = mem_w_key(KEY_D_1_88, ARRAY_SIZE(data), data);
+ return result;
+}
+
+static int inv_set_shake_reject_timeout_dmp(struct inv_mpu_iio_s *st,
+ u32 time)
+{
+ /*How long the gyros must remain below their threshold,
+ after taps have been rejected, before taps can be detected again*/
+ int result;
+ /* DMP Algorithm */
+ u16 dmpTime;
+ u8 data[2];
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ data[0] = dmpTime >> 8;
+ data[1] = dmpTime & 0xFF;
+
+ result = mem_w_key(KEY_D_1_90, ARRAY_SIZE(data), data);
+ return result;
+}
+
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_iio_s *st, bool on)
+{
+ u8 result;
+ const u8 regs_on[] = {DINADA, DINADA, DINAB1, DINAB9,
+ DINAF3, DINA8B, DINAA3, DINA91,
+ DINAB6, DINADA, DINAB4, DINADA};
+ const u8 regs_off[] = {0xd8, 0xd8, 0xb1, 0xb9, 0xf3, 0x8b,
+ 0xa3, 0x91, 0xb6, 0x09, 0xb4, 0xd9};
+ /*For some reason DINAC4 is defined as 0xb8,
+ but DINBC4 is not defined.*/
+ const u8 regs_end[] = {DINAFE, DINAF2, DINAAB, 0xc4,
+ DINAAA, DINAF1, DINADF, DINADF,
+ 0xbb, 0xaf, DINADF, DINADF};
+ const u8 regs[] = {0, 0};
+ /* reset fifo count to zero */
+ result = mem_w_key(KEY_D_1_178, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+
+ if (on)
+ /*Sets the DMP to send an interrupt and put a FIFO packet
+ in the FIFO if and only if a tap/orientation event
+ just occurred*/
+ result = mem_w_key(KEY_CFG_FIFO_ON_EVENT, ARRAY_SIZE(regs_on),
+ regs_on);
+ else
+ /*Sets the DMP to send an interrupt and put a FIFO packet
+ in the FIFO at the rate specified by the FIFO div.
+ see inv_set_fifo_div in hw_setup.c to set the FIFO div.*/
+ result = mem_w_key(KEY_CFG_FIFO_ON_EVENT, ARRAY_SIZE(regs_off),
+ regs_off);
+ if (result)
+ return result;
+
+ result = mem_w_key(KEY_CFG_6, ARRAY_SIZE(regs_end), regs_end);
+
+ return result;
+}
+
+/**
+ * inv_enable_tap_dmp() - calling this function will enable/disable tap function.
+ */
+int inv_enable_tap_dmp(struct inv_mpu_iio_s *st, bool on)
+{
+ int result;
+ result = inv_set_tap_interrupt_dmp(st, on);
+ if (result)
+ return result;
+ if (on) {
+ result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_X,
+ st->tap.thresh);
+ if (result)
+ return result;
+ result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Y,
+ st->tap.thresh);
+ if (result)
+ return result;
+ result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Z,
+ st->tap.thresh);
+ if (result)
+ return result;
+ }
+
+ result = inv_set_tap_axes_dmp(st, INV_TAP_ALL_DIRECTIONS);
+ if (result)
+ return result;
+ result = inv_set_min_taps_dmp(st, st->tap.min_count);
+ if (result)
+ return result;
+
+ result = inv_set_tap_time_dmp(st, st->tap.time);
+ if (result)
+ return result;
+
+ result = inv_set_multiple_tap_time_dmp(st, DMP_MULTI_TAP_TIME);
+ if (result)
+ return result;
+
+ result = inv_set_gyro_sf_dmp(st);
+ if (result)
+ return result;
+
+ result = inv_set_shake_reject_thresh_dmp(st, DMP_SHAKE_REJECT_THRESH);
+ if (result)
+ return result;
+
+ result = inv_set_shake_reject_time_dmp(st, DMP_SHAKE_REJECT_TIME);
+ if (result)
+ return result;
+
+ result = inv_set_shake_reject_timeout_dmp(st,
+ DMP_SHAKE_REJECT_TIMEOUT);
+ return result;
+}
+
+int inv_send_sensor_data(struct inv_mpu_iio_s *st, u16 elements)
+{
+ int result;
+ u8 regs[] = {DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
+ DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
+ DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
+ DINAA0 + 3};
+
+ if (elements & INV_ELEMENT_1)
+ regs[0] = DINACA;
+ if (elements & INV_ELEMENT_2)
+ regs[4] = DINBC4;
+ if (elements & INV_ELEMENT_3)
+ regs[5] = DINACC;
+ if (elements & INV_ELEMENT_4)
+ regs[6] = DINBC6;
+ if ((elements & INV_ELEMENT_5) || (elements & INV_ELEMENT_6) ||
+ (elements & INV_ELEMENT_7)) {
+ regs[1] = DINBC0;
+ regs[2] = DINAC8;
+ regs[3] = DINBC2;
+ }
+ result = mem_w_key(KEY_CFG_15, ARRAY_SIZE(regs), regs);
+ return result;
+}
+
+int inv_send_interrupt_word(struct inv_mpu_iio_s *st, bool on)
+{
+ const u8 regs_on[] = { DINA20 };
+ const u8 regs_off[] = { DINAA3 };
+ u8 result;
+
+ if (on)
+ result = mem_w_key(KEY_CFG_27, ARRAY_SIZE(regs_on), regs_on);
+ else
+ result = mem_w_key(KEY_CFG_27, ARRAY_SIZE(regs_off), regs_off);
+
+ return result;
+}
+
+/**
+ * inv_dmp_firmware_write() - calling this function will load the firmware.
+ * This is the write function of file "dmp_firmware".
+ */
+ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t pos, size_t size)
+{
+ u8 *firmware;
+ int result;
+ struct inv_reg_map_s *reg;
+ struct iio_dev *indio_dev;
+ struct inv_mpu_iio_s *st;
+
+ indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ st = iio_priv(indio_dev);
+
+ if (st->chip_config.is_asleep || st->chip_config.firmware_loaded)
+ return -EINVAL;
+
+ reg = &st->reg;
+ if (DMP_IMAGE_SIZE != size) {
+ pr_err("wrong DMP image size\n");
+ return -EINVAL;
+ }
+
+ firmware = kmalloc(size, GFP_KERNEL);
+ if (!firmware)
+ return -ENOMEM;
+
+ memcpy(firmware, buf, size);
+ result = crc32(CRC_FIRMWARE_SEED, firmware, size);
+ if (DMP_IMAGE_CRC_VALUE != result) {
+ result = -EINVAL;
+ pr_err("firmware CRC error - 0x%08x vs 0x%08x\n",
+ result, DMP_IMAGE_CRC_VALUE);
+ goto firmware_write_fail;
+ }
+
+ result = inv_load_firmware(st, firmware, size);
+ if (result)
+ goto firmware_write_fail;
+
+ result = inv_verify_firmware(st, firmware, size);
+ if (result)
+ goto firmware_write_fail;
+
+ result = inv_i2c_single_write(st, reg->prgm_strt_addrh,
+ st->chip_config.prog_start_addr >> 8);
+ if (result)
+ goto firmware_write_fail;
+ result = inv_i2c_single_write(st, reg->prgm_strt_addrh + 1,
+ st->chip_config.prog_start_addr & 0xff);
+ if (result)
+ goto firmware_write_fail;
+
+ result = inv_set_fifo_rate(st, DMP_DEFAULT_FIFO_RATE);
+ if (result)
+ goto firmware_write_fail;
+ result = inv_gyro_dmp_cal(st);
+ if (result)
+ goto firmware_write_fail;
+ result = inv_accel_dmp_cal(st);
+ if (result)
+ goto firmware_write_fail;
+ st->chip_config.firmware_loaded = 1;
+ result = size;
+firmware_write_fail:
+ kfree(firmware);
+
+ return result;
+}
+
+ssize_t inv_dmp_firmware_read(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ int bank, write_size, size, data, result;
+ u16 memaddr;
+ struct iio_dev *indio_dev;
+ struct inv_mpu_iio_s *st;
+
+ size = count;
+ indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ st = iio_priv(indio_dev);
+
+ data = 0;
+ for (bank = 0; size > 0; bank++, size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ memaddr = (bank << 8);
+ result = mpu_memory_read(st,
+ st->i2c_addr, memaddr, write_size, &buf[data]);
+ if (result)
+ return result;
+ }
+
+ return count;
+}
+/**
+ * @}
+ */
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu/inv_mpu_ring.c
new file mode 100644
index 00000000..b578b1c5
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_ring.c
@@ -0,0 +1,1048 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_mpu_ring.c
+ * @brief A sysfs device driver for Invensense gyroscopes.
+ * @details This file is part of inv mpu iio driver code
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/iio/iio.h>
+#include <linux/gpio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/sysfs.h>
+
+#include "inv_mpu_iio.h"
+
+/**
+ * reset_fifo_mpu3050() - Reset FIFO related registers
+ * @st: Device driver instance.
+ */
+static int reset_fifo_mpu3050(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ u8 val, user_ctrl;
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ reg = &st->reg;
+
+ /* disable interrupt */
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config);
+ if (result)
+ return result;
+ /* disable the sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ goto reset_fifo_fail;
+ result = inv_i2c_read(st, reg->user_ctrl, 1, &user_ctrl);
+ if (result)
+ goto reset_fifo_fail;
+ /* disable fifo reading */
+ user_ctrl &= ~BIT_FIFO_EN;
+ st->chip_config.has_footer = 0;
+ /* reset fifo */
+ val = (BIT_3050_FIFO_RST | user_ctrl);
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ st->last_isr_time = get_time_ns();
+ if (st->chip_config.dmp_on) {
+ /* enable interrupt when DMP is done */
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config | BIT_DMP_INT_EN);
+ if (result)
+ return result;
+
+ result = inv_i2c_single_write(st, reg->user_ctrl,
+ BIT_FIFO_EN|user_ctrl);
+ if (result)
+ return result;
+ } else {
+ /* enable interrupt */
+ if (st->chip_config.accl_fifo_enable ||
+ st->chip_config.gyro_fifo_enable) {
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config | BIT_DATA_RDY_EN);
+ if (result)
+ return result;
+ }
+ /* enable FIFO reading and I2C master interface*/
+ result = inv_i2c_single_write(st, reg->user_ctrl,
+ BIT_FIFO_EN | user_ctrl);
+ if (result)
+ return result;
+ /* enable sensor output to FIFO and FIFO footer*/
+ val = 1;
+ if (st->chip_config.accl_fifo_enable)
+ val |= BITS_3050_ACCL_OUT;
+ if (st->chip_config.gyro_fifo_enable)
+ val |= BITS_GYRO_OUT;
+ result = inv_i2c_single_write(st, reg->fifo_en, val);
+ if (result)
+ return result;
+ }
+
+ return 0;
+reset_fifo_fail:
+ if (st->chip_config.dmp_on)
+ val = BIT_DMP_INT_EN;
+ else
+ val = BIT_DATA_RDY_EN;
+ inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config | val);
+ pr_err("reset fifo failed\n");
+
+ return result;
+}
+
+/**
+ * inv_lpa_mode() - store current low power mode settings
+ */
+static int inv_lpa_mode(struct inv_mpu_iio_s *st, int lpa_mode)
+{
+ unsigned long result;
+ u8 d;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d);
+ if (result)
+ return result;
+ if (lpa_mode)
+ d |= BIT_CYCLE;
+ else
+ d &= ~BIT_CYCLE;
+
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d);
+ if (result)
+ return result;
+ if (INV_MPU6500 == st->chip_type) {
+ if (lpa_mode)
+ result = inv_i2c_single_write(st,
+ REG_6500_ACCEL_CONFIG2,
+ BIT_ACCEL_FCHOCIE_B);
+ else
+ result = inv_i2c_single_write(st, 0,
+ BIT_ACCEL_FCHOCIE_B);
+
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * reset_fifo_itg() - Reset FIFO related registers.
+ * @st: Device driver instance.
+ */
+static int reset_fifo_itg(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result, data;
+ u8 val, int_word;
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ reg = &st->reg;
+
+ if (st->chip_config.lpa_mode) {
+ result = inv_lpa_mode(st, 0);
+ if (result) {
+ pr_err("reset lpa mode failed\n");
+ return result;
+ }
+ }
+ /* disable interrupt */
+ result = inv_i2c_single_write(st, reg->int_enable, 0);
+ if (result) {
+ pr_err("int_enable write failed\n");
+ return result;
+ }
+ /* disable the sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ goto reset_fifo_fail;
+ /* disable fifo reading */
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ if (result)
+ goto reset_fifo_fail;
+ int_word = 0;
+
+ /* MPU6500's BIT_6500_WOM_EN is the same as BIT_MOT_EN */
+ if (st->mot_int.mot_on)
+ int_word |= BIT_MOT_EN;
+
+ if (st->chip_config.dmp_on) {
+ val = (BIT_FIFO_RST | BIT_DMP_RST);
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ st->last_isr_time = get_time_ns();
+ if (st->chip_config.dmp_int_on) {
+ int_word |= BIT_DMP_INT_EN;
+ result = inv_i2c_single_write(st, reg->int_enable,
+ int_word);
+ if (result)
+ return result;
+ }
+ val = (BIT_DMP_EN | BIT_FIFO_EN);
+ if (st->chip_config.compass_enable &
+ (!st->chip_config.dmp_event_int_on))
+ val |= BIT_I2C_MST_EN;
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+
+ if (st->chip_config.compass_enable) {
+ /* I2C_MST_DLY is set according to sample rate,
+ slow down the power*/
+ data = max(COMPASS_RATE_SCALE *
+ st->chip_config.fifo_rate / ONE_K_HZ,
+ st->chip_config.fifo_rate /
+ st->chip_config.dmp_output_rate);
+ if (data > 0)
+ data -= 1;
+ result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL,
+ data);
+ if (result)
+ return result;
+ }
+ val = 0;
+ if (st->chip_config.accl_fifo_enable)
+ val |= INV_ACCL_MASK;
+ if (st->chip_config.gyro_fifo_enable)
+ val |= INV_GYRO_MASK;
+ result = inv_send_sensor_data(st, val);
+ if (result)
+ return result;
+ if (st->chip_config.display_orient_on || st->chip_config.tap_on)
+ result = inv_send_interrupt_word(st, true);
+ else
+ result = inv_send_interrupt_word(st, false);
+ } else {
+ /* reset FIFO and possibly reset I2C*/
+ val = BIT_FIFO_RST;
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ st->last_isr_time = get_time_ns();
+ /* enable interrupt */
+ if (st->chip_config.accl_fifo_enable ||
+ st->chip_config.gyro_fifo_enable ||
+ st->chip_config.compass_enable) {
+ int_word |= BIT_DATA_RDY_EN;
+ }
+ result = inv_i2c_single_write(st, reg->int_enable, int_word);
+ if (result)
+ return result;
+ /* enable FIFO reading and I2C master interface*/
+ val = BIT_FIFO_EN;
+ if (st->chip_config.compass_enable)
+ val |= BIT_I2C_MST_EN;
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ if (st->chip_config.compass_enable) {
+ /* I2C_MST_DLY is set according to sample rate,
+ slow down the power*/
+ data = COMPASS_RATE_SCALE *
+ st->chip_config.fifo_rate / ONE_K_HZ;
+ if (data > 0)
+ data -= 1;
+ result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL,
+ data);
+ if (result)
+ return result;
+ }
+ /* enable sensor output to FIFO */
+ val = 0;
+ if (st->chip_config.gyro_fifo_enable)
+ val |= BITS_GYRO_OUT;
+ if (st->chip_config.accl_fifo_enable)
+ val |= BIT_ACCEL_OUT;
+ result = inv_i2c_single_write(st, reg->fifo_en, val);
+ if (result)
+ goto reset_fifo_fail;
+ }
+ st->chip_config.normal_compass_measure = 0;
+ result = inv_lpa_mode(st, st->chip_config.lpa_mode);
+ if (result)
+ goto reset_fifo_fail;
+
+ return 0;
+
+reset_fifo_fail:
+ if (st->chip_config.dmp_on)
+ val = BIT_DMP_INT_EN;
+ else
+ val = BIT_DATA_RDY_EN;
+ inv_i2c_single_write(st, reg->int_enable, val);
+ pr_err("reset fifo failed\n");
+
+ return result;
+}
+
+/**
+ * inv_clear_kfifo() - clear time stamp fifo
+ * @st: Device driver instance.
+ */
+static void inv_clear_kfifo(struct inv_mpu_iio_s *st)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&st->time_stamp_lock, flags);
+ kfifo_reset(&st->timestamps);
+ spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
+/**
+ * inv_reset_fifo() - Reset FIFO related registers.
+ * @st: Device driver instance.
+ */
+static int inv_reset_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+ inv_clear_kfifo(st);
+ if (INV_MPU3050 == st->chip_type)
+ return reset_fifo_mpu3050(indio_dev);
+ else
+ return reset_fifo_itg(indio_dev);
+}
+
+/**
+ * set_inv_enable() - Reset FIFO related registers.
+ * This also powers on the chip if needed.
+ * @st: Device driver instance.
+ * @fifo_enable: enable/disable
+ */
+int set_inv_enable(struct iio_dev *indio_dev,
+ bool enable) {
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ int result;
+
+ if (st->chip_config.is_asleep)
+ return -EINVAL;
+ reg = &st->reg;
+ if (enable) {
+ result = inv_reset_fifo(indio_dev);
+ if (result)
+ return result;
+ } else {
+ if ((INV_MPU3050 != st->chip_type)
+ && st->chip_config.lpa_mode) {
+ result = inv_lpa_mode(st, 0);
+ if (result)
+ return result;
+ }
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ return result;
+ /* disable fifo reading */
+ if (INV_MPU3050 != st->chip_type) {
+ result = inv_i2c_single_write(st, reg->int_enable, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ } else {
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config);
+ }
+ if (result)
+ return result;
+ }
+ st->chip_config.enable = !!enable;
+
+ return 0;
+}
+
+/**
+ * inv_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+static irqreturn_t inv_irq_handler(int irq, void *dev_id)
+{
+ struct inv_mpu_iio_s *st;
+ u64 timestamp;
+ int catch_up;
+ u64 time_since_last_irq;
+
+ st = (struct inv_mpu_iio_s *)dev_id;
+ timestamp = get_time_ns();
+ time_since_last_irq = timestamp - st->last_isr_time;
+ spin_lock(&st->time_stamp_lock);
+ catch_up = 0;
+ while ((time_since_last_irq > st->irq_dur_ns * 2) &&
+ (catch_up < MAX_CATCH_UP) &&
+ (!st->chip_config.lpa_mode) &&
+ (!st->chip_config.dmp_on)) {
+ st->last_isr_time += st->irq_dur_ns;
+ kfifo_in(&st->timestamps,
+ &st->last_isr_time, 1);
+ time_since_last_irq = timestamp - st->last_isr_time;
+ catch_up++;
+ }
+ kfifo_in(&st->timestamps, &timestamp, 1);
+ st->last_isr_time = timestamp;
+ spin_unlock(&st->time_stamp_lock);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int put_scan_to_buf(struct iio_dev *indio_dev, u8 *d,
+ short *s, int scan_index, int d_ind) {
+ struct iio_buffer *ring = indio_dev->buffer;
+ int st;
+ int i;
+ for (i = 0; i < 3; i++) {
+ st = iio_scan_mask_query(indio_dev, ring, scan_index + i);
+ if (st) {
+ memcpy(&d[d_ind], &s[i], sizeof(s[i]));
+ d_ind += sizeof(s[i]);
+ }
+ }
+ return d_ind;
+}
+
+static int put_scan_to_buf_q(struct iio_dev *indio_dev, u8 *d,
+ int *s, int scan_index, int d_ind) {
+ struct iio_buffer *ring = indio_dev->buffer;
+ int st;
+ int i;
+ for (i = 0; i < 4; i++) {
+ st = iio_scan_mask_query(indio_dev, ring, scan_index + i);
+ if (st) {
+ memcpy(&d[d_ind], &s[i], sizeof(s[i]));
+ d_ind += sizeof(s[i]);
+ }
+ }
+ return d_ind;
+}
+
+static void inv_report_data_3050(struct iio_dev *indio_dev, s64 t,
+ int has_footer, u8 *data)
+{
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int ind, i, d_ind;
+ struct inv_chip_config_s *conf;
+ short g[THREE_AXIS], a[THREE_AXIS];
+ s64 buf[8];
+ u8 *tmp;
+ int bytes_per_datum, scan_count;
+ conf = &st->chip_config;
+
+ scan_count = bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+ bytes_per_datum = scan_count * 2;
+
+ ind = 0;
+ if (has_footer)
+ ind += 2;
+ tmp = (u8 *)buf;
+ d_ind = 0;
+
+ if (conf->gyro_fifo_enable) {
+ for (i = 0; i < ARRAY_SIZE(g); i++) {
+ g[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+ st->raw_gyro[i] = g[i];
+ }
+ ind += BYTES_PER_SENSOR;
+ d_ind = put_scan_to_buf(indio_dev, tmp, g,
+ INV_MPU_SCAN_GYRO_X, d_ind);
+ }
+ if (conf->accl_fifo_enable) {
+ st->mpu_slave->combine_data(&data[ind], a);
+ for (i = 0; i < ARRAY_SIZE(a); i++)
+ st->raw_accel[i] = a[i];
+
+ ind += BYTES_PER_SENSOR;
+ d_ind = put_scan_to_buf(indio_dev, tmp, a,
+ INV_MPU_SCAN_ACCL_X, d_ind);
+ }
+
+ i = (bytes_per_datum + 7) / 8;
+ if (ring->scan_timestamp)
+ buf[i] = t;
+ ring->access->store_to(indio_dev->buffer, (u8 *)buf);
+}
+
+/**
+ * inv_read_fifo_mpu3050() - Transfer data from FIFO to ring buffer for
+ * mpu3050.
+ */
+irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id)
+{
+
+ struct inv_mpu_iio_s *st = (struct inv_mpu_iio_s *)dev_id;
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ int bytes_per_datum;
+ u8 data[64];
+ int result;
+ short fifo_count, byte_read;
+ u32 copied;
+ s64 timestamp;
+ struct inv_reg_map_s *reg;
+ reg = &st->reg;
+ /* It is impossible that chip is asleep or enable is
+ zero when interrupt is on
+ * because interrupt is now connected with enable */
+ if (st->chip_config.dmp_on)
+ bytes_per_datum = BYTES_FOR_DMP;
+ else
+ bytes_per_datum = (st->chip_config.accl_fifo_enable +
+ st->chip_config.gyro_fifo_enable)*BYTES_PER_SENSOR;
+ if (st->chip_config.has_footer)
+ byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+ else
+ byte_read = bytes_per_datum;
+
+ fifo_count = 0;
+ if (byte_read != 0) {
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ goto end_session;
+ fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+ if (fifo_count < byte_read)
+ goto end_session;
+ if (fifo_count & 1)
+ goto flush_fifo;
+ if (fifo_count > FIFO_THRESHOLD)
+ goto flush_fifo;
+ /* Timestamp mismatch. */
+ if (kfifo_len(&st->timestamps) <
+ fifo_count / byte_read)
+ goto flush_fifo;
+ if (kfifo_len(&st->timestamps) >
+ fifo_count / byte_read + TIME_STAMP_TOR) {
+ if (st->chip_config.dmp_on) {
+ result = kfifo_to_user(&st->timestamps,
+ &timestamp, sizeof(timestamp), &copied);
+ if (result)
+ goto flush_fifo;
+ } else {
+ goto flush_fifo;
+ }
+ }
+ }
+ while ((bytes_per_datum != 0) && (fifo_count >= byte_read)) {
+ result = inv_i2c_read(st, reg->fifo_r_w, byte_read, data);
+ if (result)
+ goto flush_fifo;
+
+ result = kfifo_to_user(&st->timestamps,
+ &timestamp, sizeof(timestamp), &copied);
+ if (result)
+ goto flush_fifo;
+ inv_report_data_3050(indio_dev, timestamp,
+ st->chip_config.has_footer, data);
+ fifo_count -= byte_read;
+ if (st->chip_config.has_footer == 0) {
+ st->chip_config.has_footer = 1;
+ byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+ }
+ }
+
+end_session:
+ return IRQ_HANDLED;
+
+flush_fifo:
+ /* Flush HW and SW FIFOs. */
+ inv_reset_fifo(indio_dev);
+ inv_clear_kfifo(st);
+ return IRQ_HANDLED;
+}
+
+static int inv_report_gyro_accl_compass(struct iio_dev *indio_dev,
+ u8 *data, s64 t)
+{
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ short g[THREE_AXIS], a[THREE_AXIS], c[THREE_AXIS];
+ int q[4];
+ int result, ind, d_ind;
+ s64 buf[8];
+ u32 word;
+ u8 d[8], compass_divider;
+ u8 *tmp;
+ int source, i;
+ struct inv_chip_config_s *conf;
+
+ conf = &st->chip_config;
+ ind = 0;
+ if (conf->quaternion_on & conf->dmp_on) {
+ for (i = 0; i < ARRAY_SIZE(q); i++) {
+ q[i] = be32_to_cpup((__be32 *)(&data[ind + i * 4]));
+ st->raw_quaternion[i] = q[i];
+ }
+ ind += QUATERNION_BYTES;
+ }
+ if (conf->accl_fifo_enable) {
+ for (i = 0; i < ARRAY_SIZE(a); i++) {
+ a[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+ a[i] *= st->chip_info.multi;
+ st->raw_accel[i] = a[i];
+ }
+ ind += BYTES_PER_SENSOR;
+ }
+ if (conf->gyro_fifo_enable) {
+ for (i = 0; i < ARRAY_SIZE(g); i++) {
+ g[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+ st->raw_gyro[i] = g[i];
+ }
+ ind += BYTES_PER_SENSOR;
+ }
+ if (conf->dmp_on && (conf->tap_on || conf->display_orient_on)) {
+ word = (u32)(be32_to_cpup((u32 *)&data[ind]));
+ source = ((word >> 16) & 0xff);
+ if (source) {
+ st->tap_data = (DMP_MASK_TAP & (word & 0xff));
+ st->display_orient_data =
+ ((DMP_MASK_DIS_ORIEN & (word & 0xff)) >>
+ DMP_DIS_ORIEN_SHIFT);
+ }
+
+ /* report tap information */
+ if (source & INT_SRC_TAP)
+ sysfs_notify(&indio_dev->dev.kobj, NULL, "event_tap");
+ /* report orientation information */
+ if (source & INT_SRC_DISPLAY_ORIENT)
+ sysfs_notify(&indio_dev->dev.kobj, NULL,
+ "event_display_orientation");
+ }
+ /*divider and counter is used to decrease the speed of read in
+ high frequency sample rate*/
+ if (conf->compass_fifo_enable) {
+ c[0] = 0;
+ c[1] = 0;
+ c[2] = 0;
+ if (conf->dmp_on)
+ compass_divider = st->compass_dmp_divider;
+ else
+ compass_divider = st->compass_divider;
+ if (compass_divider == st->compass_counter) {
+ /*read from external sensor data register */
+ result = inv_i2c_read(st, REG_EXT_SENS_DATA_00,
+ NUM_BYTES_COMPASS_SLAVE, d);
+ /* d[7] is status 2 register */
+ /*for AKM8975, bit 2 and 3 should be all be zero*/
+ /* for AMK8963, bit 3 should be zero*/
+ if ((DATA_AKM_DRDY == d[0]) &&
+ (0 == (d[7] & DATA_AKM_STAT_MASK)) &&
+ (!result)) {
+ u8 *sens;
+ sens = st->chip_info.compass_sens;
+ c[0] = (short)((d[2] << 8) | d[1]);
+ c[1] = (short)((d[4] << 8) | d[3]);
+ c[2] = (short)((d[6] << 8) | d[5]);
+ c[0] = (short)(((int)c[0] *
+ (sens[0] + 128)) >> 8);
+ c[1] = (short)(((int)c[1] *
+ (sens[1] + 128)) >> 8);
+ c[2] = (short)(((int)c[2] *
+ (sens[2] + 128)) >> 8);
+ st->raw_compass[0] = c[0];
+ st->raw_compass[1] = c[1];
+ st->raw_compass[2] = c[2];
+ }
+ st->compass_counter = 0;
+ } else if (compass_divider != 0) {
+ st->compass_counter++;
+ }
+ if (!conf->normal_compass_measure) {
+ c[0] = 0;
+ c[1] = 0;
+ c[2] = 0;
+ conf->normal_compass_measure = 1;
+ }
+ }
+
+ tmp = (u8 *)buf;
+ d_ind = 0;
+ if (conf->quaternion_on & conf->dmp_on)
+ d_ind = put_scan_to_buf_q(indio_dev, tmp, q,
+ INV_MPU_SCAN_QUAT_R, d_ind);
+ if (conf->gyro_fifo_enable)
+ d_ind = put_scan_to_buf(indio_dev, tmp, g,
+ INV_MPU_SCAN_GYRO_X, d_ind);
+ if (conf->accl_fifo_enable)
+ d_ind = put_scan_to_buf(indio_dev, tmp, a,
+ INV_MPU_SCAN_ACCL_X, d_ind);
+ if (conf->compass_fifo_enable)
+ d_ind = put_scan_to_buf(indio_dev, tmp, c,
+ INV_MPU_SCAN_MAGN_X, d_ind);
+ if (ring->scan_timestamp)
+ buf[(d_ind + 7) / 8] = t;
+ /* if nothing is enabled, send nothing */
+ if (d_ind)
+ ring->access->store_to(indio_dev->buffer, (u8 *)buf);
+
+ return 0;
+}
+
+static void inv_process_motion(struct inv_mpu_iio_s *st)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ s32 diff, true_motion;
+ s64 timestamp;
+ int result;
+ u8 data[1];
+
+ /* motion interrupt */
+ result = inv_i2c_read(st, REG_INT_STATUS, 1, data);
+ if (result)
+ return;
+
+ if (data[0] & BIT_MOT_INT) {
+ timestamp = get_time_ns();
+ diff = (int)(((timestamp - st->mpu6500_last_motion_time) >>
+ NS_PER_MS_SHIFT));
+ if (diff > st->mot_int.mot_dur) {
+ st->mpu6500_last_motion_time = timestamp;
+ true_motion = 1;
+ } else {
+ true_motion = 0;
+ }
+ if (true_motion)
+ sysfs_notify(&indio_dev->dev.kobj, NULL,
+ "event_accel_motion");
+ }
+}
+
+static int get_bytes_per_datum(struct inv_mpu_iio_s *st)
+{
+ int bytes_per_datum;
+
+ bytes_per_datum = 0;
+ if (st->chip_config.dmp_on) {
+ if (st->chip_config.quaternion_on)
+ bytes_per_datum += QUATERNION_BYTES;
+ if (st->chip_config.tap_on ||
+ st->chip_config.display_orient_on)
+ bytes_per_datum += BYTES_FOR_EVENTS;
+ }
+ if (st->chip_config.accl_fifo_enable)
+ bytes_per_datum += BYTES_PER_SENSOR;
+ if (st->chip_config.gyro_fifo_enable)
+ bytes_per_datum += BYTES_PER_SENSOR;
+
+ return bytes_per_datum;
+}
+
+/**
+ * inv_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+irqreturn_t inv_read_fifo(int irq, void *dev_id)
+{
+
+ struct inv_mpu_iio_s *st = (struct inv_mpu_iio_s *)dev_id;
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ size_t bytes_per_datum;
+ int result;
+ u8 data[BYTES_FOR_DMP + QUATERNION_BYTES];
+ u16 fifo_count;
+ u32 copied;
+ s64 timestamp;
+ struct inv_reg_map_s *reg;
+ s64 buf[8];
+ s8 *tmp;
+
+ mutex_lock(&indio_dev->mlock);
+ if (!(iio_buffer_enabled(indio_dev)))
+ goto end_session;
+
+ reg = &st->reg;
+ if (!(st->chip_config.accl_fifo_enable |
+ st->chip_config.gyro_fifo_enable |
+ st->chip_config.dmp_on |
+ st->chip_config.compass_fifo_enable |
+ st->mot_int.mot_on))
+ goto end_session;
+ if (st->mot_int.mot_on)
+ inv_process_motion(st);
+ if (st->chip_config.lpa_mode) {
+ result = inv_i2c_read(st, reg->raw_accl,
+ BYTES_PER_SENSOR, data);
+ if (result)
+ goto end_session;
+ inv_report_gyro_accl_compass(indio_dev, data,
+ get_time_ns());
+ goto end_session;
+ }
+ bytes_per_datum = get_bytes_per_datum(st);
+ fifo_count = 0;
+ if (bytes_per_datum != 0) {
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ goto end_session;
+ fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+ if (fifo_count < bytes_per_datum)
+ goto end_session;
+ /* fifo count can't be odd number */
+ if (fifo_count & 1)
+ goto flush_fifo;
+ if (fifo_count > FIFO_THRESHOLD)
+ goto flush_fifo;
+ /* timestamp mismatch. */
+ if (kfifo_len(&st->timestamps) <
+ fifo_count / bytes_per_datum)
+ goto flush_fifo;
+ if (kfifo_len(&st->timestamps) >
+ fifo_count / bytes_per_datum + TIME_STAMP_TOR) {
+ if (st->chip_config.dmp_on) {
+ result = kfifo_to_user(&st->timestamps,
+ &timestamp, sizeof(timestamp), &copied);
+ if (result)
+ goto flush_fifo;
+ } else {
+ goto flush_fifo;
+ }
+ }
+ } else {
+ result = kfifo_to_user(&st->timestamps,
+ &timestamp, sizeof(timestamp), &copied);
+ if (result)
+ goto flush_fifo;
+ }
+ tmp = (s8 *)buf;
+ while ((bytes_per_datum != 0) && (fifo_count >= bytes_per_datum)) {
+ result = inv_i2c_read(st, reg->fifo_r_w, bytes_per_datum,
+ data);
+ if (result)
+ goto flush_fifo;
+
+ result = kfifo_to_user(&st->timestamps,
+ &timestamp, sizeof(timestamp), &copied);
+ if (result)
+ goto flush_fifo;
+ inv_report_gyro_accl_compass(indio_dev, data, timestamp);
+ fifo_count -= bytes_per_datum;
+ }
+ if (bytes_per_datum == 0 && st->chip_config.compass_fifo_enable)
+ inv_report_gyro_accl_compass(indio_dev, data, timestamp);
+
+end_session:
+ mutex_unlock(&indio_dev->mlock);
+
+ return IRQ_HANDLED;
+
+flush_fifo:
+ /* Flush HW and SW FIFOs. */
+ inv_reset_fifo(indio_dev);
+ inv_clear_kfifo(st);
+ mutex_unlock(&indio_dev->mlock);
+
+ return IRQ_HANDLED;
+}
+
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ free_irq(gpio_to_irq(st->client->irq), st);
+ iio_kfifo_free(indio_dev->buffer);
+};
+
+static int inv_postenable(struct iio_dev *indio_dev)
+{
+ return set_inv_enable(indio_dev, true);
+}
+
+static int inv_predisable(struct iio_dev *indio_dev)
+{
+ return set_inv_enable(indio_dev, false);
+}
+
+static void inv_scan_query(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int result;
+
+ if (iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_GYRO_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_GYRO_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_GYRO_Z))
+ st->chip_config.gyro_fifo_enable = 1;
+ else
+ st->chip_config.gyro_fifo_enable = 0;
+
+ if (iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_ACCL_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_ACCL_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_ACCL_Z))
+ st->chip_config.accl_fifo_enable = 1;
+ else
+ st->chip_config.accl_fifo_enable = 0;
+
+ if (iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_MAGN_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_MAGN_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_MAGN_Z))
+ st->chip_config.compass_fifo_enable = 1;
+ else
+ st->chip_config.compass_fifo_enable = 0;
+
+ /* check to make sure engine is turned on if fifo is turned on */
+ if (st->chip_config.gyro_fifo_enable &&
+ (!st->chip_config.gyro_enable)) {
+ result = st->switch_gyro_engine(st, true);
+ if (result)
+ return;
+ st->chip_config.gyro_enable = true;
+ }
+ if (st->chip_config.accl_fifo_enable &&
+ (!st->chip_config.accl_enable)) {
+ result = st->switch_accl_engine(st, true);
+ if (result)
+ return;
+ st->chip_config.accl_enable = true;
+ }
+}
+
+static int inv_check_quaternion(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int result;
+
+ if (st->chip_config.dmp_on) {
+ if (
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_QUAT_R) ||
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_QUAT_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_QUAT_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_QUAT_Z))
+ st->chip_config.quaternion_on = 1;
+ else
+ st->chip_config.quaternion_on = 0;
+
+ result = inv_send_quaternion(st,
+ st->chip_config.quaternion_on);
+ if (result)
+ return result;
+ } else {
+ st->chip_config.quaternion_on = 0;
+ clear_bit(INV_MPU_SCAN_QUAT_R, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_QUAT_X, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_QUAT_Y, ring->scan_mask);
+ clear_bit(INV_MPU_SCAN_QUAT_Z, ring->scan_mask);
+ }
+
+ return 0;
+}
+
+static int inv_check_conflict_sysfs(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int result;
+
+ if (st->chip_config.lpa_mode) {
+ /* dmp cannot run with low power mode on */
+ st->chip_config.dmp_on = 0;
+ result = st->gyro_en(st, ring, false);
+ if (result)
+ return result;
+ result = st->compass_en(st, ring, false);
+ if (result)
+ return result;
+ result = st->quaternion_en(st, ring, false);
+ if (result)
+ return result;
+
+ result = st->accl_en(st, ring, true);
+ if (result)
+ return result;
+ }
+ result = inv_check_quaternion(indio_dev);
+ if (result)
+ return result;
+
+ return result;
+}
+
+static int inv_preenable(struct iio_dev *indio_dev)
+{
+ int result;
+
+ result = inv_check_conflict_sysfs(indio_dev);
+ if (result)
+ return result;
+ inv_scan_query(indio_dev);
+ result = iio_sw_buffer_preenable(indio_dev);
+
+ return result;
+}
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+ .preenable = &inv_preenable,
+ .postenable = &inv_postenable,
+ .predisable = &inv_predisable,
+};
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring;
+
+ ring = iio_kfifo_allocate(indio_dev);
+ if (!ring)
+ return -ENOMEM;
+ indio_dev->buffer = ring;
+ /* setup ring buffer */
+ ring->scan_timestamp = true;
+ indio_dev->setup_ops = &inv_mpu_ring_setup_ops;
+ /*scan count double count timestamp. should subtract 1. but
+ number of channels still includes timestamp*/
+ if (INV_MPU3050 == st->chip_type)
+ ret = request_threaded_irq(gpio_to_irq(st->client->irq), inv_irq_handler,
+ inv_read_fifo_mpu3050,
+ IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+ else
+ ret = request_threaded_irq(gpio_to_irq(st->client->irq), inv_irq_handler,
+ inv_read_fifo,
+ IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+ if (ret)
+ goto error_iio_sw_rb_free;
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+ return 0;
+error_iio_sw_rb_free:
+ iio_kfifo_free(indio_dev->buffer);
+ return ret;
+}
+
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu/inv_mpu_trigger.c
new file mode 100644
index 00000000..4e01ad15
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_trigger.c
@@ -0,0 +1,92 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_mpu_trigger.c
+ * @brief A sysfs device driver for Invensense devices
+ * @details This file is part of inv mpu iio driver code
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+
+#include "inv_mpu_iio.h"
+
+/**
+ * inv_mpu_data_rdy_trigger_set_state() set data ready interrupt state
+ **/
+static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ return 0;
+}
+
+static const struct iio_trigger_ops inv_mpu_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
+};
+
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+ st->trig = iio_trigger_alloc("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL)
+ return -ENOMEM;
+ st->trig->dev.parent = &st->client->dev;
+ st->trig->ops = &inv_mpu_trigger_ops;
+ ret = iio_trigger_register(st->trig);
+
+ if (ret) {
+ iio_trigger_free(st->trig);
+ return -EPERM;
+ }
+ indio_dev->trig = st->trig;
+
+ return 0;
+}
+
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_trigger_free(st->trig);
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/imu/inv_mpu/inv_slave_bma250.c b/drivers/iio/imu/inv_mpu/inv_slave_bma250.c
new file mode 100644
index 00000000..bd84f637
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_slave_bma250.c
@@ -0,0 +1,330 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_slave_bma250.c
+ * @brief A sysfs device driver for Invensense devices
+ * @details This file is part of invensense mpu driver code
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#define BMA250_CHIP_ID 3
+#define BMA250_RANGE_SET 0
+#define BMA250_BW_SET 4
+
+/* range and bandwidth */
+#define BMA250_RANGE_2G 3
+#define BMA250_RANGE_4G 5
+#define BMA250_RANGE_8G 8
+#define BMA250_RANGE_16G 12
+#define BMA250_RANGE_MAX 4
+#define BMA250_RANGE_MASK 0xF0
+
+#define BMA250_BW_7_81HZ 0x08
+#define BMA250_BW_15_63HZ 0x09
+#define BMA250_BW_31_25HZ 0x0A
+#define BMA250_BW_62_50HZ 0x0B
+#define BMA250_BW_125HZ 0x0C
+#define BMA250_BW_250HZ 0x0D
+#define BMA250_BW_500HZ 0x0E
+#define BMA250_BW_1000HZ 0x0F
+#define BMA250_MAX_BW_SIZE 8
+#define BMA250_BW_REG_MASK 0xE0
+
+/* register definitions */
+#define BMA250_X_AXIS_LSB_REG 0x02
+#define BMA250_RANGE_SEL_REG 0x0F
+#define BMA250_BW_SEL_REG 0x10
+#define BMA250_MODE_CTRL_REG 0x11
+
+/* mode settings */
+#define BMA250_MODE_NORMAL 0
+#define BMA250_MODE_LOWPOWER 1
+#define BMA250_MODE_SUSPEND 2
+#define BMA250_MODE_MAX 3
+#define BMA250_MODE_MASK 0x3F
+#define BMA250_BIT_SUSPEND 0x80
+#define BMA250_BIT_LP 0x40
+
+struct bma_property {
+ int range;
+ int bandwidth;
+ int mode;
+};
+
+static struct bma_property bma_static_property = {
+ .range = BMA250_RANGE_SET,
+ .bandwidth = BMA250_BW_SET,
+ .mode = BMA250_MODE_SUSPEND
+};
+
+static int bma250_set_bandwidth(struct inv_mpu_iio_s *st, u8 bw)
+{
+ int res;
+ u8 data;
+ int bandwidth;
+ switch (bw) {
+ case 0:
+ bandwidth = BMA250_BW_7_81HZ;
+ break;
+ case 1:
+ bandwidth = BMA250_BW_15_63HZ;
+ break;
+ case 2:
+ bandwidth = BMA250_BW_31_25HZ;
+ break;
+ case 3:
+ bandwidth = BMA250_BW_62_50HZ;
+ break;
+ case 4:
+ bandwidth = BMA250_BW_125HZ;
+ break;
+ case 5:
+ bandwidth = BMA250_BW_250HZ;
+ break;
+ case 6:
+ bandwidth = BMA250_BW_500HZ;
+ break;
+ case 7:
+ bandwidth = BMA250_BW_1000HZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = inv_secondary_read(BMA250_BW_SEL_REG, 1, &data);
+ if (res)
+ return res;
+ data &= BMA250_BW_REG_MASK;
+ data |= bandwidth;
+ res = inv_secondary_write(BMA250_BW_SEL_REG, data);
+ return res;
+}
+
+static int bma250_set_range(struct inv_mpu_iio_s *st, u8 range)
+{
+ int res;
+ u8 orig, data;
+ switch (range) {
+ case 0:
+ data = BMA250_RANGE_2G;
+ break;
+ case 1:
+ data = BMA250_RANGE_4G;
+ break;
+ case 2:
+ data = BMA250_RANGE_8G;
+ break;
+ case 3:
+ data = BMA250_RANGE_16G;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = inv_secondary_read(BMA250_RANGE_SEL_REG, 1, &orig);
+ if (res)
+ return res;
+ orig &= BMA250_RANGE_MASK;
+ data |= orig;
+ res = inv_secondary_write(BMA250_RANGE_SEL_REG, data);
+ if (res)
+ return res;
+ bma_static_property.range = range;
+
+ return 0;
+}
+
+static int setup_slave_bma250(struct inv_mpu_iio_s *st)
+{
+ int result;
+ u8 data[2];
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ /*read secondary i2c ID register */
+ result = inv_secondary_read(0, 1, data);
+ if (result)
+ return result;
+ if (BMA250_CHIP_ID != data[0])
+ return -EINVAL;
+ result = set_3050_bypass(st, false);
+ if (result)
+ return result;
+ /*AUX(accel), slave address is set inside set_3050_bypass*/
+ /* bma250 x axis LSB register address is 2 */
+ result = inv_i2c_single_write(st, REG_3050_AUX_BST_ADDR,
+ BMA250_X_AXIS_LSB_REG);
+
+ return result;
+}
+
+static int bma250_set_mode(struct inv_mpu_iio_s *st, u8 mode)
+{
+ int res;
+ u8 data;
+
+ res = inv_secondary_read(BMA250_MODE_CTRL_REG, 1, &data);
+ if (res)
+ return res;
+ data &= BMA250_MODE_MASK;
+ switch (mode) {
+ case BMA250_MODE_NORMAL:
+ break;
+ case BMA250_MODE_LOWPOWER:
+ data |= BMA250_BIT_LP;
+ break;
+ case BMA250_MODE_SUSPEND:
+ data |= BMA250_BIT_SUSPEND;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = inv_secondary_write(BMA250_MODE_CTRL_REG, data);
+ if (res)
+ return res;
+ bma_static_property.mode = mode;
+
+ return 0;
+}
+
+static int suspend_slave_bma250(struct inv_mpu_iio_s *st)
+{
+ int result;
+ if (bma_static_property.mode == BMA250_MODE_SUSPEND)
+ return 0;
+ /*set to bypass mode */
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ bma250_set_mode(st, BMA250_MODE_SUSPEND);
+ /* no need to recover to non-bypass mode because we need it now */
+
+ return 0;
+}
+
+static int resume_slave_bma250(struct inv_mpu_iio_s *st)
+{
+ int result;
+ if (bma_static_property.mode == BMA250_MODE_NORMAL)
+ return 0;
+ /*set to bypass mode */
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ result = bma250_set_mode(st, BMA250_MODE_NORMAL);
+ /* recover bypass mode */
+ result |= set_3050_bypass(st, false);
+
+ return result ? (-EINVAL) : 0;
+}
+
+static int combine_data_slave_bma250(u8 *in, short *out)
+{
+ out[0] = le16_to_cpup((__le16 *)(&in[0]));
+ out[1] = le16_to_cpup((__le16 *)(&in[2]));
+ out[2] = le16_to_cpup((__le16 *)(&in[4]));
+
+ return 0;
+}
+
+static int get_mode_slave_bma250(void)
+{
+ switch (bma_static_property.mode) {
+ case BMA250_MODE_SUSPEND:
+ return INV_MODE_SUSPEND;
+ case BMA250_MODE_NORMAL:
+ return INV_MODE_NORMAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * set_lpf_bma250() - set lpf value
+ */
+
+static int set_lpf_bma250(struct inv_mpu_iio_s *st, int rate)
+{
+ const short hz[] = {1000, 500, 250, 125, 62, 31, 15, 7};
+ const int d[] = {7, 6, 5, 4, 3, 2, 1, 0};
+ int i, h, data, result;
+ h = (rate >> 1);
+ i = 0;
+ while ((h < hz[i]) && (i < ARRAY_SIZE(hz) - 1))
+ i++;
+ data = d[i];
+
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ result = bma250_set_bandwidth(st, (u8) data);
+ result |= set_3050_bypass(st, false);
+
+ return result ? (-EINVAL) : 0;
+}
+/**
+ * set_fs_bma250() - set range value
+ */
+
+static int set_fs_bma250(struct inv_mpu_iio_s *st, int fs)
+{
+ int result;
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ result = bma250_set_range(st, (u8) fs);
+ result |= set_3050_bypass(st, false);
+
+ return result ? (-EINVAL) : 0;
+}
+
+static struct inv_mpu_slave slave_bma250 = {
+ .suspend = suspend_slave_bma250,
+ .resume = resume_slave_bma250,
+ .setup = setup_slave_bma250,
+ .combine_data = combine_data_slave_bma250,
+ .get_mode = get_mode_slave_bma250,
+ .set_lpf = set_lpf_bma250,
+ .set_fs = set_fs_bma250
+};
+
+int inv_register_mpu3050_slave(struct inv_mpu_iio_s *st)
+{
+ st->mpu_slave = &slave_bma250;
+
+ return 0;
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
new file mode 100644
index 00000000..111ac381
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
@@ -0,0 +1,798 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+#include "inv_mpu_iio.h"
+
+/*
+ * this is the gyro scale translated from dynamic range plus/minus
+ * {250, 500, 1000, 2000} to rad/s
+ */
+static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724};
+
+/*
+ * this is the accel scale translated from dynamic range plus/minus
+ * {2, 4, 8, 16} to m/s^2
+ */
+static const int accel_scale[] = {598, 1196, 2392, 4785};
+
+static const struct inv_mpu6050_reg_map reg_set_6050 = {
+ .sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
+ .lpf = INV_MPU6050_REG_CONFIG,
+ .user_ctrl = INV_MPU6050_REG_USER_CTRL,
+ .fifo_en = INV_MPU6050_REG_FIFO_EN,
+ .gyro_config = INV_MPU6050_REG_GYRO_CONFIG,
+ .accl_config = INV_MPU6050_REG_ACCEL_CONFIG,
+ .fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H,
+ .fifo_r_w = INV_MPU6050_REG_FIFO_R_W,
+ .raw_gyro = INV_MPU6050_REG_RAW_GYRO,
+ .raw_accl = INV_MPU6050_REG_RAW_ACCEL,
+ .temperature = INV_MPU6050_REG_TEMPERATURE,
+ .int_enable = INV_MPU6050_REG_INT_ENABLE,
+ .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
+ .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
+};
+
+static const struct inv_mpu6050_chip_config chip_config_6050 = {
+ .fsr = INV_MPU6050_FSR_2000DPS,
+ .lpf = INV_MPU6050_FILTER_20HZ,
+ .fifo_rate = INV_MPU6050_INIT_FIFO_RATE,
+ .gyro_fifo_enable = false,
+ .accl_fifo_enable = false,
+ .accl_fs = INV_MPU6050_FS_02G,
+};
+
+static const struct inv_mpu6050_hw hw_info[INV_NUM_PARTS] = {
+ {
+ .num_reg = 117,
+ .name = "MPU6050",
+ .reg = &reg_set_6050,
+ .config = &chip_config_6050,
+ },
+};
+
+int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 d)
+{
+ return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d);
+}
+
+int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
+{
+ u8 d, mgmt_1;
+ int result;
+
+ /* switch clock needs to be careful. Only when gyro is on, can
+ clock source be switched to gyro. Otherwise, it must be set to
+ internal clock */
+ if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) {
+ result = i2c_smbus_read_i2c_block_data(st->client,
+ st->reg->pwr_mgmt_1, 1, &mgmt_1);
+ if (result != 1)
+ return result;
+
+ mgmt_1 &= ~INV_MPU6050_BIT_CLK_MASK;
+ }
+
+ if ((INV_MPU6050_BIT_PWR_GYRO_STBY == mask) && (!en)) {
+ /* turning off gyro requires switch to internal clock first.
+ Then turn off gyro engine */
+ mgmt_1 |= INV_CLK_INTERNAL;
+ result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, mgmt_1);
+ if (result)
+ return result;
+ }
+
+ result = i2c_smbus_read_i2c_block_data(st->client,
+ st->reg->pwr_mgmt_2, 1, &d);
+ if (result != 1)
+ return result;
+ if (en)
+ d &= ~mask;
+ else
+ d |= mask;
+ result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_2, d);
+ if (result)
+ return result;
+
+ if (en) {
+ /* Wait for output stablize */
+ msleep(INV_MPU6050_TEMP_UP_TIME);
+ if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) {
+ /* switch internal clock to PLL */
+ mgmt_1 |= INV_CLK_PLL;
+ result = inv_mpu6050_write_reg(st,
+ st->reg->pwr_mgmt_1, mgmt_1);
+ if (result)
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
+{
+ int result;
+
+ if (power_on)
+ result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, 0);
+ else
+ result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
+ INV_MPU6050_BIT_SLEEP);
+ if (result)
+ return result;
+
+ if (power_on)
+ msleep(INV_MPU6050_REG_UP_TIME);
+
+ return 0;
+}
+
+/**
+ * inv_mpu6050_init_config() - Initialize hardware, disable FIFO.
+ *
+ * Initial configuration:
+ * FSR: ± 2000DPS
+ * DLPF: 20Hz
+ * FIFO rate: 50Hz
+ * Clock source: Gyro PLL
+ */
+static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
+{
+ int result;
+ u8 d;
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ result = inv_mpu6050_set_power_itg(st, true);
+ if (result)
+ return result;
+ d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d);
+ if (result)
+ return result;
+
+ d = INV_MPU6050_FILTER_20HZ;
+ result = inv_mpu6050_write_reg(st, st->reg->lpf, d);
+ if (result)
+ return result;
+
+ d = INV_MPU6050_ONE_K_HZ / INV_MPU6050_INIT_FIFO_RATE - 1;
+ result = inv_mpu6050_write_reg(st, st->reg->sample_rate_div, d);
+ if (result)
+ return result;
+
+ d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st, st->reg->accl_config, d);
+ if (result)
+ return result;
+
+ memcpy(&st->chip_config, hw_info[st->chip_type].config,
+ sizeof(struct inv_mpu6050_chip_config));
+ result = inv_mpu6050_set_power_itg(st, false);
+
+ return result;
+}
+
+static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg,
+ int axis, int *val)
+{
+ int ind, result;
+ __be16 d;
+
+ ind = (axis - IIO_MOD_X) * 2;
+ result = i2c_smbus_read_i2c_block_data(st->client, reg + ind, 2,
+ (u8 *)&d);
+ if (result != 2)
+ return -EINVAL;
+ *val = (short)be16_to_cpup(&d);
+
+ return IIO_VAL_INT;
+}
+
+static int inv_mpu6050_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask) {
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ {
+ int ret, result;
+
+ ret = IIO_VAL_INT;
+ result = 0;
+ mutex_lock(&indio_dev->mlock);
+ if (!st->chip_config.enable) {
+ result = inv_mpu6050_set_power_itg(st, true);
+ if (result)
+ goto error_read_raw;
+ }
+ /* when enable is on, power is already on */
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ if (!st->chip_config.gyro_fifo_enable ||
+ !st->chip_config.enable) {
+ result = inv_mpu6050_switch_engine(st, true,
+ INV_MPU6050_BIT_PWR_GYRO_STBY);
+ if (result)
+ goto error_read_raw;
+ }
+ ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
+ chan->channel2, val);
+ if (!st->chip_config.gyro_fifo_enable ||
+ !st->chip_config.enable) {
+ result = inv_mpu6050_switch_engine(st, false,
+ INV_MPU6050_BIT_PWR_GYRO_STBY);
+ if (result)
+ goto error_read_raw;
+ }
+ break;
+ case IIO_ACCEL:
+ if (!st->chip_config.accl_fifo_enable ||
+ !st->chip_config.enable) {
+ result = inv_mpu6050_switch_engine(st, true,
+ INV_MPU6050_BIT_PWR_ACCL_STBY);
+ if (result)
+ goto error_read_raw;
+ }
+ ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl,
+ chan->channel2, val);
+ if (!st->chip_config.accl_fifo_enable ||
+ !st->chip_config.enable) {
+ result = inv_mpu6050_switch_engine(st, false,
+ INV_MPU6050_BIT_PWR_ACCL_STBY);
+ if (result)
+ goto error_read_raw;
+ }
+ break;
+ case IIO_TEMP:
+ /* wait for stablization */
+ msleep(INV_MPU6050_SENSOR_UP_TIME);
+ inv_mpu6050_sensor_show(st, st->reg->temperature,
+ IIO_MOD_X, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+error_read_raw:
+ if (!st->chip_config.enable)
+ result |= inv_mpu6050_set_power_itg(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ return ret;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val = 0;
+ *val2 = gyro_scale_6050[st->chip_config.fsr];
+
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = accel_scale[st->chip_config.accl_fs];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = INV_MPU6050_TEMP_SCALE;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = INV_MPU6050_TEMP_OFFSET;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_mpu6050_write_fsr(struct inv_mpu6050_state *st, int fsr)
+{
+ int result;
+ u8 d;
+
+ if (fsr < 0 || fsr > INV_MPU6050_MAX_GYRO_FS_PARAM)
+ return -EINVAL;
+ if (fsr == st->chip_config.fsr)
+ return 0;
+
+ d = (fsr << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d);
+ if (result)
+ return result;
+ st->chip_config.fsr = fsr;
+
+ return 0;
+}
+
+static int inv_mpu6050_write_accel_fs(struct inv_mpu6050_state *st, int fs)
+{
+ int result;
+ u8 d;
+
+ if (fs < 0 || fs > INV_MPU6050_MAX_ACCL_FS_PARAM)
+ return -EINVAL;
+ if (fs == st->chip_config.accl_fs)
+ return 0;
+
+ d = (fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st, st->reg->accl_config, d);
+ if (result)
+ return result;
+ st->chip_config.accl_fs = fs;
+
+ return 0;
+}
+
+static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask) {
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ /* we should only update scale when the chip is disabled, i.e.,
+ not running */
+ if (st->chip_config.enable) {
+ result = -EBUSY;
+ goto error_write_raw;
+ }
+ result = inv_mpu6050_set_power_itg(st, true);
+ if (result)
+ goto error_write_raw;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ result = inv_mpu6050_write_fsr(st, val);
+ break;
+ case IIO_ACCEL:
+ result = inv_mpu6050_write_accel_fs(st, val);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+
+error_write_raw:
+ result |= inv_mpu6050_set_power_itg(st, false);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+/**
+ * inv_mpu6050_set_lpf() - set low pass filer based on fifo rate.
+ *
+ * Based on the Nyquist principle, the sampling rate must
+ * exceed twice of the bandwidth of the signal, or there
+ * would be alising. This function basically search for the
+ * correct low pass parameters based on the fifo rate, e.g,
+ * sampling frequency.
+ */
+static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate)
+{
+ const int hz[] = {188, 98, 42, 20, 10, 5};
+ const int d[] = {INV_MPU6050_FILTER_188HZ, INV_MPU6050_FILTER_98HZ,
+ INV_MPU6050_FILTER_42HZ, INV_MPU6050_FILTER_20HZ,
+ INV_MPU6050_FILTER_10HZ, INV_MPU6050_FILTER_5HZ};
+ int i, h, result;
+ u8 data;
+
+ h = (rate >> 1);
+ i = 0;
+ while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
+ i++;
+ data = d[i];
+ result = inv_mpu6050_write_reg(st, st->reg->lpf, data);
+ if (result)
+ return result;
+ st->chip_config.lpf = data;
+
+ return 0;
+}
+
+/**
+ * inv_mpu6050_fifo_rate_store() - Set fifo rate.
+ */
+static ssize_t inv_mpu6050_fifo_rate_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ s32 fifo_rate;
+ u8 d;
+ int result;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ if (kstrtoint(buf, 10, &fifo_rate))
+ return -EINVAL;
+ if (fifo_rate < INV_MPU6050_MIN_FIFO_RATE ||
+ fifo_rate > INV_MPU6050_MAX_FIFO_RATE)
+ return -EINVAL;
+ if (fifo_rate == st->chip_config.fifo_rate)
+ return count;
+
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.enable) {
+ result = -EBUSY;
+ goto fifo_rate_fail;
+ }
+ result = inv_mpu6050_set_power_itg(st, true);
+ if (result)
+ goto fifo_rate_fail;
+
+ d = INV_MPU6050_ONE_K_HZ / fifo_rate - 1;
+ result = inv_mpu6050_write_reg(st, st->reg->sample_rate_div, d);
+ if (result)
+ goto fifo_rate_fail;
+ st->chip_config.fifo_rate = fifo_rate;
+
+ result = inv_mpu6050_set_lpf(st, fifo_rate);
+ if (result)
+ goto fifo_rate_fail;
+
+fifo_rate_fail:
+ result |= inv_mpu6050_set_power_itg(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ return count;
+}
+
+/**
+ * inv_fifo_rate_show() - Get the current sampling rate.
+ */
+static ssize_t inv_fifo_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "%d\n", st->chip_config.fifo_rate);
+}
+
+/**
+ * inv_attr_show() - calling this function will show current
+ * parameters.
+ */
+static ssize_t inv_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ s8 *m;
+
+ switch (this_attr->address) {
+ /* In MPU6050, the two matrix are the same because gyro and accel
+ are integrated in one chip */
+ case ATTR_GYRO_MATRIX:
+ case ATTR_ACCL_MATRIX:
+ m = st->plat_data.orientation;
+
+ return sprintf(buf, "%d, %d, %d; %d, %d, %d; %d, %d, %d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * inv_mpu6050_validate_trigger() - validate_trigger callback for invensense
+ * MPU6050 device.
+ * @indio_dev: The IIO device
+ * @trig: The new trigger
+ *
+ * Returns: 0 if the 'trig' matches the trigger registered by the MPU6050
+ * device, -EINVAL otherwise.
+ */
+static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ if (st->trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+#define INV_MPU6050_CHAN(_type, _channel2, _index) \
+ { \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = _channel2, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .shift = 0 , \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec inv_mpu_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
+ /*
+ * Note that temperature should only be via polled reading only,
+ * not the final scan elements output.
+ */
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_OFFSET)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = -1,
+ },
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
+
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
+};
+
+/* constant IIO attribute */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
+static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show,
+ inv_mpu6050_fifo_rate_store);
+static IIO_DEVICE_ATTR(in_gyro_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_GYRO_MATRIX);
+static IIO_DEVICE_ATTR(in_accel_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_ACCL_MATRIX);
+
+static struct attribute *inv_attributes[] = {
+ &iio_dev_attr_in_gyro_matrix.dev_attr.attr,
+ &iio_dev_attr_in_accel_matrix.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group inv_attribute_group = {
+ .attrs = inv_attributes
+};
+
+static const struct iio_info mpu_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &inv_mpu6050_read_raw,
+ .write_raw = &inv_mpu6050_write_raw,
+ .attrs = &inv_attribute_group,
+ .validate_trigger = inv_mpu6050_validate_trigger,
+};
+
+/**
+ * inv_check_and_setup_chip() - check and setup chip.
+ */
+static int inv_check_and_setup_chip(struct inv_mpu6050_state *st,
+ const struct i2c_device_id *id)
+{
+ int result;
+
+ st->chip_type = INV_MPU6050;
+ st->hw = &hw_info[st->chip_type];
+ st->reg = hw_info[st->chip_type].reg;
+
+ /* reset to make sure previous state are not there */
+ result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
+ INV_MPU6050_BIT_H_RESET);
+ if (result)
+ return result;
+ msleep(INV_MPU6050_POWER_UP_TIME);
+ /* toggle power state. After reset, the sleep bit could be on
+ or off depending on the OTP settings. Toggling power would
+ make it in a definite state as well as making the hardware
+ state align with the software state */
+ result = inv_mpu6050_set_power_itg(st, false);
+ if (result)
+ return result;
+ result = inv_mpu6050_set_power_itg(st, true);
+ if (result)
+ return result;
+
+ result = inv_mpu6050_switch_engine(st, false,
+ INV_MPU6050_BIT_PWR_ACCL_STBY);
+ if (result)
+ return result;
+ result = inv_mpu6050_switch_engine(st, false,
+ INV_MPU6050_BIT_PWR_GYRO_STBY);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+/**
+ * inv_mpu_probe() - probe function.
+ * @client: i2c client.
+ * @id: i2c device id.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int inv_mpu_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_mpu6050_state *st;
+ struct iio_dev *indio_dev;
+ struct inv_mpu6050_platform_data *pdata;
+ int result;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK |
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
+ result = -ENOSYS;
+ goto out_no_free;
+ }
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->client = client;
+ pdata = (struct inv_mpu6050_platform_data
+ *)dev_get_platdata(&client->dev);
+ if (pdata)
+ st->plat_data = *pdata;
+ /* power is turned on inside check chip type*/
+ result = inv_check_and_setup_chip(st, id);
+ if (result)
+ goto out_free;
+
+ result = inv_mpu6050_init_config(indio_dev);
+ if (result) {
+ dev_err(&client->dev,
+ "Could not initialize device.\n");
+ goto out_free;
+ }
+
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = inv_mpu_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+
+ indio_dev->info = &mpu_info;
+ indio_dev->modes = INDIO_BUFFER_TRIGGERED;
+
+ result = iio_triggered_buffer_setup(indio_dev,
+ inv_mpu6050_irq_handler,
+ inv_mpu6050_read_fifo,
+ NULL);
+ if (result) {
+ dev_err(&st->client->dev, "configure buffer fail %d\n",
+ result);
+ goto out_free;
+ }
+ result = inv_mpu6050_probe_trigger(indio_dev);
+ if (result) {
+ dev_err(&st->client->dev, "trigger probe fail %d\n", result);
+ goto out_unreg_ring;
+ }
+
+ INIT_KFIFO(st->timestamps);
+ spin_lock_init(&st->time_stamp_lock);
+ result = iio_device_register(indio_dev);
+ if (result) {
+ dev_err(&st->client->dev, "IIO register fail %d\n", result);
+ goto out_remove_trigger;
+ }
+
+ return 0;
+
+out_remove_trigger:
+ inv_mpu6050_remove_trigger(st);
+out_unreg_ring:
+ iio_triggered_buffer_cleanup(indio_dev);
+out_free:
+ iio_device_free(indio_dev);
+out_no_free:
+
+ return result;
+}
+
+static int inv_mpu_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ inv_mpu6050_remove_trigger(st);
+ iio_triggered_buffer_cleanup(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+#ifdef CONFIG_PM_SLEEP
+
+static int inv_mpu_resume(struct device *dev)
+{
+ return inv_mpu6050_set_power_itg(
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev))), true);
+}
+
+static int inv_mpu_suspend(struct device *dev)
+{
+ return inv_mpu6050_set_power_itg(
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev))), false);
+}
+static SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume);
+
+#define INV_MPU6050_PMOPS (&inv_mpu_pmops)
+#else
+#define INV_MPU6050_PMOPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+/*
+ * device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_mpu_id[] = {
+ {"mpu6050", INV_MPU6050},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+
+static struct i2c_driver inv_mpu_driver = {
+ .probe = inv_mpu_probe,
+ .remove = inv_mpu_remove,
+ .id_table = inv_mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-mpu6050",
+ .pm = INV_MPU6050_PMOPS,
+ },
+};
+
+module_i2c_driver(inv_mpu_driver);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device MPU6050 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
new file mode 100644
index 00000000..926fccea
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
@@ -0,0 +1,155 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#include "inv_mpu_iio.h"
+
+static void inv_scan_query(struct iio_dev *indio_dev)
+{
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ st->chip_config.gyro_fifo_enable =
+ test_bit(INV_MPU6050_SCAN_GYRO_X,
+ indio_dev->active_scan_mask) ||
+ test_bit(INV_MPU6050_SCAN_GYRO_Y,
+ indio_dev->active_scan_mask) ||
+ test_bit(INV_MPU6050_SCAN_GYRO_Z,
+ indio_dev->active_scan_mask);
+
+ st->chip_config.accl_fifo_enable =
+ test_bit(INV_MPU6050_SCAN_ACCL_X,
+ indio_dev->active_scan_mask) ||
+ test_bit(INV_MPU6050_SCAN_ACCL_Y,
+ indio_dev->active_scan_mask) ||
+ test_bit(INV_MPU6050_SCAN_ACCL_Z,
+ indio_dev->active_scan_mask);
+}
+
+/**
+ * inv_mpu6050_set_enable() - enable chip functions.
+ * @indio_dev: Device driver instance.
+ * @enable: enable/disable
+ */
+static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ int result;
+
+ if (enable) {
+ result = inv_mpu6050_set_power_itg(st, true);
+ if (result)
+ return result;
+ inv_scan_query(indio_dev);
+ if (st->chip_config.gyro_fifo_enable) {
+ result = inv_mpu6050_switch_engine(st, true,
+ INV_MPU6050_BIT_PWR_GYRO_STBY);
+ if (result)
+ return result;
+ }
+ if (st->chip_config.accl_fifo_enable) {
+ result = inv_mpu6050_switch_engine(st, true,
+ INV_MPU6050_BIT_PWR_ACCL_STBY);
+ if (result)
+ return result;
+ }
+ result = inv_reset_fifo(indio_dev);
+ if (result)
+ return result;
+ } else {
+ result = inv_mpu6050_write_reg(st, st->reg->fifo_en, 0);
+ if (result)
+ return result;
+
+ result = inv_mpu6050_write_reg(st, st->reg->int_enable, 0);
+ if (result)
+ return result;
+
+ result = inv_mpu6050_write_reg(st, st->reg->user_ctrl, 0);
+ if (result)
+ return result;
+
+ result = inv_mpu6050_switch_engine(st, false,
+ INV_MPU6050_BIT_PWR_GYRO_STBY);
+ if (result)
+ return result;
+
+ result = inv_mpu6050_switch_engine(st, false,
+ INV_MPU6050_BIT_PWR_ACCL_STBY);
+ if (result)
+ return result;
+ result = inv_mpu6050_set_power_itg(st, false);
+ if (result)
+ return result;
+ }
+ st->chip_config.enable = enable;
+
+ return 0;
+}
+
+/**
+ * inv_mpu_data_rdy_trigger_set_state() - set data ready interrupt state
+ * @trig: Trigger instance
+ * @state: Desired trigger state
+ */
+static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ return inv_mpu6050_set_enable(iio_trigger_get_drvdata(trig), state);
+}
+
+static const struct iio_trigger_ops inv_mpu_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
+};
+
+int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ st->trig = iio_trigger_alloc("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ ret = request_irq(st->client->irq, &iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING,
+ "inv_mpu",
+ st->trig);
+ if (ret)
+ goto error_free_trig;
+ st->trig->dev.parent = &st->client->dev;
+ st->trig->ops = &inv_mpu_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = iio_trigger_register(st->trig);
+ if (ret)
+ goto error_free_irq;
+ indio_dev->trig = iio_trigger_get(st->trig);
+
+ return 0;
+
+error_free_irq:
+ free_irq(st->client->irq, st->trig);
+error_free_trig:
+ iio_trigger_free(st->trig);
+error_ret:
+ return ret;
+}
+
+void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st)
+{
+ iio_trigger_unregister(st->trig);
+ free_irq(st->client->irq, st->trig);
+ iio_trigger_free(st->trig);
+}
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
new file mode 100644
index 00000000..e13c5f4b
--- /dev/null
+++ b/drivers/iio/industrialio-buffer.c
@@ -0,0 +1,938 @@
+/* The industrial I/O core
+ *
+ * Copyright (c) 2008 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Handling of buffer allocation / resizing.
+ *
+ *
+ * Things to look at here.
+ * - Better memory allocation techniques?
+ * - Alternative access techniques?
+ */
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+
+#include <linux/iio/iio.h>
+#include "iio_core.h"
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+
+static const char * const iio_endian_prefix[] = {
+ [IIO_BE] = "be",
+ [IIO_LE] = "le",
+};
+
+static bool iio_buffer_is_active(struct iio_dev *indio_dev,
+ struct iio_buffer *buf)
+{
+ struct list_head *p;
+
+ list_for_each(p, &indio_dev->buffer_list)
+ if (p == &buf->buffer_list)
+ return true;
+
+ return false;
+}
+
+/**
+ * iio_buffer_read_first_n_outer() - chrdev read for buffer access
+ *
+ * This function relies on all buffer implementations having an
+ * iio_buffer as their first element.
+ **/
+ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
+ size_t n, loff_t *f_ps)
+{
+ struct iio_dev *indio_dev = filp->private_data;
+ struct iio_buffer *rb = indio_dev->buffer;
+
+ if (!rb || !rb->access->read_first_n)
+ return -EINVAL;
+ return rb->access->read_first_n(rb, n, buf);
+}
+
+/**
+ * iio_buffer_poll() - poll the buffer to find out if it has data
+ */
+unsigned int iio_buffer_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct iio_dev *indio_dev = filp->private_data;
+ struct iio_buffer *rb = indio_dev->buffer;
+
+ poll_wait(filp, &rb->pollq, wait);
+ if (rb->stufftoread)
+ return POLLIN | POLLRDNORM;
+ /* need a way of knowing if there may be enough data... */
+ return 0;
+}
+
+void iio_buffer_init(struct iio_buffer *buffer)
+{
+ INIT_LIST_HEAD(&buffer->demux_list);
+ init_waitqueue_head(&buffer->pollq);
+}
+EXPORT_SYMBOL(iio_buffer_init);
+
+static ssize_t iio_show_scan_index(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%u\n", to_iio_dev_attr(attr)->c->scan_index);
+}
+
+static ssize_t iio_show_fixed_type(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ u8 type = this_attr->c->scan_type.endianness;
+
+ if (type == IIO_CPU) {
+#ifdef __LITTLE_ENDIAN
+ type = IIO_LE;
+#else
+ type = IIO_BE;
+#endif
+ }
+ return sprintf(buf, "%s:%c%d/%d>>%u\n",
+ iio_endian_prefix[type],
+ this_attr->c->scan_type.sign,
+ this_attr->c->scan_type.realbits,
+ this_attr->c->scan_type.storagebits,
+ this_attr->c->scan_type.shift);
+}
+
+static ssize_t iio_scan_el_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+
+ /* Ensure ret is 0 or 1. */
+ ret = !!test_bit(to_iio_dev_attr(attr)->address,
+ indio_dev->buffer->scan_mask);
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static int iio_scan_mask_clear(struct iio_buffer *buffer, int bit)
+{
+ clear_bit(bit, buffer->scan_mask);
+ return 0;
+}
+
+static ssize_t iio_scan_el_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ bool state;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = strtobool(buf, &state);
+ if (ret < 0)
+ return ret;
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) {
+ ret = -EBUSY;
+ goto error_ret;
+ }
+ ret = iio_scan_mask_query(indio_dev, buffer, this_attr->address);
+ if (ret < 0)
+ goto error_ret;
+ if (!state && ret) {
+ ret = iio_scan_mask_clear(buffer, this_attr->address);
+ if (ret)
+ goto error_ret;
+ } else if (state && !ret) {
+ ret = iio_scan_mask_set(indio_dev, buffer, this_attr->address);
+ if (ret)
+ goto error_ret;
+ }
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret < 0 ? ret : len;
+
+}
+
+static ssize_t iio_scan_el_ts_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ return sprintf(buf, "%d\n", indio_dev->buffer->scan_timestamp);
+}
+
+static ssize_t iio_scan_el_ts_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ bool state;
+
+ ret = strtobool(buf, &state);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) {
+ ret = -EBUSY;
+ goto error_ret;
+ }
+ indio_dev->buffer->scan_timestamp = state;
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ int ret, attrcount = 0;
+ struct iio_buffer *buffer = indio_dev->buffer;
+
+ ret = __iio_add_chan_devattr("index",
+ chan,
+ &iio_show_scan_index,
+ NULL,
+ 0,
+ 0,
+ &indio_dev->dev,
+ &buffer->scan_el_dev_attr_list);
+ if (ret)
+ goto error_ret;
+ attrcount++;
+ ret = __iio_add_chan_devattr("type",
+ chan,
+ &iio_show_fixed_type,
+ NULL,
+ 0,
+ 0,
+ &indio_dev->dev,
+ &buffer->scan_el_dev_attr_list);
+ if (ret)
+ goto error_ret;
+ attrcount++;
+ if (chan->type != IIO_TIMESTAMP)
+ ret = __iio_add_chan_devattr("en",
+ chan,
+ &iio_scan_el_show,
+ &iio_scan_el_store,
+ chan->scan_index,
+ 0,
+ &indio_dev->dev,
+ &buffer->scan_el_dev_attr_list);
+ else
+ ret = __iio_add_chan_devattr("en",
+ chan,
+ &iio_scan_el_ts_show,
+ &iio_scan_el_ts_store,
+ chan->scan_index,
+ 0,
+ &indio_dev->dev,
+ &buffer->scan_el_dev_attr_list);
+ attrcount++;
+ ret = attrcount;
+error_ret:
+ return ret;
+}
+
+static void iio_buffer_remove_and_free_scan_dev_attr(struct iio_dev *indio_dev,
+ struct iio_dev_attr *p)
+{
+ kfree(p->dev_attr.attr.name);
+ kfree(p);
+}
+
+static void __iio_buffer_attr_cleanup(struct iio_dev *indio_dev)
+{
+ struct iio_dev_attr *p, *n;
+ struct iio_buffer *buffer = indio_dev->buffer;
+
+ list_for_each_entry_safe(p, n,
+ &buffer->scan_el_dev_attr_list, l)
+ iio_buffer_remove_and_free_scan_dev_attr(indio_dev, p);
+}
+
+static const char * const iio_scan_elements_group_name = "scan_elements";
+
+int iio_buffer_register(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *channels,
+ int num_channels)
+{
+ struct iio_dev_attr *p;
+ struct attribute **attr;
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int ret, i, attrn, attrcount, attrcount_orig = 0;
+
+ if (buffer->attrs)
+ indio_dev->groups[indio_dev->groupcounter++] = buffer->attrs;
+
+ if (buffer->scan_el_attrs != NULL) {
+ attr = buffer->scan_el_attrs->attrs;
+ while (*attr++ != NULL)
+ attrcount_orig++;
+ }
+ attrcount = attrcount_orig;
+ INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
+ if (channels) {
+ /* new magic */
+ for (i = 0; i < num_channels; i++) {
+ if (channels[i].scan_index < 0)
+ continue;
+
+ /* Establish necessary mask length */
+ if (channels[i].scan_index >
+ (int)indio_dev->masklength - 1)
+ indio_dev->masklength
+ = channels[i].scan_index + 1;
+
+ ret = iio_buffer_add_channel_sysfs(indio_dev,
+ &channels[i]);
+ if (ret < 0)
+ goto error_cleanup_dynamic;
+ attrcount += ret;
+ if (channels[i].type == IIO_TIMESTAMP)
+ indio_dev->scan_index_timestamp =
+ channels[i].scan_index;
+ }
+ if (indio_dev->masklength && buffer->scan_mask == NULL) {
+ buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
+ sizeof(*buffer->scan_mask),
+ GFP_KERNEL);
+ if (buffer->scan_mask == NULL) {
+ ret = -ENOMEM;
+ goto error_cleanup_dynamic;
+ }
+ }
+ }
+
+ buffer->scan_el_group.name = iio_scan_elements_group_name;
+
+ buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
+ sizeof(buffer->scan_el_group.attrs[0]),
+ GFP_KERNEL);
+ if (buffer->scan_el_group.attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_free_scan_mask;
+ }
+ if (buffer->scan_el_attrs)
+ memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs,
+ sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig);
+ attrn = attrcount_orig;
+
+ list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
+ buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
+ indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group;
+
+ return 0;
+
+error_free_scan_mask:
+ kfree(buffer->scan_mask);
+error_cleanup_dynamic:
+ __iio_buffer_attr_cleanup(indio_dev);
+
+ return ret;
+}
+EXPORT_SYMBOL(iio_buffer_register);
+
+void iio_buffer_unregister(struct iio_dev *indio_dev)
+{
+ kfree(indio_dev->buffer->scan_mask);
+ kfree(indio_dev->buffer->scan_el_group.attrs);
+ __iio_buffer_attr_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(iio_buffer_unregister);
+
+ssize_t iio_buffer_read_length(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_buffer *buffer = indio_dev->buffer;
+
+ if (buffer->access->get_length)
+ return sprintf(buf, "%d\n",
+ buffer->access->get_length(buffer));
+
+ return 0;
+}
+EXPORT_SYMBOL(iio_buffer_read_length);
+
+ssize_t iio_buffer_write_length(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_buffer *buffer = indio_dev->buffer;
+ unsigned int val;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (buffer->access->get_length)
+ if (val == buffer->access->get_length(buffer))
+ return len;
+
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) {
+ ret = -EBUSY;
+ } else {
+ if (buffer->access->set_length)
+ buffer->access->set_length(buffer, val);
+ ret = 0;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+EXPORT_SYMBOL(iio_buffer_write_length);
+
+ssize_t iio_buffer_show_enable(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ return sprintf(buf, "%d\n",
+ iio_buffer_is_active(indio_dev,
+ indio_dev->buffer));
+}
+EXPORT_SYMBOL(iio_buffer_show_enable);
+
+/* note NULL used as error indicator as it doesn't make sense. */
+static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
+ unsigned int masklength,
+ const unsigned long *mask)
+{
+ if (bitmap_empty(mask, masklength))
+ return NULL;
+ while (*av_masks) {
+ if (bitmap_subset(mask, av_masks, masklength))
+ return av_masks;
+ av_masks += BITS_TO_LONGS(masklength);
+ }
+ return NULL;
+}
+
+static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask,
+ bool timestamp)
+{
+ const struct iio_chan_spec *ch;
+ unsigned bytes = 0;
+ int length, i;
+
+ /* How much space will the demuxed element take? */
+ for_each_set_bit(i, mask,
+ indio_dev->masklength) {
+ ch = iio_find_channel_from_si(indio_dev, i);
+ length = ch->scan_type.storagebits / 8;
+ bytes = ALIGN(bytes, length);
+ bytes += length;
+ }
+ if (timestamp) {
+ ch = iio_find_channel_from_si(indio_dev,
+ indio_dev->scan_index_timestamp);
+ length = ch->scan_type.storagebits / 8;
+ bytes = ALIGN(bytes, length);
+ bytes += length;
+ }
+ return bytes;
+}
+
+int iio_update_buffers(struct iio_dev *indio_dev,
+ struct iio_buffer *insert_buffer,
+ struct iio_buffer *remove_buffer)
+{
+ int ret;
+ int success = 0;
+ struct iio_buffer *buffer;
+ unsigned long *compound_mask;
+ const unsigned long *old_mask;
+
+ /* Wind down existing buffers - iff there are any */
+ if (!list_empty(&indio_dev->buffer_list)) {
+ if (indio_dev->setup_ops->predisable) {
+ ret = indio_dev->setup_ops->predisable(indio_dev);
+ if (ret)
+ goto error_ret;
+ }
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+ if (indio_dev->setup_ops->postdisable) {
+ ret = indio_dev->setup_ops->postdisable(indio_dev);
+ if (ret)
+ goto error_ret;
+ }
+ }
+ /* Keep a copy of current setup to allow roll back */
+ old_mask = indio_dev->active_scan_mask;
+ if (!indio_dev->available_scan_masks)
+ indio_dev->active_scan_mask = NULL;
+
+ if (remove_buffer)
+ list_del(&remove_buffer->buffer_list);
+ if (insert_buffer)
+ list_add(&insert_buffer->buffer_list, &indio_dev->buffer_list);
+
+ /* If no buffers in list, we are done */
+ if (list_empty(&indio_dev->buffer_list)) {
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+ if (indio_dev->available_scan_masks == NULL)
+ kfree(old_mask);
+ return 0;
+ }
+
+ /* What scan mask do we actually have ?*/
+ compound_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
+ sizeof(long), GFP_KERNEL);
+ if (compound_mask == NULL) {
+ if (indio_dev->available_scan_masks == NULL)
+ kfree(old_mask);
+ return -ENOMEM;
+ }
+ indio_dev->scan_timestamp = 0;
+
+ list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
+ bitmap_or(compound_mask, compound_mask, buffer->scan_mask,
+ indio_dev->masklength);
+ indio_dev->scan_timestamp |= buffer->scan_timestamp;
+ }
+ if (indio_dev->available_scan_masks) {
+ indio_dev->active_scan_mask =
+ iio_scan_mask_match(indio_dev->available_scan_masks,
+ indio_dev->masklength,
+ compound_mask);
+ if (indio_dev->active_scan_mask == NULL) {
+ /*
+ * Roll back.
+ * Note can only occur when adding a buffer.
+ */
+ list_del(&insert_buffer->buffer_list);
+ indio_dev->active_scan_mask = old_mask;
+ success = -EINVAL;
+ }
+ } else {
+ indio_dev->active_scan_mask = compound_mask;
+ }
+
+ iio_update_demux(indio_dev);
+
+ /* Wind up again */
+ if (indio_dev->setup_ops->preenable) {
+ ret = indio_dev->setup_ops->preenable(indio_dev);
+ if (ret) {
+ printk(KERN_ERR
+ "Buffer not started:"
+ "buffer preenable failed\n");
+ goto error_remove_inserted;
+ }
+ }
+ indio_dev->scan_bytes =
+ iio_compute_scan_bytes(indio_dev,
+ indio_dev->active_scan_mask,
+ indio_dev->scan_timestamp);
+ list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
+ if (buffer->access->request_update) {
+ ret = buffer->access->request_update(buffer);
+ if (ret) {
+ printk(KERN_INFO
+ "Buffer not started:"
+ "buffer parameter update failed\n");
+ goto error_run_postdisable;
+ }
+ }
+ if (indio_dev->info->update_scan_mode) {
+ ret = indio_dev->info
+ ->update_scan_mode(indio_dev,
+ indio_dev->active_scan_mask);
+ if (ret < 0) {
+ printk(KERN_INFO "update scan mode failed\n");
+ goto error_run_postdisable;
+ }
+ }
+ /* Definitely possible for devices to support both of these.*/
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {
+ if (!indio_dev->trig) {
+ printk(KERN_INFO "Buffer not started: no trigger\n");
+ ret = -EINVAL;
+ /* Can only occur on first buffer */
+ goto error_run_postdisable;
+ }
+ indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
+ } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
+ indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
+ } else { /* should never be reached */
+ ret = -EINVAL;
+ goto error_run_postdisable;
+ }
+
+ if (indio_dev->setup_ops->postenable) {
+ ret = indio_dev->setup_ops->postenable(indio_dev);
+ if (ret) {
+ printk(KERN_INFO
+ "Buffer not started: postenable failed\n");
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+ if (indio_dev->setup_ops->postdisable)
+ indio_dev->setup_ops->postdisable(indio_dev);
+ goto error_disable_all_buffers;
+ }
+ }
+
+ if (indio_dev->available_scan_masks)
+ kfree(compound_mask);
+ else
+ kfree(old_mask);
+
+ return success;
+
+error_disable_all_buffers:
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+error_run_postdisable:
+ if (indio_dev->setup_ops->postdisable)
+ indio_dev->setup_ops->postdisable(indio_dev);
+error_remove_inserted:
+
+ if (insert_buffer)
+ list_del(&insert_buffer->buffer_list);
+ indio_dev->active_scan_mask = old_mask;
+ kfree(compound_mask);
+error_ret:
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_update_buffers);
+
+ssize_t iio_buffer_store_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ bool requested_state;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_buffer *pbuf = indio_dev->buffer;
+ bool inlist;
+
+ ret = strtobool(buf, &requested_state);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+
+ /* Find out if it is in the list */
+ inlist = iio_buffer_is_active(indio_dev, pbuf);
+ /* Already in desired state */
+ if (inlist == requested_state)
+ goto done;
+
+ if (requested_state)
+ ret = iio_update_buffers(indio_dev,
+ indio_dev->buffer, NULL);
+ else
+ ret = iio_update_buffers(indio_dev,
+ NULL, indio_dev->buffer);
+
+ if (ret < 0)
+ goto done;
+done:
+ mutex_unlock(&indio_dev->mlock);
+ return (ret < 0) ? ret : len;
+}
+EXPORT_SYMBOL(iio_buffer_store_enable);
+
+int iio_sw_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *buffer;
+ unsigned bytes;
+ dev_dbg(&indio_dev->dev, "%s\n", __func__);
+
+ list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
+ if (buffer->access->set_bytes_per_datum) {
+ bytes = iio_compute_scan_bytes(indio_dev,
+ buffer->scan_mask,
+ buffer->scan_timestamp);
+
+ buffer->access->set_bytes_per_datum(buffer, bytes);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(iio_sw_buffer_preenable);
+
+/**
+ * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected
+ * @indio_dev: the iio device
+ * @mask: scan mask to be checked
+ *
+ * Return true if exactly one bit is set in the scan mask, false otherwise. It
+ * can be used for devices where only one channel can be active for sampling at
+ * a time.
+ */
+bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
+ const unsigned long *mask)
+{
+ return bitmap_weight(mask, indio_dev->masklength) == 1;
+}
+EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot);
+
+static bool iio_validate_scan_mask(struct iio_dev *indio_dev,
+ const unsigned long *mask)
+{
+ if (!indio_dev->setup_ops->validate_scan_mask)
+ return true;
+
+ return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask);
+}
+
+/**
+ * iio_scan_mask_set() - set particular bit in the scan mask
+ * @buffer: the buffer whose scan mask we are interested in
+ * @bit: the bit to be set.
+ *
+ * Note that at this point we have no way of knowing what other
+ * buffers might request, hence this code only verifies that the
+ * individual buffers request is plausible.
+ */
+int iio_scan_mask_set(struct iio_dev *indio_dev,
+ struct iio_buffer *buffer, int bit)
+{
+ const unsigned long *mask;
+ unsigned long *trialmask;
+
+ trialmask = kmalloc(sizeof(*trialmask)*
+ BITS_TO_LONGS(indio_dev->masklength),
+ GFP_KERNEL);
+
+ if (trialmask == NULL)
+ return -ENOMEM;
+ if (!indio_dev->masklength) {
+ WARN_ON("trying to set scanmask prior to registering buffer\n");
+ goto err_invalid_mask;
+ }
+ bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength);
+ set_bit(bit, trialmask);
+
+ if (!iio_validate_scan_mask(indio_dev, trialmask))
+ goto err_invalid_mask;
+
+ if (indio_dev->available_scan_masks) {
+ mask = iio_scan_mask_match(indio_dev->available_scan_masks,
+ indio_dev->masklength,
+ trialmask);
+ if (!mask)
+ goto err_invalid_mask;
+ }
+ bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength);
+
+ kfree(trialmask);
+
+ return 0;
+
+err_invalid_mask:
+ kfree(trialmask);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(iio_scan_mask_set);
+
+int iio_scan_mask_query(struct iio_dev *indio_dev,
+ struct iio_buffer *buffer, int bit)
+{
+ if (bit > indio_dev->masklength)
+ return -EINVAL;
+
+ if (!buffer->scan_mask)
+ return 0;
+
+ /* Ensure return value is 0 or 1. */
+ return !!test_bit(bit, buffer->scan_mask);
+};
+EXPORT_SYMBOL_GPL(iio_scan_mask_query);
+
+/**
+ * struct iio_demux_table() - table describing demux memcpy ops
+ * @from: index to copy from
+ * @to: index to copy to
+ * @length: how many bytes to copy
+ * @l: list head used for management
+ */
+struct iio_demux_table {
+ unsigned from;
+ unsigned to;
+ unsigned length;
+ struct list_head l;
+};
+
+static unsigned char *iio_demux(struct iio_buffer *buffer,
+ unsigned char *datain)
+{
+ struct iio_demux_table *t;
+
+ if (list_empty(&buffer->demux_list))
+ return datain;
+ list_for_each_entry(t, &buffer->demux_list, l)
+ memcpy(buffer->demux_bounce + t->to,
+ datain + t->from, t->length);
+
+ return buffer->demux_bounce;
+}
+
+static int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data)
+{
+ unsigned char *dataout = iio_demux(buffer, data);
+
+ return buffer->access->store_to(buffer, dataout);
+}
+
+static void iio_buffer_demux_free(struct iio_buffer *buffer)
+{
+ struct iio_demux_table *p, *q;
+ list_for_each_entry_safe(p, q, &buffer->demux_list, l) {
+ list_del(&p->l);
+ kfree(p);
+ }
+}
+
+
+int iio_push_to_buffers(struct iio_dev *indio_dev, unsigned char *data)
+{
+ int ret;
+ struct iio_buffer *buf;
+
+ list_for_each_entry(buf, &indio_dev->buffer_list, buffer_list) {
+ ret = iio_push_to_buffer(buf, data);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_push_to_buffers);
+
+static int iio_buffer_update_demux(struct iio_dev *indio_dev,
+ struct iio_buffer *buffer)
+{
+ const struct iio_chan_spec *ch;
+ int ret, in_ind = -1, out_ind, length;
+ unsigned in_loc = 0, out_loc = 0;
+ struct iio_demux_table *p;
+
+ /* Clear out any old demux */
+ iio_buffer_demux_free(buffer);
+ kfree(buffer->demux_bounce);
+ buffer->demux_bounce = NULL;
+
+ /* First work out which scan mode we will actually have */
+ if (bitmap_equal(indio_dev->active_scan_mask,
+ buffer->scan_mask,
+ indio_dev->masklength))
+ return 0;
+
+ /* Now we have the two masks, work from least sig and build up sizes */
+ for_each_set_bit(out_ind,
+ buffer->scan_mask,
+ indio_dev->masklength) {
+ in_ind = find_next_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength,
+ in_ind + 1);
+ while (in_ind != out_ind) {
+ in_ind = find_next_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength,
+ in_ind + 1);
+ ch = iio_find_channel_from_si(indio_dev, in_ind);
+ length = ch->scan_type.storagebits/8;
+ /* Make sure we are aligned */
+ in_loc += length;
+ if (in_loc % length)
+ in_loc += length - in_loc % length;
+ }
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ ret = -ENOMEM;
+ goto error_clear_mux_table;
+ }
+ ch = iio_find_channel_from_si(indio_dev, in_ind);
+ length = ch->scan_type.storagebits/8;
+ if (out_loc % length)
+ out_loc += length - out_loc % length;
+ if (in_loc % length)
+ in_loc += length - in_loc % length;
+ p->from = in_loc;
+ p->to = out_loc;
+ p->length = length;
+ list_add_tail(&p->l, &buffer->demux_list);
+ out_loc += length;
+ in_loc += length;
+ }
+ /* Relies on scan_timestamp being last */
+ if (buffer->scan_timestamp) {
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ ret = -ENOMEM;
+ goto error_clear_mux_table;
+ }
+ ch = iio_find_channel_from_si(indio_dev,
+ indio_dev->scan_index_timestamp);
+ length = ch->scan_type.storagebits/8;
+ if (out_loc % length)
+ out_loc += length - out_loc % length;
+ if (in_loc % length)
+ in_loc += length - in_loc % length;
+ p->from = in_loc;
+ p->to = out_loc;
+ p->length = length;
+ list_add_tail(&p->l, &buffer->demux_list);
+ out_loc += length;
+ in_loc += length;
+ }
+ buffer->demux_bounce = kzalloc(out_loc, GFP_KERNEL);
+ if (buffer->demux_bounce == NULL) {
+ ret = -ENOMEM;
+ goto error_clear_mux_table;
+ }
+ return 0;
+
+error_clear_mux_table:
+ iio_buffer_demux_free(buffer);
+
+ return ret;
+}
+
+int iio_update_demux(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *buffer;
+ int ret;
+
+ list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
+ ret = iio_buffer_update_demux(indio_dev, buffer);
+ if (ret < 0)
+ goto error_clear_mux_table;
+ }
+ return 0;
+
+error_clear_mux_table:
+ list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
+ iio_buffer_demux_free(buffer);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_update_demux);
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
new file mode 100644
index 00000000..dd576560
--- /dev/null
+++ b/drivers/iio/industrialio-core.c
@@ -0,0 +1,1046 @@
+/* The industrial I/O core
+ *
+ * Copyright (c) 2008 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Based on elements of hwmon and input subsystems.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <linux/kdev_t.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/anon_inodes.h>
+#include <linux/debugfs.h>
+#include <linux/iio/iio.h>
+#include "iio_core.h"
+#include "iio_core_trigger.h"
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+
+/* IDA to assign each registered device a unique id */
+static DEFINE_IDA(iio_ida);
+
+static dev_t iio_devt;
+
+#define IIO_DEV_MAX 256
+struct bus_type iio_bus_type = {
+ .name = "iio",
+};
+EXPORT_SYMBOL(iio_bus_type);
+
+static struct dentry *iio_debugfs_dentry;
+
+static const char * const iio_direction[] = {
+ [0] = "in",
+ [1] = "out",
+};
+
+static const char * const iio_chan_type_name_spec[] = {
+ [IIO_VOLTAGE] = "voltage",
+ [IIO_CURRENT] = "current",
+ [IIO_POWER] = "power",
+ [IIO_ACCEL] = "accel",
+ [IIO_ANGL_VEL] = "anglvel",
+ [IIO_MAGN] = "magn",
+ [IIO_LIGHT] = "illuminance",
+ [IIO_INTENSITY] = "intensity",
+ [IIO_PROXIMITY] = "proximity",
+ [IIO_TEMP] = "temp",
+ [IIO_INCLI] = "incli",
+ [IIO_ROT] = "rot",
+ [IIO_ANGL] = "angl",
+ [IIO_TIMESTAMP] = "timestamp",
+ [IIO_CAPACITANCE] = "capacitance",
+ [IIO_ALTVOLTAGE] = "altvoltage",
+ [IIO_CCT] = "cct",
+ [IIO_PRESSURE] = "pressure",
+ [IIO_QUATERNION] = "quaternion",
+};
+
+static const char * const iio_modifier_names[] = {
+ [IIO_MOD_X] = "x",
+ [IIO_MOD_Y] = "y",
+ [IIO_MOD_Z] = "z",
+ [IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)",
+ [IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2",
+ [IIO_MOD_LIGHT_BOTH] = "both",
+ [IIO_MOD_LIGHT_IR] = "ir",
+ [IIO_MOD_LIGHT_CLEAR] = "clear",
+ [IIO_MOD_LIGHT_RED] = "red",
+ [IIO_MOD_LIGHT_GREEN] = "green",
+ [IIO_MOD_LIGHT_BLUE] = "blue",
+ [IIO_MOD_R] = "r",
+};
+
+/* relies on pairs of these shared then separate */
+static const char * const iio_chan_info_postfix[] = {
+ [IIO_CHAN_INFO_RAW] = "raw",
+ [IIO_CHAN_INFO_PROCESSED] = "input",
+ [IIO_CHAN_INFO_SCALE] = "scale",
+ [IIO_CHAN_INFO_OFFSET] = "offset",
+ [IIO_CHAN_INFO_CALIBSCALE] = "calibscale",
+ [IIO_CHAN_INFO_CALIBBIAS] = "calibbias",
+ [IIO_CHAN_INFO_PEAK] = "peak_raw",
+ [IIO_CHAN_INFO_PEAK_SCALE] = "peak_scale",
+ [IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW] = "quadrature_correction_raw",
+ [IIO_CHAN_INFO_AVERAGE_RAW] = "mean_raw",
+ [IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY]
+ = "filter_low_pass_3db_frequency",
+ [IIO_CHAN_INFO_SAMP_FREQ] = "sampling_frequency",
+ [IIO_CHAN_INFO_FREQUENCY] = "frequency",
+ [IIO_CHAN_INFO_PHASE] = "phase",
+ [IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain",
+ [IIO_CHAN_INFO_HYSTERESIS] = "hysteresis",
+};
+
+const struct iio_chan_spec
+*iio_find_channel_from_si(struct iio_dev *indio_dev, int si)
+{
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].scan_index == si)
+ return &indio_dev->channels[i];
+ return NULL;
+}
+
+/* This turns up an awful lot */
+ssize_t iio_read_const_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", to_iio_const_attr(attr)->string);
+}
+EXPORT_SYMBOL(iio_read_const_attr);
+
+static int __init iio_init(void)
+{
+ int ret;
+
+ /* Register sysfs bus */
+ ret = bus_register(&iio_bus_type);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "%s could not register bus type\n",
+ __FILE__);
+ goto error_nothing;
+ }
+
+ ret = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio");
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to allocate char dev region\n",
+ __FILE__);
+ goto error_unregister_bus_type;
+ }
+
+ iio_debugfs_dentry = debugfs_create_dir("iio", NULL);
+
+ return 0;
+
+error_unregister_bus_type:
+ bus_unregister(&iio_bus_type);
+error_nothing:
+ return ret;
+}
+
+static void __exit iio_exit(void)
+{
+ if (iio_devt)
+ unregister_chrdev_region(iio_devt, IIO_DEV_MAX);
+ bus_unregister(&iio_bus_type);
+ debugfs_remove(iio_debugfs_dentry);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct iio_dev *indio_dev = file->private_data;
+ char buf[20];
+ unsigned val = 0;
+ ssize_t len;
+ int ret;
+
+ ret = indio_dev->info->debugfs_reg_access(indio_dev,
+ indio_dev->cached_reg_addr,
+ 0, &val);
+ if (ret)
+ dev_err(indio_dev->dev.parent, "%s: read failed\n", __func__);
+
+ len = snprintf(buf, sizeof(buf), "0x%X\n", val);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static ssize_t iio_debugfs_write_reg(struct file *file,
+ const char __user *userbuf, size_t count, loff_t *ppos)
+{
+ struct iio_dev *indio_dev = file->private_data;
+ unsigned reg, val;
+ char buf[80];
+ int ret;
+
+ count = min_t(size_t, count, (sizeof(buf)-1));
+ if (copy_from_user(buf, userbuf, count))
+ return -EFAULT;
+
+ buf[count] = 0;
+
+ ret = sscanf(buf, "%i %i", &reg, &val);
+
+ switch (ret) {
+ case 1:
+ indio_dev->cached_reg_addr = reg;
+ break;
+ case 2:
+ indio_dev->cached_reg_addr = reg;
+ ret = indio_dev->info->debugfs_reg_access(indio_dev, reg,
+ val, NULL);
+ if (ret) {
+ dev_err(indio_dev->dev.parent, "%s: write failed\n",
+ __func__);
+ return ret;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations iio_debugfs_reg_fops = {
+ .open = simple_open,
+ .read = iio_debugfs_read_reg,
+ .write = iio_debugfs_write_reg,
+};
+
+static void iio_device_unregister_debugfs(struct iio_dev *indio_dev)
+{
+ debugfs_remove_recursive(indio_dev->debugfs_dentry);
+}
+
+static int iio_device_register_debugfs(struct iio_dev *indio_dev)
+{
+ struct dentry *d;
+
+ if (indio_dev->info->debugfs_reg_access == NULL)
+ return 0;
+
+ if (!iio_debugfs_dentry)
+ return 0;
+
+ indio_dev->debugfs_dentry =
+ debugfs_create_dir(dev_name(&indio_dev->dev),
+ iio_debugfs_dentry);
+ if (indio_dev->debugfs_dentry == NULL) {
+ dev_warn(indio_dev->dev.parent,
+ "Failed to create debugfs directory\n");
+ return -EFAULT;
+ }
+
+ d = debugfs_create_file("direct_reg_access", 0644,
+ indio_dev->debugfs_dentry,
+ indio_dev, &iio_debugfs_reg_fops);
+ if (!d) {
+ iio_device_unregister_debugfs(indio_dev);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+#else
+static int iio_device_register_debugfs(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static void iio_device_unregister_debugfs(struct iio_dev *indio_dev)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static ssize_t iio_read_channel_ext_info(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ ext_info = &this_attr->c->ext_info[this_attr->address];
+
+ return ext_info->read(indio_dev, ext_info->private, this_attr->c, buf);
+}
+
+static ssize_t iio_write_channel_ext_info(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ ext_info = &this_attr->c->ext_info[this_attr->address];
+
+ return ext_info->write(indio_dev, ext_info->private,
+ this_attr->c, buf, len);
+}
+
+ssize_t iio_enum_available_read(struct iio_dev *indio_dev,
+ uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+ const struct iio_enum *e = (const struct iio_enum *)priv;
+ unsigned int i;
+ size_t len = 0;
+
+ if (!e->num_items)
+ return 0;
+
+ for (i = 0; i < e->num_items; ++i)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", e->items[i]);
+
+ /* replace last space with a newline */
+ buf[len - 1] = '\n';
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(iio_enum_available_read);
+
+ssize_t iio_enum_read(struct iio_dev *indio_dev,
+ uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+ const struct iio_enum *e = (const struct iio_enum *)priv;
+ int i;
+
+ if (!e->get)
+ return -EINVAL;
+
+ i = e->get(indio_dev, chan);
+ if (i < 0)
+ return i;
+ else if (i >= e->num_items)
+ return -EINVAL;
+
+ return sprintf(buf, "%s\n", e->items[i]);
+}
+EXPORT_SYMBOL_GPL(iio_enum_read);
+
+ssize_t iio_enum_write(struct iio_dev *indio_dev,
+ uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ const struct iio_enum *e = (const struct iio_enum *)priv;
+ unsigned int i;
+ int ret;
+
+ if (!e->set)
+ return -EINVAL;
+
+ for (i = 0; i < e->num_items; i++) {
+ if (sysfs_streq(buf, e->items[i]))
+ break;
+ }
+
+ if (i == e->num_items)
+ return -EINVAL;
+
+ ret = e->set(indio_dev, chan, i);
+ return ret ? ret : len;
+}
+EXPORT_SYMBOL_GPL(iio_enum_write);
+
+static ssize_t iio_read_channel_info(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ unsigned long long tmp;
+ int val, val2;
+ bool scale_db = false;
+ int ret = indio_dev->info->read_raw(indio_dev, this_attr->c,
+ &val, &val2, this_attr->address);
+
+ if (ret < 0)
+ return ret;
+
+ switch (ret) {
+ case IIO_VAL_INT:
+ return sprintf(buf, "%d\n", val);
+ case IIO_VAL_INT_PLUS_MICRO_DB:
+ scale_db = true;
+ case IIO_VAL_INT_PLUS_MICRO:
+ if (val2 < 0)
+ return sprintf(buf, "-%d.%06u%s\n", val, -val2,
+ scale_db ? " dB" : "");
+ else
+ return sprintf(buf, "%d.%06u%s\n", val, val2,
+ scale_db ? " dB" : "");
+ case IIO_VAL_INT_PLUS_NANO:
+ if (val2 < 0)
+ return sprintf(buf, "-%d.%09u\n", val, -val2);
+ else
+ return sprintf(buf, "%d.%09u\n", val, val2);
+ case IIO_VAL_FRACTIONAL:
+ tmp = div_s64((s64)val * 1000000000LL, val2);
+ val2 = do_div(tmp, 1000000000LL);
+ val = tmp;
+ return sprintf(buf, "%d.%09u\n", val, val2);
+ case IIO_VAL_FRACTIONAL_LOG2:
+ tmp = (s64)val * 1000000000LL >> val2;
+ val2 = do_div(tmp, 1000000000LL);
+ val = tmp;
+ return sprintf(buf, "%d.%09u\n", val, val2);
+ default:
+ return 0;
+ }
+}
+
+/**
+ * iio_str_to_fixpoint() - Parse a fixed-point number from a string
+ * @str: The string to parse
+ * @fract_mult: Multiplier for the first decimal place, should be a power of 10
+ * @integer: The integer part of the number
+ * @fract: The fractional part of the number
+ *
+ * Returns 0 on success, or a negative error code if the string could not be
+ * parsed.
+ */
+int iio_str_to_fixpoint(const char *str, int fract_mult,
+ int *integer, int *fract)
+{
+ int i = 0, f = 0;
+ bool integer_part = true, negative = false;
+
+ if (str[0] == '-') {
+ negative = true;
+ str++;
+ } else if (str[0] == '+') {
+ str++;
+ }
+
+ while (*str) {
+ if ('0' <= *str && *str <= '9') {
+ if (integer_part) {
+ i = i * 10 + *str - '0';
+ } else {
+ f += fract_mult * (*str - '0');
+ fract_mult /= 10;
+ }
+ } else if (*str == '\n') {
+ if (*(str + 1) == '\0')
+ break;
+ else
+ return -EINVAL;
+ } else if (*str == '.' && integer_part) {
+ integer_part = false;
+ } else {
+ return -EINVAL;
+ }
+ str++;
+ }
+
+ if (negative) {
+ if (i)
+ i = -i;
+ else
+ f = -f;
+ }
+
+ *integer = i;
+ *fract = f;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_str_to_fixpoint);
+
+static ssize_t iio_write_channel_info(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret, fract_mult = 100000;
+ int integer, fract;
+
+ /* Assumes decimal - precision based on number of digits */
+ if (!indio_dev->info->write_raw)
+ return -EINVAL;
+
+ if (indio_dev->info->write_raw_get_fmt)
+ switch (indio_dev->info->write_raw_get_fmt(indio_dev,
+ this_attr->c, this_attr->address)) {
+ case IIO_VAL_INT_PLUS_MICRO:
+ fract_mult = 100000;
+ break;
+ case IIO_VAL_INT_PLUS_NANO:
+ fract_mult = 100000000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = iio_str_to_fixpoint(buf, fract_mult, &integer, &fract);
+ if (ret)
+ return ret;
+
+ ret = indio_dev->info->write_raw(indio_dev, this_attr->c,
+ integer, fract, this_attr->address);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static
+int __iio_device_attr_init(struct device_attribute *dev_attr,
+ const char *postfix,
+ struct iio_chan_spec const *chan,
+ ssize_t (*readfunc)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*writefunc)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len),
+ bool generic)
+{
+ int ret;
+ char *name_format, *full_postfix;
+ sysfs_attr_init(&dev_attr->attr);
+
+ /* Build up postfix of <extend_name>_<modifier>_postfix */
+ if (chan->modified && !generic) {
+ if (chan->extend_name)
+ full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_modifier_names[chan
+ ->channel2],
+ chan->extend_name,
+ postfix);
+ else
+ full_postfix = kasprintf(GFP_KERNEL, "%s_%s",
+ iio_modifier_names[chan
+ ->channel2],
+ postfix);
+ } else {
+ if (chan->extend_name == NULL)
+ full_postfix = kstrdup(postfix, GFP_KERNEL);
+ else
+ full_postfix = kasprintf(GFP_KERNEL,
+ "%s_%s",
+ chan->extend_name,
+ postfix);
+ }
+ if (full_postfix == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ if (chan->differential) { /* Differential can not have modifier */
+ if (generic)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s-%s_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ iio_chan_type_name_spec[chan->type],
+ full_postfix);
+ else if (chan->indexed)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s%d-%s%d_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ chan->channel,
+ iio_chan_type_name_spec[chan->type],
+ chan->channel2,
+ full_postfix);
+ else {
+ WARN_ON("Differential channels must be indexed\n");
+ ret = -EINVAL;
+ goto error_free_full_postfix;
+ }
+ } else { /* Single ended */
+ if (generic)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ full_postfix);
+ else if (chan->indexed)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ chan->channel,
+ full_postfix);
+ else
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_direction[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ full_postfix);
+ }
+ if (name_format == NULL) {
+ ret = -ENOMEM;
+ goto error_free_full_postfix;
+ }
+ dev_attr->attr.name = kasprintf(GFP_KERNEL,
+ name_format,
+ chan->channel,
+ chan->channel2);
+ if (dev_attr->attr.name == NULL) {
+ ret = -ENOMEM;
+ goto error_free_name_format;
+ }
+
+ if (readfunc) {
+ dev_attr->attr.mode |= S_IRUGO;
+ dev_attr->show = readfunc;
+ }
+
+ if (writefunc) {
+ dev_attr->attr.mode |= S_IWUSR;
+ dev_attr->store = writefunc;
+ }
+ kfree(name_format);
+ kfree(full_postfix);
+
+ return 0;
+
+error_free_name_format:
+ kfree(name_format);
+error_free_full_postfix:
+ kfree(full_postfix);
+error_ret:
+ return ret;
+}
+
+static void __iio_device_attr_deinit(struct device_attribute *dev_attr)
+{
+ kfree(dev_attr->attr.name);
+}
+
+int __iio_add_chan_devattr(const char *postfix,
+ struct iio_chan_spec const *chan,
+ ssize_t (*readfunc)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*writefunc)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len),
+ u64 mask,
+ bool generic,
+ struct device *dev,
+ struct list_head *attr_list)
+{
+ int ret;
+ struct iio_dev_attr *iio_attr, *t;
+
+ iio_attr = kzalloc(sizeof *iio_attr, GFP_KERNEL);
+ if (iio_attr == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ ret = __iio_device_attr_init(&iio_attr->dev_attr,
+ postfix, chan,
+ readfunc, writefunc, generic);
+ if (ret)
+ goto error_iio_dev_attr_free;
+ iio_attr->c = chan;
+ iio_attr->address = mask;
+ list_for_each_entry(t, attr_list, l)
+ if (strcmp(t->dev_attr.attr.name,
+ iio_attr->dev_attr.attr.name) == 0) {
+ if (!generic)
+ dev_err(dev, "tried to double register : %s\n",
+ t->dev_attr.attr.name);
+ ret = -EBUSY;
+ goto error_device_attr_deinit;
+ }
+ list_add(&iio_attr->l, attr_list);
+
+ return 0;
+
+error_device_attr_deinit:
+ __iio_device_attr_deinit(&iio_attr->dev_attr);
+error_iio_dev_attr_free:
+ kfree(iio_attr);
+error_ret:
+ return ret;
+}
+
+static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ int ret, attrcount = 0;
+ int i;
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ if (chan->channel < 0)
+ return 0;
+ for_each_set_bit(i, &chan->info_mask_separate, sizeof(long)*8) {
+ ret = __iio_add_chan_devattr(iio_chan_info_postfix[i],
+ chan,
+ &iio_read_channel_info,
+ &iio_write_channel_info,
+ i,
+ 0,
+ &indio_dev->dev,
+ &indio_dev->channel_attr_list);
+ if (ret < 0)
+ goto error_ret;
+ attrcount++;
+ }
+ for_each_set_bit(i, &chan->info_mask_shared_by_type, sizeof(long)*8) {
+ ret = __iio_add_chan_devattr(iio_chan_info_postfix[i],
+ chan,
+ &iio_read_channel_info,
+ &iio_write_channel_info,
+ i,
+ 1,
+ &indio_dev->dev,
+ &indio_dev->channel_attr_list);
+ if (ret == -EBUSY) {
+ ret = 0;
+ continue;
+ } else if (ret < 0) {
+ goto error_ret;
+ }
+ attrcount++;
+ }
+
+ if (chan->ext_info) {
+ unsigned int i = 0;
+ for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
+ ret = __iio_add_chan_devattr(ext_info->name,
+ chan,
+ ext_info->read ?
+ &iio_read_channel_ext_info : NULL,
+ ext_info->write ?
+ &iio_write_channel_ext_info : NULL,
+ i,
+ ext_info->shared,
+ &indio_dev->dev,
+ &indio_dev->channel_attr_list);
+ i++;
+ if (ret == -EBUSY && ext_info->shared)
+ continue;
+
+ if (ret)
+ goto error_ret;
+
+ attrcount++;
+ }
+ }
+
+ ret = attrcount;
+error_ret:
+ return ret;
+}
+
+static void iio_device_remove_and_free_read_attr(struct iio_dev *indio_dev,
+ struct iio_dev_attr *p)
+{
+ kfree(p->dev_attr.attr.name);
+ kfree(p);
+}
+
+static ssize_t iio_show_dev_name(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ return sprintf(buf, "%s\n", indio_dev->name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL);
+
+static int iio_device_register_sysfs(struct iio_dev *indio_dev)
+{
+ int i, ret = 0, attrcount, attrn, attrcount_orig = 0;
+ struct iio_dev_attr *p, *n;
+ struct attribute **attr;
+
+ /* First count elements in any existing group */
+ if (indio_dev->info->attrs) {
+ attr = indio_dev->info->attrs->attrs;
+ while (*attr++ != NULL)
+ attrcount_orig++;
+ }
+ attrcount = attrcount_orig;
+ /*
+ * New channel registration method - relies on the fact a group does
+ * not need to be initialized if its name is NULL.
+ */
+ if (indio_dev->channels)
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ ret = iio_device_add_channel_sysfs(indio_dev,
+ &indio_dev
+ ->channels[i]);
+ if (ret < 0)
+ goto error_clear_attrs;
+ attrcount += ret;
+ }
+
+ if (indio_dev->name)
+ attrcount++;
+
+ indio_dev->chan_attr_group.attrs = kcalloc(attrcount + 1,
+ sizeof(indio_dev->chan_attr_group.attrs[0]),
+ GFP_KERNEL);
+ if (indio_dev->chan_attr_group.attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_clear_attrs;
+ }
+ /* Copy across original attributes */
+ if (indio_dev->info->attrs)
+ memcpy(indio_dev->chan_attr_group.attrs,
+ indio_dev->info->attrs->attrs,
+ sizeof(indio_dev->chan_attr_group.attrs[0])
+ *attrcount_orig);
+ attrn = attrcount_orig;
+ /* Add all elements from the list. */
+ list_for_each_entry(p, &indio_dev->channel_attr_list, l)
+ indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr;
+ if (indio_dev->name)
+ indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr;
+
+ indio_dev->groups[indio_dev->groupcounter++] =
+ &indio_dev->chan_attr_group;
+
+ return 0;
+
+error_clear_attrs:
+ list_for_each_entry_safe(p, n,
+ &indio_dev->channel_attr_list, l) {
+ list_del(&p->l);
+ iio_device_remove_and_free_read_attr(indio_dev, p);
+ }
+
+ return ret;
+}
+
+static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
+{
+
+ struct iio_dev_attr *p, *n;
+
+ list_for_each_entry_safe(p, n, &indio_dev->channel_attr_list, l) {
+ list_del(&p->l);
+ iio_device_remove_and_free_read_attr(indio_dev, p);
+ }
+ kfree(indio_dev->chan_attr_group.attrs);
+}
+
+static void iio_dev_release(struct device *device)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(device);
+ if (indio_dev->chrdev.dev)
+ cdev_del(&indio_dev->chrdev);
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ iio_device_unregister_trigger_consumer(indio_dev);
+ iio_device_unregister_eventset(indio_dev);
+ iio_device_unregister_sysfs(indio_dev);
+ iio_device_unregister_debugfs(indio_dev);
+
+ ida_simple_remove(&iio_ida, indio_dev->id);
+ kfree(indio_dev);
+}
+
+struct device_type iio_device_type = {
+ .name = "iio_device",
+ .release = iio_dev_release,
+};
+
+struct iio_dev *iio_device_alloc(int sizeof_priv)
+{
+ struct iio_dev *dev;
+ size_t alloc_size;
+
+ alloc_size = sizeof(struct iio_dev);
+ if (sizeof_priv) {
+ alloc_size = ALIGN(alloc_size, IIO_ALIGN);
+ alloc_size += sizeof_priv;
+ }
+ /* ensure 32-byte alignment of whole construct ? */
+ alloc_size += IIO_ALIGN - 1;
+
+ dev = kzalloc(alloc_size, GFP_KERNEL);
+
+ if (dev) {
+ dev->dev.groups = dev->groups;
+ dev->dev.type = &iio_device_type;
+ dev->dev.bus = &iio_bus_type;
+ device_initialize(&dev->dev);
+ dev_set_drvdata(&dev->dev, (void *)dev);
+ mutex_init(&dev->mlock);
+ mutex_init(&dev->info_exist_lock);
+ INIT_LIST_HEAD(&dev->channel_attr_list);
+
+ dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);
+ if (dev->id < 0) {
+ /* cannot use a dev_err as the name isn't available */
+ printk(KERN_ERR "Failed to get id\n");
+ kfree(dev);
+ return NULL;
+ }
+ dev_set_name(&dev->dev, "iio:device%d", dev->id);
+ INIT_LIST_HEAD(&dev->buffer_list);
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL(iio_device_alloc);
+
+void iio_device_free(struct iio_dev *dev)
+{
+ if (dev)
+ put_device(&dev->dev);
+}
+EXPORT_SYMBOL(iio_device_free);
+
+/**
+ * iio_chrdev_open() - chrdev file open for buffer access and ioctls
+ **/
+static int iio_chrdev_open(struct inode *inode, struct file *filp)
+{
+ struct iio_dev *indio_dev = container_of(inode->i_cdev,
+ struct iio_dev, chrdev);
+
+ if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags))
+ return -EBUSY;
+
+ filp->private_data = indio_dev;
+
+ return 0;
+}
+
+/**
+ * iio_chrdev_release() - chrdev file close buffer access and ioctls
+ **/
+static int iio_chrdev_release(struct inode *inode, struct file *filp)
+{
+ struct iio_dev *indio_dev = container_of(inode->i_cdev,
+ struct iio_dev, chrdev);
+ clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
+ return 0;
+}
+
+/* Somewhat of a cross file organization violation - ioctls here are actually
+ * event related */
+static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct iio_dev *indio_dev = filp->private_data;
+ int __user *ip = (int __user *)arg;
+ int fd;
+
+ if (cmd == IIO_GET_EVENT_FD_IOCTL) {
+ fd = iio_event_getfd(indio_dev);
+ if (copy_to_user(ip, &fd, sizeof(fd)))
+ return -EFAULT;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static const struct file_operations iio_buffer_fileops = {
+ .read = iio_buffer_read_first_n_outer_addr,
+ .release = iio_chrdev_release,
+ .open = iio_chrdev_open,
+ .poll = iio_buffer_poll_addr,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = iio_ioctl,
+ .compat_ioctl = iio_ioctl,
+};
+
+static const struct iio_buffer_setup_ops noop_ring_setup_ops;
+
+int iio_device_register(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ /* If the calling driver did not initialize of_node, do it here */
+ if (!indio_dev->dev.of_node && indio_dev->dev.parent)
+ indio_dev->dev.of_node = indio_dev->dev.parent->of_node;
+
+ /* configure elements for the chrdev */
+ indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
+
+ ret = iio_device_register_debugfs(indio_dev);
+ if (ret) {
+ dev_err(indio_dev->dev.parent,
+ "Failed to register debugfs interfaces\n");
+ goto error_ret;
+ }
+ ret = iio_device_register_sysfs(indio_dev);
+ if (ret) {
+ dev_err(indio_dev->dev.parent,
+ "Failed to register sysfs interfaces\n");
+ goto error_unreg_debugfs;
+ }
+ ret = iio_device_register_eventset(indio_dev);
+ if (ret) {
+ dev_err(indio_dev->dev.parent,
+ "Failed to register event set\n");
+ goto error_free_sysfs;
+ }
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ iio_device_register_trigger_consumer(indio_dev);
+
+ if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
+ indio_dev->setup_ops == NULL)
+ indio_dev->setup_ops = &noop_ring_setup_ops;
+
+ ret = device_add(&indio_dev->dev);
+ if (ret < 0)
+ goto error_unreg_eventset;
+ cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
+ indio_dev->chrdev.owner = indio_dev->info->driver_module;
+ ret = cdev_add(&indio_dev->chrdev, indio_dev->dev.devt, 1);
+ if (ret < 0)
+ goto error_del_device;
+ return 0;
+
+error_del_device:
+ device_del(&indio_dev->dev);
+error_unreg_eventset:
+ iio_device_unregister_eventset(indio_dev);
+error_free_sysfs:
+ iio_device_unregister_sysfs(indio_dev);
+error_unreg_debugfs:
+ iio_device_unregister_debugfs(indio_dev);
+error_ret:
+ return ret;
+}
+EXPORT_SYMBOL(iio_device_register);
+
+void iio_device_unregister(struct iio_dev *indio_dev)
+{
+ mutex_lock(&indio_dev->info_exist_lock);
+ indio_dev->info = NULL;
+ mutex_unlock(&indio_dev->info_exist_lock);
+ device_del(&indio_dev->dev);
+}
+EXPORT_SYMBOL(iio_device_unregister);
+subsys_initcall(iio_init);
+module_exit(iio_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Industrial I/O core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
new file mode 100644
index 00000000..145c9861
--- /dev/null
+++ b/drivers/iio/industrialio-event.c
@@ -0,0 +1,456 @@
+/* Industrial I/O event handling
+ *
+ * Copyright (c) 2008 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Based on elements of hwmon and input subsystems.
+ */
+
+#include <linux/anon_inodes.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/iio/iio.h>
+#include "iio_core.h"
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+
+/**
+ * struct iio_event_interface - chrdev interface for an event line
+ * @wait: wait queue to allow blocking reads of events
+ * @det_events: list of detected events
+ * @dev_attr_list: list of event interface sysfs attribute
+ * @flags: file operations related flags including busy flag.
+ * @group: event interface sysfs attribute group
+ */
+struct iio_event_interface {
+ wait_queue_head_t wait;
+ struct mutex read_lock;
+ DECLARE_KFIFO(det_events, struct iio_event_data, 16);
+
+ struct list_head dev_attr_list;
+ unsigned long flags;
+ struct attribute_group group;
+};
+
+int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
+{
+ struct iio_event_interface *ev_int = indio_dev->event_interface;
+ struct iio_event_data ev;
+ unsigned long flags;
+ int copied;
+
+ /* Does anyone care? */
+ spin_lock_irqsave(&ev_int->wait.lock, flags);
+ if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
+
+ ev.id = ev_code;
+ ev.timestamp = timestamp;
+
+ copied = kfifo_put(&ev_int->det_events, &ev);
+ if (copied != 0)
+ wake_up_locked_poll(&ev_int->wait, POLLIN);
+ }
+ spin_unlock_irqrestore(&ev_int->wait.lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(iio_push_event);
+
+/**
+ * iio_event_poll() - poll the event queue to find out if it has data
+ */
+static unsigned int iio_event_poll(struct file *filep,
+ struct poll_table_struct *wait)
+{
+ struct iio_event_interface *ev_int = filep->private_data;
+ unsigned int events = 0;
+
+ poll_wait(filep, &ev_int->wait, wait);
+
+ spin_lock_irq(&ev_int->wait.lock);
+ if (!kfifo_is_empty(&ev_int->det_events))
+ events = POLLIN | POLLRDNORM;
+ spin_unlock_irq(&ev_int->wait.lock);
+
+ return events;
+}
+
+static ssize_t iio_event_chrdev_read(struct file *filep,
+ char __user *buf,
+ size_t count,
+ loff_t *f_ps)
+{
+ struct iio_event_interface *ev_int = filep->private_data;
+ unsigned int copied;
+ int ret;
+
+ if (count < sizeof(struct iio_event_data))
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&ev_int->read_lock))
+ return -ERESTARTSYS;
+
+ if (kfifo_is_empty(&ev_int->det_events)) {
+ if (filep->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ goto error_unlock;
+ }
+ /* Blocking on device; waiting for something to be there */
+ ret = wait_event_interruptible(ev_int->wait,
+ !kfifo_is_empty(&ev_int->det_events));
+ if (ret)
+ goto error_unlock;
+ /* Single access device so no one else can get the data */
+ }
+
+ ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied);
+
+error_unlock:
+ mutex_unlock(&ev_int->read_lock);
+
+ return ret ? ret : copied;
+}
+
+static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
+{
+ struct iio_event_interface *ev_int = filep->private_data;
+
+ spin_lock_irq(&ev_int->wait.lock);
+ __clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
+ /*
+ * In order to maintain a clean state for reopening,
+ * clear out any awaiting events. The mask will prevent
+ * any new __iio_push_event calls running.
+ */
+ kfifo_reset_out(&ev_int->det_events);
+ spin_unlock_irq(&ev_int->wait.lock);
+
+ return 0;
+}
+
+static const struct file_operations iio_event_chrdev_fileops = {
+ .read = iio_event_chrdev_read,
+ .poll = iio_event_poll,
+ .release = iio_event_chrdev_release,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+};
+
+int iio_event_getfd(struct iio_dev *indio_dev)
+{
+ struct iio_event_interface *ev_int = indio_dev->event_interface;
+ int fd;
+
+ if (ev_int == NULL)
+ return -ENODEV;
+
+ spin_lock_irq(&ev_int->wait.lock);
+ if (__test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
+ spin_unlock_irq(&ev_int->wait.lock);
+ return -EBUSY;
+ }
+ spin_unlock_irq(&ev_int->wait.lock);
+ fd = anon_inode_getfd("iio:event",
+ &iio_event_chrdev_fileops, ev_int, O_RDONLY);
+ if (fd < 0) {
+ spin_lock_irq(&ev_int->wait.lock);
+ __clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
+ spin_unlock_irq(&ev_int->wait.lock);
+ }
+ return fd;
+}
+
+static const char * const iio_ev_type_text[] = {
+ [IIO_EV_TYPE_THRESH] = "thresh",
+ [IIO_EV_TYPE_MAG] = "mag",
+ [IIO_EV_TYPE_ROC] = "roc",
+ [IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
+ [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
+};
+
+static const char * const iio_ev_dir_text[] = {
+ [IIO_EV_DIR_EITHER] = "either",
+ [IIO_EV_DIR_RISING] = "rising",
+ [IIO_EV_DIR_FALLING] = "falling"
+};
+
+static ssize_t iio_ev_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ bool val;
+
+ ret = strtobool(buf, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = indio_dev->info->write_event_config(indio_dev,
+ this_attr->address,
+ val);
+ return (ret < 0) ? ret : len;
+}
+
+static ssize_t iio_ev_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int val = indio_dev->info->read_event_config(indio_dev,
+ this_attr->address);
+
+ if (val < 0)
+ return val;
+ else
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t iio_ev_value_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int val, ret;
+
+ ret = indio_dev->info->read_event_value(indio_dev,
+ this_attr->address, &val);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t iio_ev_value_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int val;
+ int ret;
+
+ if (!indio_dev->info->write_event_value)
+ return -EINVAL;
+
+ ret = kstrtoint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = indio_dev->info->write_event_value(indio_dev, this_attr->address,
+ val);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+static int iio_device_add_event_sysfs(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ int ret = 0, i, attrcount = 0;
+ u64 mask = 0;
+ char *postfix;
+ if (!chan->event_mask)
+ return 0;
+
+ for_each_set_bit(i, &chan->event_mask, sizeof(chan->event_mask)*8) {
+ postfix = kasprintf(GFP_KERNEL, "%s_%s_en",
+ iio_ev_type_text[i/IIO_EV_DIR_MAX],
+ iio_ev_dir_text[i%IIO_EV_DIR_MAX]);
+ if (postfix == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ if (chan->modified)
+ mask = IIO_MOD_EVENT_CODE(chan->type, 0, chan->channel,
+ i/IIO_EV_DIR_MAX,
+ i%IIO_EV_DIR_MAX);
+ else if (chan->differential)
+ mask = IIO_EVENT_CODE(chan->type,
+ 0, 0,
+ i%IIO_EV_DIR_MAX,
+ i/IIO_EV_DIR_MAX,
+ 0,
+ chan->channel,
+ chan->channel2);
+ else
+ mask = IIO_UNMOD_EVENT_CODE(chan->type,
+ chan->channel,
+ i/IIO_EV_DIR_MAX,
+ i%IIO_EV_DIR_MAX);
+
+ ret = __iio_add_chan_devattr(postfix,
+ chan,
+ &iio_ev_state_show,
+ iio_ev_state_store,
+ mask,
+ 0,
+ &indio_dev->dev,
+ &indio_dev->event_interface->
+ dev_attr_list);
+ kfree(postfix);
+ if (ret)
+ goto error_ret;
+ attrcount++;
+ postfix = kasprintf(GFP_KERNEL, "%s_%s_value",
+ iio_ev_type_text[i/IIO_EV_DIR_MAX],
+ iio_ev_dir_text[i%IIO_EV_DIR_MAX]);
+ if (postfix == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ ret = __iio_add_chan_devattr(postfix, chan,
+ iio_ev_value_show,
+ iio_ev_value_store,
+ mask,
+ 0,
+ &indio_dev->dev,
+ &indio_dev->event_interface->
+ dev_attr_list);
+ kfree(postfix);
+ if (ret)
+ goto error_ret;
+ attrcount++;
+ }
+ ret = attrcount;
+error_ret:
+ return ret;
+}
+
+static inline void __iio_remove_event_config_attrs(struct iio_dev *indio_dev)
+{
+ struct iio_dev_attr *p, *n;
+ list_for_each_entry_safe(p, n,
+ &indio_dev->event_interface->
+ dev_attr_list, l) {
+ kfree(p->dev_attr.attr.name);
+ kfree(p);
+ }
+}
+
+static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev)
+{
+ int j, ret, attrcount = 0;
+
+ /* Dynically created from the channels array */
+ for (j = 0; j < indio_dev->num_channels; j++) {
+ ret = iio_device_add_event_sysfs(indio_dev,
+ &indio_dev->channels[j]);
+ if (ret < 0)
+ return ret;
+ attrcount += ret;
+ }
+ return attrcount;
+}
+
+static bool iio_check_for_dynamic_events(struct iio_dev *indio_dev)
+{
+ int j;
+
+ for (j = 0; j < indio_dev->num_channels; j++)
+ if (indio_dev->channels[j].event_mask != 0)
+ return true;
+ return false;
+}
+
+static void iio_setup_ev_int(struct iio_event_interface *ev_int)
+{
+ INIT_KFIFO(ev_int->det_events);
+ init_waitqueue_head(&ev_int->wait);
+ mutex_init(&ev_int->read_lock);
+}
+
+static const char *iio_event_group_name = "events";
+int iio_device_register_eventset(struct iio_dev *indio_dev)
+{
+ struct iio_dev_attr *p;
+ int ret = 0, attrcount_orig = 0, attrcount, attrn;
+ struct attribute **attr;
+
+ if (!(indio_dev->info->event_attrs ||
+ iio_check_for_dynamic_events(indio_dev)))
+ return 0;
+
+ indio_dev->event_interface =
+ kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL);
+ if (indio_dev->event_interface == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ INIT_LIST_HEAD(&indio_dev->event_interface->dev_attr_list);
+
+ iio_setup_ev_int(indio_dev->event_interface);
+ if (indio_dev->info->event_attrs != NULL) {
+ attr = indio_dev->info->event_attrs->attrs;
+ while (*attr++ != NULL)
+ attrcount_orig++;
+ }
+ attrcount = attrcount_orig;
+ if (indio_dev->channels) {
+ ret = __iio_add_event_config_attrs(indio_dev);
+ if (ret < 0)
+ goto error_free_setup_event_lines;
+ attrcount += ret;
+ }
+
+ indio_dev->event_interface->group.name = iio_event_group_name;
+ indio_dev->event_interface->group.attrs = kcalloc(attrcount + 1,
+ sizeof(indio_dev->event_interface->group.attrs[0]),
+ GFP_KERNEL);
+ if (indio_dev->event_interface->group.attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_free_setup_event_lines;
+ }
+ if (indio_dev->info->event_attrs)
+ memcpy(indio_dev->event_interface->group.attrs,
+ indio_dev->info->event_attrs->attrs,
+ sizeof(indio_dev->event_interface->group.attrs[0])
+ *attrcount_orig);
+ attrn = attrcount_orig;
+ /* Add all elements from the list. */
+ list_for_each_entry(p,
+ &indio_dev->event_interface->dev_attr_list,
+ l)
+ indio_dev->event_interface->group.attrs[attrn++] =
+ &p->dev_attr.attr;
+ indio_dev->groups[indio_dev->groupcounter++] =
+ &indio_dev->event_interface->group;
+
+ return 0;
+
+error_free_setup_event_lines:
+ __iio_remove_event_config_attrs(indio_dev);
+ mutex_destroy(&indio_dev->event_interface->read_lock);
+ kfree(indio_dev->event_interface);
+error_ret:
+
+ return ret;
+}
+
+void iio_device_unregister_eventset(struct iio_dev *indio_dev)
+{
+ if (indio_dev->event_interface == NULL)
+ return;
+ __iio_remove_event_config_attrs(indio_dev);
+ kfree(indio_dev->event_interface->group.attrs);
+ mutex_destroy(&indio_dev->event_interface->read_lock);
+ kfree(indio_dev->event_interface);
+}
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
new file mode 100644
index 00000000..4d6c7d84
--- /dev/null
+++ b/drivers/iio/industrialio-trigger.c
@@ -0,0 +1,496 @@
+/* The industrial I/O core, trigger handling functions
+ *
+ * Copyright (c) 2008 Jonathan Cameron
+ *
+ * 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/kernel.h>
+#include <linux/idr.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include "iio_core.h"
+#include "iio_core_trigger.h"
+#include <linux/iio/trigger_consumer.h>
+
+/* RFC - Question of approach
+ * Make the common case (single sensor single trigger)
+ * simple by starting trigger capture from when first sensors
+ * is added.
+ *
+ * Complex simultaneous start requires use of 'hold' functionality
+ * of the trigger. (not implemented)
+ *
+ * Any other suggestions?
+ */
+
+static DEFINE_IDA(iio_trigger_ida);
+
+/* Single list of all available triggers */
+static LIST_HEAD(iio_trigger_list);
+static DEFINE_MUTEX(iio_trigger_list_lock);
+
+/**
+ * iio_trigger_read_name() - retrieve useful identifying name
+ **/
+static ssize_t iio_trigger_read_name(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ return sprintf(buf, "%s\n", trig->name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *iio_trig_dev_attrs[] = {
+ &dev_attr_name.attr,
+ NULL,
+};
+
+static struct attribute_group iio_trig_attr_group = {
+ .attrs = iio_trig_dev_attrs,
+};
+
+static const struct attribute_group *iio_trig_attr_groups[] = {
+ &iio_trig_attr_group,
+ NULL
+};
+
+int iio_trigger_register(struct iio_trigger *trig_info)
+{
+ int ret;
+
+ trig_info->id = ida_simple_get(&iio_trigger_ida, 0, 0, GFP_KERNEL);
+ if (trig_info->id < 0) {
+ ret = trig_info->id;
+ goto error_ret;
+ }
+ /* Set the name used for the sysfs directory etc */
+ dev_set_name(&trig_info->dev, "trigger%ld",
+ (unsigned long) trig_info->id);
+
+ ret = device_add(&trig_info->dev);
+ if (ret)
+ goto error_unregister_id;
+
+ /* Add to list of available triggers held by the IIO core */
+ mutex_lock(&iio_trigger_list_lock);
+ list_add_tail(&trig_info->list, &iio_trigger_list);
+ mutex_unlock(&iio_trigger_list_lock);
+
+ return 0;
+
+error_unregister_id:
+ ida_simple_remove(&iio_trigger_ida, trig_info->id);
+error_ret:
+ return ret;
+}
+EXPORT_SYMBOL(iio_trigger_register);
+
+void iio_trigger_unregister(struct iio_trigger *trig_info)
+{
+ mutex_lock(&iio_trigger_list_lock);
+ list_del(&trig_info->list);
+ mutex_unlock(&iio_trigger_list_lock);
+
+ ida_simple_remove(&iio_trigger_ida, trig_info->id);
+ /* Possible issue in here */
+ device_unregister(&trig_info->dev);
+}
+EXPORT_SYMBOL(iio_trigger_unregister);
+
+static struct iio_trigger *iio_trigger_find_by_name(const char *name,
+ size_t len)
+{
+ struct iio_trigger *trig = NULL, *iter;
+
+ mutex_lock(&iio_trigger_list_lock);
+ list_for_each_entry(iter, &iio_trigger_list, list)
+ if (sysfs_streq(iter->name, name)) {
+ trig = iter;
+ break;
+ }
+ mutex_unlock(&iio_trigger_list_lock);
+
+ return trig;
+}
+
+void iio_trigger_poll(struct iio_trigger *trig, s64 time)
+{
+ int i;
+ if (!trig->use_count)
+ for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++)
+ if (trig->subirqs[i].enabled) {
+ trig->use_count++;
+ generic_handle_irq(trig->subirq_base + i);
+ }
+}
+EXPORT_SYMBOL(iio_trigger_poll);
+
+irqreturn_t iio_trigger_generic_data_rdy_poll(int irq, void *private)
+{
+ iio_trigger_poll(private, iio_get_time_ns());
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(iio_trigger_generic_data_rdy_poll);
+
+void iio_trigger_poll_chained(struct iio_trigger *trig, s64 time)
+{
+ int i;
+ if (!trig->use_count)
+ for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++)
+ if (trig->subirqs[i].enabled) {
+ trig->use_count++;
+ handle_nested_irq(trig->subirq_base + i);
+ }
+}
+EXPORT_SYMBOL(iio_trigger_poll_chained);
+
+void iio_trigger_notify_done(struct iio_trigger *trig)
+{
+ trig->use_count--;
+ if (trig->use_count == 0 && trig->ops && trig->ops->try_reenable)
+ if (trig->ops->try_reenable(trig))
+ /* Missed an interrupt so launch new poll now */
+ iio_trigger_poll(trig, 0);
+}
+EXPORT_SYMBOL(iio_trigger_notify_done);
+
+/* Trigger Consumer related functions */
+static int iio_trigger_get_irq(struct iio_trigger *trig)
+{
+ int ret;
+ mutex_lock(&trig->pool_lock);
+ ret = bitmap_find_free_region(trig->pool,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER,
+ ilog2(1));
+ mutex_unlock(&trig->pool_lock);
+ if (ret >= 0)
+ ret += trig->subirq_base;
+
+ return ret;
+}
+
+static void iio_trigger_put_irq(struct iio_trigger *trig, int irq)
+{
+ mutex_lock(&trig->pool_lock);
+ clear_bit(irq - trig->subirq_base, trig->pool);
+ mutex_unlock(&trig->pool_lock);
+}
+
+/* Complexity in here. With certain triggers (datardy) an acknowledgement
+ * may be needed if the pollfuncs do not include the data read for the
+ * triggering device.
+ * This is not currently handled. Alternative of not enabling trigger unless
+ * the relevant function is in there may be the best option.
+ */
+/* Worth protecting against double additions? */
+static int iio_trigger_attach_poll_func(struct iio_trigger *trig,
+ struct iio_poll_func *pf)
+{
+ int ret = 0;
+ bool notinuse
+ = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
+
+ /* Prevent the module from being removed whilst attached to a trigger */
+ __module_get(pf->indio_dev->info->driver_module);
+ pf->irq = iio_trigger_get_irq(trig);
+ ret = request_threaded_irq(pf->irq, pf->h, pf->thread,
+ pf->type, pf->name,
+ pf);
+ if (ret < 0) {
+ module_put(pf->indio_dev->info->driver_module);
+ return ret;
+ }
+
+ if (trig->ops && trig->ops->set_trigger_state && notinuse) {
+ ret = trig->ops->set_trigger_state(trig, true);
+ if (ret < 0)
+ module_put(pf->indio_dev->info->driver_module);
+ }
+
+ return ret;
+}
+
+static int iio_trigger_detach_poll_func(struct iio_trigger *trig,
+ struct iio_poll_func *pf)
+{
+ int ret = 0;
+ bool no_other_users
+ = (bitmap_weight(trig->pool,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER)
+ == 1);
+ if (trig->ops && trig->ops->set_trigger_state && no_other_users) {
+ ret = trig->ops->set_trigger_state(trig, false);
+ if (ret)
+ goto error_ret;
+ }
+ iio_trigger_put_irq(trig, pf->irq);
+ free_irq(pf->irq, pf);
+ module_put(pf->indio_dev->info->driver_module);
+
+error_ret:
+ return ret;
+}
+
+irqreturn_t iio_pollfunc_store_time(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ pf->timestamp = iio_get_time_ns();
+ return IRQ_WAKE_THREAD;
+}
+EXPORT_SYMBOL(iio_pollfunc_store_time);
+
+struct iio_poll_func
+*iio_alloc_pollfunc(irqreturn_t (*h)(int irq, void *p),
+ irqreturn_t (*thread)(int irq, void *p),
+ int type,
+ struct iio_dev *indio_dev,
+ const char *fmt,
+ ...)
+{
+ va_list vargs;
+ struct iio_poll_func *pf;
+
+ pf = kmalloc(sizeof *pf, GFP_KERNEL);
+ if (pf == NULL)
+ return NULL;
+ va_start(vargs, fmt);
+ pf->name = kvasprintf(GFP_KERNEL, fmt, vargs);
+ va_end(vargs);
+ if (pf->name == NULL) {
+ kfree(pf);
+ return NULL;
+ }
+ pf->h = h;
+ pf->thread = thread;
+ pf->type = type;
+ pf->indio_dev = indio_dev;
+
+ return pf;
+}
+EXPORT_SYMBOL_GPL(iio_alloc_pollfunc);
+
+void iio_dealloc_pollfunc(struct iio_poll_func *pf)
+{
+ kfree(pf->name);
+ kfree(pf);
+}
+EXPORT_SYMBOL_GPL(iio_dealloc_pollfunc);
+
+/**
+ * iio_trigger_read_current() - trigger consumer sysfs query current trigger
+ *
+ * For trigger consumers the current_trigger interface allows the trigger
+ * used by the device to be queried.
+ **/
+static ssize_t iio_trigger_read_current(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+
+ if (indio_dev->trig)
+ return sprintf(buf, "%s\n", indio_dev->trig->name);
+ return 0;
+}
+
+/**
+ * iio_trigger_write_current() - trigger consumer sysfs set current trigger
+ *
+ * For trigger consumers the current_trigger interface allows the trigger
+ * used for this device to be specified at run time based on the triggers
+ * name.
+ **/
+static ssize_t iio_trigger_write_current(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_trigger *oldtrig = indio_dev->trig;
+ struct iio_trigger *trig;
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ trig = iio_trigger_find_by_name(buf, len);
+ if (oldtrig == trig)
+ return len;
+
+ if (trig && indio_dev->info->validate_trigger) {
+ ret = indio_dev->info->validate_trigger(indio_dev, trig);
+ if (ret)
+ return ret;
+ }
+
+ if (trig && trig->ops && trig->ops->validate_device) {
+ ret = trig->ops->validate_device(trig, indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ indio_dev->trig = trig;
+
+ if (oldtrig && indio_dev->trig != oldtrig)
+ iio_trigger_put(oldtrig);
+ if (indio_dev->trig)
+ iio_trigger_get(indio_dev->trig);
+
+ return len;
+}
+
+static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR,
+ iio_trigger_read_current,
+ iio_trigger_write_current);
+
+static struct attribute *iio_trigger_consumer_attrs[] = {
+ &dev_attr_current_trigger.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_trigger_consumer_attr_group = {
+ .name = "trigger",
+ .attrs = iio_trigger_consumer_attrs,
+};
+
+static void iio_trig_release(struct device *device)
+{
+ struct iio_trigger *trig = to_iio_trigger(device);
+ int i;
+
+ if (trig->subirq_base) {
+ for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
+ irq_modify_status(trig->subirq_base + i,
+ IRQ_NOAUTOEN,
+ IRQ_NOREQUEST | IRQ_NOPROBE);
+ irq_set_chip(trig->subirq_base + i,
+ NULL);
+ irq_set_handler(trig->subirq_base + i,
+ NULL);
+ }
+
+ irq_free_descs(trig->subirq_base,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER);
+ }
+ kfree(trig->name);
+ kfree(trig);
+}
+
+static struct device_type iio_trig_type = {
+ .release = iio_trig_release,
+ .groups = iio_trig_attr_groups,
+};
+
+static void iio_trig_subirqmask(struct irq_data *d)
+{
+ struct irq_chip *chip = irq_data_get_irq_chip(d);
+ struct iio_trigger *trig
+ = container_of(chip,
+ struct iio_trigger, subirq_chip);
+ trig->subirqs[d->irq - trig->subirq_base].enabled = false;
+}
+
+static void iio_trig_subirqunmask(struct irq_data *d)
+{
+ struct irq_chip *chip = irq_data_get_irq_chip(d);
+ struct iio_trigger *trig
+ = container_of(chip,
+ struct iio_trigger, subirq_chip);
+ trig->subirqs[d->irq - trig->subirq_base].enabled = true;
+}
+
+struct iio_trigger *iio_trigger_alloc(const char *fmt, ...)
+{
+ va_list vargs;
+ struct iio_trigger *trig;
+ trig = kzalloc(sizeof *trig, GFP_KERNEL);
+ if (trig) {
+ int i;
+ trig->dev.type = &iio_trig_type;
+ trig->dev.bus = &iio_bus_type;
+ device_initialize(&trig->dev);
+
+ mutex_init(&trig->pool_lock);
+ trig->subirq_base
+ = irq_alloc_descs(-1, 0,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER,
+ 0);
+ if (trig->subirq_base < 0) {
+ kfree(trig);
+ return NULL;
+ }
+ va_start(vargs, fmt);
+ trig->name = kvasprintf(GFP_KERNEL, fmt, vargs);
+ va_end(vargs);
+ if (trig->name == NULL) {
+ irq_free_descs(trig->subirq_base,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER);
+ kfree(trig);
+ return NULL;
+ }
+ trig->subirq_chip.name = trig->name;
+ trig->subirq_chip.irq_mask = &iio_trig_subirqmask;
+ trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask;
+ for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
+ irq_set_chip(trig->subirq_base + i,
+ &trig->subirq_chip);
+ irq_set_handler(trig->subirq_base + i,
+ &handle_simple_irq);
+ irq_modify_status(trig->subirq_base + i,
+ IRQ_NOREQUEST | IRQ_NOAUTOEN,
+ IRQ_NOPROBE);
+ }
+ get_device(&trig->dev);
+ }
+ return trig;
+}
+EXPORT_SYMBOL(iio_trigger_alloc);
+
+void iio_trigger_free(struct iio_trigger *trig)
+{
+ if (trig)
+ put_device(&trig->dev);
+}
+EXPORT_SYMBOL(iio_trigger_free);
+
+void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
+{
+ indio_dev->groups[indio_dev->groupcounter++] =
+ &iio_trigger_consumer_attr_group;
+}
+
+void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev)
+{
+ /* Clean up an associated but not attached trigger reference */
+ if (indio_dev->trig)
+ iio_trigger_put(indio_dev->trig);
+}
+
+int iio_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+ return iio_trigger_attach_poll_func(indio_dev->trig,
+ indio_dev->pollfunc);
+}
+EXPORT_SYMBOL(iio_triggered_buffer_postenable);
+
+int iio_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+ return iio_trigger_detach_poll_func(indio_dev->trig,
+ indio_dev->pollfunc);
+}
+EXPORT_SYMBOL(iio_triggered_buffer_predisable);
diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/industrialio-triggered-buffer.c
new file mode 100644
index 00000000..46c619b0
--- /dev/null
+++ b/drivers/iio/industrialio-triggered-buffer.c
@@ -0,0 +1,110 @@
+ /*
+ * Copyright (c) 2012 Analog Devices, Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.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/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .preenable = &iio_sw_buffer_preenable,
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+};
+
+/**
+ * iio_triggered_buffer_setup() - Setup triggered buffer and pollfunc
+ * @indio_dev: IIO device structure
+ * @pollfunc_bh: Function which will be used as pollfunc bottom half
+ * @pollfunc_th: Function which will be used as pollfunc top half
+ * @setup_ops: Buffer setup functions to use for this device.
+ * If NULL the default setup functions for triggered
+ * buffers will be used.
+ *
+ * This function combines some common tasks which will normally be performed
+ * when setting up a triggered buffer. It will allocate the buffer and the
+ * pollfunc, as well as register the buffer with the IIO core.
+ *
+ * Before calling this function the indio_dev structure should already be
+ * completely initialized, but not yet registered. In practice this means that
+ * this function should be called right before iio_device_register().
+ *
+ * To free the resources allocated by this function call
+ * iio_triggered_buffer_cleanup().
+ */
+int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
+ irqreturn_t (*pollfunc_bh)(int irq, void *p),
+ irqreturn_t (*pollfunc_th)(int irq, void *p),
+ const struct iio_buffer_setup_ops *setup_ops)
+{
+ int ret;
+
+ indio_dev->buffer = iio_kfifo_allocate(indio_dev);
+ if (!indio_dev->buffer) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
+ pollfunc_th,
+ IRQF_ONESHOT,
+ indio_dev,
+ "%s_consumer%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (indio_dev->pollfunc == NULL) {
+ ret = -ENOMEM;
+ goto error_kfifo_free;
+ }
+
+ /* Ring buffer functions - here trigger setup related */
+ if (setup_ops)
+ indio_dev->setup_ops = setup_ops;
+ else
+ indio_dev->setup_ops = &iio_triggered_buffer_setup_ops;
+
+ /* Flag that polled ring buffering is possible */
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+
+ ret = iio_buffer_register(indio_dev,
+ indio_dev->channels,
+ indio_dev->num_channels);
+ if (ret)
+ goto error_dealloc_pollfunc;
+
+ return 0;
+
+error_dealloc_pollfunc:
+ iio_dealloc_pollfunc(indio_dev->pollfunc);
+error_kfifo_free:
+ iio_kfifo_free(indio_dev->buffer);
+error_ret:
+ return ret;
+}
+EXPORT_SYMBOL(iio_triggered_buffer_setup);
+
+/**
+ * iio_triggered_buffer_cleanup() - Free resources allocated by iio_triggered_buffer_setup()
+ * @indio_dev: IIO device structure
+ */
+void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
+{
+ iio_buffer_unregister(indio_dev);
+ iio_dealloc_pollfunc(indio_dev->pollfunc);
+ iio_kfifo_free(indio_dev->buffer);
+}
+EXPORT_SYMBOL(iio_triggered_buffer_cleanup);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
new file mode 100644
index 00000000..4fc88e61
--- /dev/null
+++ b/drivers/iio/inkern.c
@@ -0,0 +1,580 @@
+/* The industrial I/O core in kernel channel mapping
+ *
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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/err.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include "iio_core.h"
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/consumer.h>
+
+struct iio_map_internal {
+ struct iio_dev *indio_dev;
+ struct iio_map *map;
+ struct list_head l;
+};
+
+static LIST_HEAD(iio_map_list);
+static DEFINE_MUTEX(iio_map_list_lock);
+
+int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
+{
+ int i = 0, ret = 0;
+ struct iio_map_internal *mapi;
+
+ if (maps == NULL)
+ return 0;
+
+ mutex_lock(&iio_map_list_lock);
+ while (maps[i].consumer_dev_name != NULL) {
+ mapi = kzalloc(sizeof(*mapi), GFP_KERNEL);
+ if (mapi == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ mapi->map = &maps[i];
+ mapi->indio_dev = indio_dev;
+ list_add(&mapi->l, &iio_map_list);
+ i++;
+ }
+error_ret:
+ mutex_unlock(&iio_map_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_map_array_register);
+
+
+/*
+ * Remove all map entries associated with the given iio device
+ */
+int iio_map_array_unregister(struct iio_dev *indio_dev)
+{
+ int ret = -ENODEV;
+ struct iio_map_internal *mapi;
+ struct list_head *pos, *tmp;
+
+ mutex_lock(&iio_map_list_lock);
+ 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;
+ }
+ }
+ mutex_unlock(&iio_map_list_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_map_array_unregister);
+
+static const struct iio_chan_spec
+*iio_chan_spec_from_name(const struct iio_dev *indio_dev, const char *name)
+{
+ int i;
+ const struct iio_chan_spec *chan = NULL;
+
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].datasheet_name &&
+ strcmp(name, indio_dev->channels[i].datasheet_name) == 0) {
+ chan = &indio_dev->channels[i];
+ break;
+ }
+ return chan;
+}
+
+#ifdef CONFIG_OF
+
+static int iio_dev_node_match(struct device *dev, void *data)
+{
+ return dev->of_node == data && dev->type == &iio_device_type;
+}
+
+static int __of_iio_channel_get(struct iio_channel *channel,
+ struct device_node *np, int index)
+{
+ struct device *idev;
+ struct iio_dev *indio_dev;
+ int err;
+ struct of_phandle_args iiospec;
+
+ err = of_parse_phandle_with_args(np, "io-channels",
+ "#io-channel-cells",
+ index, &iiospec);
+ if (err)
+ return err;
+
+ idev = bus_find_device(&iio_bus_type, NULL, iiospec.np,
+ iio_dev_node_match);
+ of_node_put(iiospec.np);
+ if (idev == NULL)
+ return -EPROBE_DEFER;
+
+ indio_dev = dev_to_iio_dev(idev);
+ channel->indio_dev = indio_dev;
+ index = iiospec.args_count ? iiospec.args[0] : 0;
+ if (index >= indio_dev->num_channels) {
+ err = -EINVAL;
+ goto err_put;
+ }
+ channel->channel = &indio_dev->channels[index];
+
+ return 0;
+
+err_put:
+ iio_device_put(indio_dev);
+ return err;
+}
+
+static struct iio_channel *of_iio_channel_get(struct device_node *np, int index)
+{
+ struct iio_channel *channel;
+ int err;
+
+ if (index < 0)
+ return ERR_PTR(-EINVAL);
+
+ channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+ if (channel == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ err = __of_iio_channel_get(channel, np, index);
+ if (err)
+ goto err_free_channel;
+
+ return channel;
+
+err_free_channel:
+ kfree(channel);
+ return ERR_PTR(err);
+}
+
+static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
+ const char *name)
+{
+ struct iio_channel *chan = NULL;
+
+ /* Walk up the tree of devices looking for a matching iio channel */
+ while (np) {
+ int index = 0;
+
+ /*
+ * For named iio channels, first look up the name in the
+ * "io-channel-names" property. If it cannot be found, the
+ * index will be an error code, and of_iio_channel_get()
+ * will fail.
+ */
+ if (name)
+ index = of_property_match_string(np, "io-channel-names",
+ name);
+ chan = of_iio_channel_get(np, index);
+ if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER)
+ break;
+ else if (name && index >= 0) {
+ pr_err("ERROR: could not get IIO channel %s:%s(%i)\n",
+ np->full_name, name ? name : "", index);
+ return NULL;
+ }
+
+ /*
+ * No matching IIO channel found on this node.
+ * If the parent node has a "io-channel-ranges" property,
+ * then we can try one of its channels.
+ */
+ np = np->parent;
+ if (np && !of_get_property(np, "io-channel-ranges", NULL))
+ return NULL;
+ }
+
+ return chan;
+}
+
+static struct iio_channel *of_iio_channel_get_all(struct device *dev)
+{
+ struct iio_channel *chans;
+ int i, mapind, nummaps = 0;
+ int ret;
+
+ do {
+ ret = of_parse_phandle_with_args(dev->of_node,
+ "io-channels",
+ "#io-channel-cells",
+ nummaps, NULL);
+ if (ret < 0)
+ break;
+ } while (++nummaps);
+
+ if (nummaps == 0) /* no error, return NULL to search map table */
+ return NULL;
+
+ /* NULL terminated array to save passing size */
+ chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
+ if (chans == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ /* Search for OF matches */
+ for (mapind = 0; mapind < nummaps; mapind++) {
+ ret = __of_iio_channel_get(&chans[mapind], dev->of_node,
+ mapind);
+ if (ret)
+ goto error_free_chans;
+ }
+ return chans;
+
+error_free_chans:
+ for (i = 0; i < mapind; i++)
+ iio_device_put(chans[i].indio_dev);
+ kfree(chans);
+ return ERR_PTR(ret);
+}
+
+#else /* CONFIG_OF */
+
+static inline struct iio_channel *
+of_iio_channel_get_by_name(struct device_node *np, const char *name)
+{
+ return NULL;
+}
+
+static inline struct iio_channel *of_iio_channel_get_all(struct device *dev)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_OF */
+
+static struct iio_channel *iio_channel_get_sys(const char *name,
+ const char *channel_name)
+{
+ struct iio_map_internal *c_i = NULL, *c = NULL;
+ struct iio_channel *channel;
+ int err;
+
+ if (name == NULL && channel_name == NULL)
+ return ERR_PTR(-ENODEV);
+
+ /* first find matching entry the channel map */
+ mutex_lock(&iio_map_list_lock);
+ list_for_each_entry(c_i, &iio_map_list, l) {
+ if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
+ (channel_name &&
+ strcmp(channel_name, c_i->map->consumer_channel) != 0))
+ continue;
+ c = c_i;
+ iio_device_get(c->indio_dev);
+ break;
+ }
+ mutex_unlock(&iio_map_list_lock);
+ if (c == NULL)
+ return ERR_PTR(-ENODEV);
+
+ channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+ if (channel == NULL) {
+ err = -ENOMEM;
+ goto error_no_mem;
+ }
+
+ channel->indio_dev = c->indio_dev;
+
+ if (c->map->adc_channel_label) {
+ channel->channel =
+ iio_chan_spec_from_name(channel->indio_dev,
+ c->map->adc_channel_label);
+
+ if (channel->channel == NULL) {
+ err = -EINVAL;
+ goto error_no_chan;
+ }
+ }
+
+ return channel;
+
+error_no_chan:
+ kfree(channel);
+error_no_mem:
+ iio_device_put(c->indio_dev);
+ return ERR_PTR(err);
+}
+
+struct iio_channel *iio_channel_get(struct device *dev,
+ const char *channel_name)
+{
+ const char *name = dev ? dev_name(dev) : NULL;
+ struct iio_channel *channel;
+
+ if (dev) {
+ channel = of_iio_channel_get_by_name(dev->of_node,
+ channel_name);
+ if (channel != NULL)
+ return channel;
+ }
+
+ return iio_channel_get_sys(name, channel_name);
+}
+EXPORT_SYMBOL_GPL(iio_channel_get);
+
+void iio_channel_release(struct iio_channel *channel)
+{
+ iio_device_put(channel->indio_dev);
+ kfree(channel);
+}
+EXPORT_SYMBOL_GPL(iio_channel_release);
+
+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 (dev == NULL)
+ return ERR_PTR(-EINVAL);
+
+ chans = of_iio_channel_get_all(dev);
+ if (chans)
+ return chans;
+
+ name = dev_name(dev);
+
+ mutex_lock(&iio_map_list_lock);
+ /* first count the matching maps */
+ list_for_each_entry(c, &iio_map_list, l)
+ if (name && strcmp(name, c->map->consumer_dev_name) != 0)
+ continue;
+ else
+ nummaps++;
+
+ if (nummaps == 0) {
+ ret = -ENODEV;
+ goto error_ret;
+ }
+
+ /* NULL terminated array to save passing size */
+ chans = kzalloc(sizeof(*chans)*(nummaps + 1), GFP_KERNEL);
+ if (chans == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ /* for each map fill in the chans element */
+ list_for_each_entry(c, &iio_map_list, l) {
+ if (name && strcmp(name, c->map->consumer_dev_name) != 0)
+ continue;
+ chans[mapind].indio_dev = c->indio_dev;
+ chans[mapind].data = c->map->consumer_data;
+ chans[mapind].channel =
+ iio_chan_spec_from_name(chans[mapind].indio_dev,
+ c->map->adc_channel_label);
+ if (chans[mapind].channel == NULL) {
+ ret = -EINVAL;
+ goto error_free_chans;
+ }
+ iio_device_get(chans[mapind].indio_dev);
+ mapind++;
+ }
+ if (mapind == 0) {
+ ret = -ENODEV;
+ goto error_free_chans;
+ }
+ mutex_unlock(&iio_map_list_lock);
+
+ return chans;
+
+error_free_chans:
+ for (i = 0; i < nummaps; i++)
+ iio_device_put(chans[i].indio_dev);
+ kfree(chans);
+error_ret:
+ mutex_unlock(&iio_map_list_lock);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(iio_channel_get_all);
+
+void iio_channel_release_all(struct iio_channel *channels)
+{
+ struct iio_channel *chan = &channels[0];
+
+ while (chan->indio_dev) {
+ iio_device_put(chan->indio_dev);
+ chan++;
+ }
+ kfree(channels);
+}
+EXPORT_SYMBOL_GPL(iio_channel_release_all);
+
+static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
+ enum iio_chan_info_enum info)
+{
+ int unused;
+
+ if (val2 == NULL)
+ val2 = &unused;
+
+ return chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
+ val, val2, info);
+}
+
+int iio_read_channel_raw(struct iio_channel *chan, int *val)
+{
+ int ret;
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_raw);
+
+static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
+ int raw, int *processed, unsigned int scale)
+{
+ int scale_type, scale_val, scale_val2, offset;
+ s64 raw64 = raw;
+ int ret;
+
+ ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_OFFSET);
+ if (ret >= 0)
+ raw64 += offset;
+
+ scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
+ IIO_CHAN_INFO_SCALE);
+ if (scale_type < 0)
+ return scale_type;
+
+ switch (scale_type) {
+ case IIO_VAL_INT:
+ *processed = raw64 * scale_val;
+ break;
+ case IIO_VAL_INT_PLUS_MICRO:
+ if (scale_val2 < 0)
+ *processed = -raw64 * scale_val;
+ else
+ *processed = raw64 * scale_val;
+ *processed += div_s64(raw64 * (s64)scale_val2 * scale,
+ 1000000LL);
+ break;
+ case IIO_VAL_INT_PLUS_NANO:
+ if (scale_val2 < 0)
+ *processed = -raw64 * scale_val;
+ else
+ *processed = raw64 * scale_val;
+ *processed += div_s64(raw64 * (s64)scale_val2 * scale,
+ 1000000000LL);
+ break;
+ case IIO_VAL_FRACTIONAL:
+ *processed = div_s64(raw64 * (s64)scale_val * scale,
+ scale_val2);
+ break;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ *processed = (raw64 * (s64)scale_val * scale) >> scale_val2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
+ int *processed, unsigned int scale)
+{
+ int ret;
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed,
+ scale);
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
+
+int iio_read_channel_processed(struct iio_channel *chan, int *val)
+{
+ int ret;
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) {
+ ret = iio_channel_read(chan, val, NULL,
+ IIO_CHAN_INFO_PROCESSED);
+ } else {
+ ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
+ if (ret < 0)
+ goto err_unlock;
+ ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 1);
+ }
+
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_processed);
+
+int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
+{
+ int ret;
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_scale);
+
+int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
+{
+ int ret = 0;
+ /* Need to verify underlying driver has not gone away */
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ *type = chan->channel->type;
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_get_channel_type);
diff --git a/drivers/iio/inv_test/Kconfig b/drivers/iio/inv_test/Kconfig
new file mode 100644
index 00000000..f6b6e159
--- /dev/null
+++ b/drivers/iio/inv_test/Kconfig
@@ -0,0 +1,11 @@
+#
+# Kconfig for Invensense IIO testing hooks
+#
+
+config INV_TESTING
+ boolean "Invensense IIO testing hooks"
+ depends on INV_MPU_IIO || INV_AMI306_IIO || INV_YAS530
+ default n
+ help
+ This flag enables display of additional testing information from the
+ Invensense IIO drivers
diff --git a/drivers/iio/inv_test/Makefile b/drivers/iio/inv_test/Makefile
new file mode 100644
index 00000000..4f0edd3d
--- /dev/null
+++ b/drivers/iio/inv_test/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Invensense IIO testing hooks.
+#
+
+obj-$(CONFIG_INV_TESTING) += inv_counters.o
+
diff --git a/drivers/iio/inv_test/inv_counters.c b/drivers/iio/inv_test/inv_counters.c
new file mode 100644
index 00000000..929227aa
--- /dev/null
+++ b/drivers/iio/inv_test/inv_counters.c
@@ -0,0 +1,153 @@
+/*
+ * @file inv_counters.c
+ * @brief Exports i2c read write counts through sysfs
+ *
+ * @version 0.1
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/kdev_t.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+#include <linux/kernel_stat.h>
+
+#include "inv_counters.h"
+
+static int mpu_irq;
+static int accel_irq;
+static int compass_irq;
+
+struct inv_counters {
+ uint32_t i2c_tempreads;
+ uint32_t i2c_mpureads;
+ uint32_t i2c_mpuwrites;
+ uint32_t i2c_accelreads;
+ uint32_t i2c_accelwrites;
+ uint32_t i2c_compassreads;
+ uint32_t i2c_compasswrites;
+ uint32_t i2c_compassirq;
+ uint32_t i2c_accelirq;
+};
+
+static struct inv_counters Counters;
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq)
+{
+ switch (type) {
+ case MPU:
+ mpu_irq = irq;
+ break;
+ case ACCEL:
+ accel_irq = irq;
+ break;
+ case COMPASS:
+ compass_irq = irq;
+ break;
+ }
+}
+
+static ssize_t i2c_counters_show(struct class *cls,
+ struct class_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "%ld.%03ld %u %u %u %u %u %u %u %u %u %u\n",
+ jiffies / HZ, ((jiffies % HZ) * (1024 / HZ)),
+ mpu_irq ? kstat_irqs(mpu_irq) : 0,
+ Counters.i2c_tempreads,
+ Counters.i2c_mpureads, Counters.i2c_mpuwrites,
+ accel_irq ? kstat_irqs(accel_irq) : Counters.i2c_accelirq,
+ Counters.i2c_accelreads, Counters.i2c_accelwrites,
+ compass_irq ? kstat_irqs(compass_irq) : Counters.i2c_compassirq,
+ Counters.i2c_compassreads, Counters.i2c_compasswrites);
+}
+
+void inv_iio_counters_tempread(int count)
+{
+ Counters.i2c_tempreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_tempread);
+
+void inv_iio_counters_mpuread(int count)
+{
+ Counters.i2c_mpureads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuread);
+
+void inv_iio_counters_mpuwrite(int count)
+{
+ Counters.i2c_mpuwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuwrite);
+
+void inv_iio_counters_accelread(int count)
+{
+ Counters.i2c_accelreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelread);
+
+void inv_iio_counters_accelwrite(int count)
+{
+ Counters.i2c_accelwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelwrite);
+
+void inv_iio_counters_compassread(int count)
+{
+ Counters.i2c_compassreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassread);
+
+void inv_iio_counters_compasswrite(int count)
+{
+ Counters.i2c_compasswrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compasswrite);
+
+void inv_iio_counters_compassirq(void)
+{
+ Counters.i2c_compassirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassirq);
+
+void inv_iio_counters_accelirq(void)
+{
+ Counters.i2c_accelirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelirq);
+
+static struct class_attribute inv_class_attr[] = {
+ __ATTR(i2c_counter, S_IRUGO, i2c_counters_show, NULL),
+ __ATTR_NULL
+};
+
+static struct class inv_counters_class = {
+ .name = "inv_counters",
+ .owner = THIS_MODULE,
+ .class_attrs = (struct class_attribute *) &inv_class_attr
+};
+
+static int __init inv_counters_init(void)
+{
+ memset(&Counters, 0, sizeof(Counters));
+
+ return class_register(&inv_counters_class);
+}
+
+static void __exit inv_counters_exit(void)
+{
+ class_unregister(&inv_counters_class);
+}
+
+module_init(inv_counters_init);
+module_exit(inv_counters_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("GESL");
+MODULE_DESCRIPTION("inv_counters debug support");
+
diff --git a/drivers/iio/inv_test/inv_counters.h b/drivers/iio/inv_test/inv_counters.h
new file mode 100644
index 00000000..35a802b0
--- /dev/null
+++ b/drivers/iio/inv_test/inv_counters.h
@@ -0,0 +1,72 @@
+/*
+ * @file inv_counters.h
+ * @brief Debug file to keep track of various counters for the InvenSense
+ * sensor drivers.
+ *
+ * @version 0.1
+ */
+
+#ifndef _INV_COUNTERS_H_
+#define _INV_COUNTERS_H_
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+
+enum irqtype {
+ MPU,
+ ACCEL,
+ COMPASS
+};
+
+#ifdef CONFIG_INV_TESTING
+
+#define INV_I2C_INC_MPUREAD(x) inv_iio_counters_mpuread(x)
+#define INV_I2C_INC_MPUWRITE(x) inv_iio_counters_mpuwrite(x)
+#define INV_I2C_INC_ACCELREAD(x) inv_iio_counters_accelread(x)
+#define INV_I2C_INC_ACCELWRITE(x) inv_iio_counters_accelwrite(x)
+#define INV_I2C_INC_COMPASSREAD(x) inv_iio_counters_compassread(x)
+#define INV_I2C_INC_COMPASSWRITE(x) inv_iio_counters_compasswrite(x)
+
+#define INV_I2C_INC_TEMPREAD(x) inv_iio_counters_tempread(x)
+
+#define INV_I2C_SETIRQ(type, irq) inv_iio_counters_set_i2cirq(type, irq)
+#define INV_I2C_INC_COMPASSIRQ() inv_iio_counters_compassirq()
+#define INV_I2C_INC_ACCELIRQ() inv_iio_counters_accelirq()
+
+void inv_iio_counters_mpuread(int count);
+void inv_iio_counters_mpuwrite(int count);
+void inv_iio_counters_accelread(int count);
+void inv_iio_counters_accelwrite(int count);
+void inv_iio_counters_compassread(int count);
+void inv_iio_counters_compasswrite(int count);
+
+void inv_iio_counters_tempread(int count);
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq);
+void inv_iio_counters_compassirq(void);
+void inv_iio_counters_accelirq(void);
+
+#else
+
+#define INV_I2C_INC_MPUREAD(x)
+#define INV_I2C_INC_MPUWRITE(x)
+#define INV_I2C_INC_ACCELREAD(x)
+#define INV_I2C_INC_ACCELWRITE(x)
+#define INV_I2C_INC_COMPASSREAD(x)
+#define INV_I2C_INC_COMPASSWRITE(x)
+
+#define INV_I2C_INC_TEMPREAD(x)
+
+#define INV_I2C_SETIRQ(type, irq)
+#define INV_I2C_INC_COMPASSIRQ()
+#define INV_I2C_INC_ACCELIRQ()
+
+#endif /* CONFIG_INV_TESTING */
+
+#endif /* _INV_COUNTERS_H_ */
+
diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c
new file mode 100644
index 00000000..a923c78d
--- /dev/null
+++ b/drivers/iio/kfifo_buf.c
@@ -0,0 +1,164 @@
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/kfifo.h>
+#include <linux/mutex.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/sched.h>
+
+struct iio_kfifo {
+ struct iio_buffer buffer;
+ struct kfifo kf;
+ int update_needed;
+};
+
+#define iio_to_kfifo(r) container_of(r, struct iio_kfifo, buffer)
+
+static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
+ int bytes_per_datum, int length)
+{
+ if ((length == 0) || (bytes_per_datum == 0))
+ return -EINVAL;
+
+ return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
+ bytes_per_datum, GFP_KERNEL);
+}
+
+static int iio_request_update_kfifo(struct iio_buffer *r)
+{
+ int ret = 0;
+ struct iio_kfifo *buf = iio_to_kfifo(r);
+
+ if (!buf->update_needed)
+ goto error_ret;
+ kfifo_free(&buf->kf);
+ ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
+ buf->buffer.length);
+ r->stufftoread = false;
+error_ret:
+ return ret;
+}
+
+static int iio_get_length_kfifo(struct iio_buffer *r)
+{
+ return r->length;
+}
+
+static IIO_BUFFER_ENABLE_ATTR;
+static IIO_BUFFER_LENGTH_ATTR;
+
+static struct attribute *iio_kfifo_attributes[] = {
+ &dev_attr_length.attr,
+ &dev_attr_enable.attr,
+ NULL,
+};
+
+static struct attribute_group iio_kfifo_attribute_group = {
+ .attrs = iio_kfifo_attributes,
+ .name = "buffer",
+};
+
+static int iio_get_bytes_per_datum_kfifo(struct iio_buffer *r)
+{
+ return r->bytes_per_datum;
+}
+
+static int iio_mark_update_needed_kfifo(struct iio_buffer *r)
+{
+ struct iio_kfifo *kf = iio_to_kfifo(r);
+ kf->update_needed = true;
+ return 0;
+}
+
+static int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd)
+{
+ if (r->bytes_per_datum != bpd) {
+ r->bytes_per_datum = bpd;
+ iio_mark_update_needed_kfifo(r);
+ }
+ return 0;
+}
+
+static int iio_set_length_kfifo(struct iio_buffer *r, int length)
+{
+ /* Avoid an invalid state */
+ if (length < 2)
+ length = 2;
+ if (r->length != length) {
+ r->length = length;
+ iio_mark_update_needed_kfifo(r);
+ }
+ return 0;
+}
+
+static int iio_store_to_kfifo(struct iio_buffer *r,
+ u8 *data)
+{
+ int ret;
+ struct iio_kfifo *kf = iio_to_kfifo(r);
+ ret = kfifo_in(&kf->kf, data, 1);
+ if (ret != 1)
+ return -EBUSY;
+ r->stufftoread = true;
+ wake_up_interruptible(&r->pollq);
+
+ return 0;
+}
+
+static int iio_read_first_n_kfifo(struct iio_buffer *r,
+ size_t n, char __user *buf)
+{
+ int ret, copied;
+ struct iio_kfifo *kf = iio_to_kfifo(r);
+
+ if (n < r->bytes_per_datum || r->bytes_per_datum == 0)
+ return -EINVAL;
+
+ ret = kfifo_to_user(&kf->kf, buf, n, &copied);
+ if (ret < 0)
+ return ret;
+
+ if (kfifo_is_empty(&kf->kf))
+ r->stufftoread = false;
+ /* verify it is still empty to avoid race */
+ if (!kfifo_is_empty(&kf->kf))
+ r->stufftoread = true;
+
+ return copied;
+}
+
+static const struct iio_buffer_access_funcs kfifo_access_funcs = {
+ .store_to = &iio_store_to_kfifo,
+ .read_first_n = &iio_read_first_n_kfifo,
+ .request_update = &iio_request_update_kfifo,
+ .get_bytes_per_datum = &iio_get_bytes_per_datum_kfifo,
+ .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
+ .get_length = &iio_get_length_kfifo,
+ .set_length = &iio_set_length_kfifo,
+};
+
+struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
+{
+ struct iio_kfifo *kf;
+
+ kf = kzalloc(sizeof *kf, GFP_KERNEL);
+ if (!kf)
+ return NULL;
+ kf->update_needed = true;
+ iio_buffer_init(&kf->buffer);
+ kf->buffer.attrs = &iio_kfifo_attribute_group;
+ kf->buffer.access = &kfifo_access_funcs;
+ kf->buffer.length = 2;
+ return &kf->buffer;
+}
+EXPORT_SYMBOL(iio_kfifo_allocate);
+
+void iio_kfifo_free(struct iio_buffer *r)
+{
+ kfree(iio_to_kfifo(r));
+}
+EXPORT_SYMBOL(iio_kfifo_free);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
new file mode 100644
index 00000000..5ef1a396
--- /dev/null
+++ b/drivers/iio/light/Kconfig
@@ -0,0 +1,66 @@
+#
+# Light sensors
+#
+menu "Light sensors"
+
+config ADJD_S311
+ tristate "ADJD-S311-CR999 digital color sensor"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ depends on I2C
+ help
+ If you say yes here you get support for the Avago ADJD-S311-CR999
+ digital color light sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called adjd_s311.
+
+config SENSORS_LM3533
+ tristate "LM3533 ambient light sensor"
+ depends on MFD_LM3533
+ help
+ If you say yes here you get support for the ambient light sensor
+ interface on National Semiconductor / TI LM3533 Lighting Power
+ chips.
+
+ The sensor interface can be used to control the LEDs and backlights
+ of the chip through defining five light zones and three sets of
+ corresponding output-current values.
+
+ The driver provides raw and mean adc readings along with the current
+ light zone through sysfs. A threshold event can be generated on zone
+ changes. The ALS-control output values can be set per zone for the
+ three current output channels.
+
+config SENSORS_TSL2563
+ tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"
+ depends on I2C
+ help
+ If you say yes here you get support for the Taos TSL2560,
+ TSL2561, TSL2562 and TSL2563 ambient light sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called tsl2563.
+
+config VCNL4000
+ tristate "VCNL4000 combined ALS and proximity sensor"
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Vishay VCNL4000
+ combined ambient light and proximity sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vcnl4000.
+
+config HID_SENSOR_ALS
+ depends on HID_SENSOR_HUB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select HID_SENSOR_IIO_COMMON
+ select HID_SENSOR_IIO_TRIGGER
+ tristate "HID ALS"
+ help
+ Say yes here to build support for the HID SENSOR
+ Ambient light sensor.
+
+endmenu
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
new file mode 100644
index 00000000..040d9c75
--- /dev/null
+++ b/drivers/iio/light/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for IIO Light sensors
+#
+
+obj-$(CONFIG_ADJD_S311) += adjd_s311.o
+obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
+obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
+obj-$(CONFIG_VCNL4000) += vcnl4000.o
+obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c
new file mode 100644
index 00000000..5f4749e6
--- /dev/null
+++ b/drivers/iio/light/adjd_s311.c
@@ -0,0 +1,364 @@
+/*
+ * adjd_s311.c - Support for ADJD-S311-CR999 digital color sensor
+ *
+ * Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * driver for ADJD-S311-CR999 digital color sensor (10-bit channels for
+ * red, green, blue, clear); 7-bit I2C slave address 0x74
+ *
+ * limitations: no calibration, no offset mode, no sleep mode
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/bitmap.h>
+#include <linux/err.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/triggered_buffer.h>
+
+#define ADJD_S311_DRV_NAME "adjd_s311"
+
+#define ADJD_S311_CTRL 0x00
+#define ADJD_S311_CONFIG 0x01
+#define ADJD_S311_CAP_RED 0x06
+#define ADJD_S311_CAP_GREEN 0x07
+#define ADJD_S311_CAP_BLUE 0x08
+#define ADJD_S311_CAP_CLEAR 0x09
+#define ADJD_S311_INT_RED_LO 0x0a
+#define ADJD_S311_INT_RED_HI 0x0b
+#define ADJD_S311_INT_GREEN_LO 0x0c
+#define ADJD_S311_INT_GREEN_HI 0x0d
+#define ADJD_S311_INT_BLUE_LO 0x0e
+#define ADJD_S311_INT_BLUE_HI 0x0f
+#define ADJD_S311_INT_CLEAR_LO 0x10
+#define ADJD_S311_INT_CLEAR_HI 0x11
+#define ADJD_S311_DATA_RED_LO 0x40
+#define ADJD_S311_DATA_RED_HI 0x41
+#define ADJD_S311_DATA_GREEN_LO 0x42
+#define ADJD_S311_DATA_GREEN_HI 0x43
+#define ADJD_S311_DATA_BLUE_LO 0x44
+#define ADJD_S311_DATA_BLUE_HI 0x45
+#define ADJD_S311_DATA_CLEAR_LO 0x46
+#define ADJD_S311_DATA_CLEAR_HI 0x47
+#define ADJD_S311_OFFSET_RED 0x48
+#define ADJD_S311_OFFSET_GREEN 0x49
+#define ADJD_S311_OFFSET_BLUE 0x4a
+#define ADJD_S311_OFFSET_CLEAR 0x4b
+
+#define ADJD_S311_CTRL_GOFS 0x02
+#define ADJD_S311_CTRL_GSSR 0x01
+#define ADJD_S311_CAP_MASK 0x0f
+#define ADJD_S311_INT_MASK 0x0fff
+#define ADJD_S311_DATA_MASK 0x03ff
+
+struct adjd_s311_data {
+ struct i2c_client *client;
+ u16 *buffer;
+};
+
+enum adjd_s311_channel_idx {
+ IDX_RED, IDX_GREEN, IDX_BLUE, IDX_CLEAR
+};
+
+#define ADJD_S311_DATA_REG(chan) (ADJD_S311_DATA_RED_LO + (chan) * 2)
+#define ADJD_S311_INT_REG(chan) (ADJD_S311_INT_RED_LO + (chan) * 2)
+#define ADJD_S311_CAP_REG(chan) (ADJD_S311_CAP_RED + (chan))
+
+static int adjd_s311_req_data(struct iio_dev *indio_dev)
+{
+ struct adjd_s311_data *data = iio_priv(indio_dev);
+ int tries = 10;
+
+ int ret = i2c_smbus_write_byte_data(data->client, ADJD_S311_CTRL,
+ ADJD_S311_CTRL_GSSR);
+ if (ret < 0)
+ return ret;
+
+ while (tries--) {
+ ret = i2c_smbus_read_byte_data(data->client, ADJD_S311_CTRL);
+ if (ret < 0)
+ return ret;
+ if (!(ret & ADJD_S311_CTRL_GSSR))
+ break;
+ msleep(20);
+ }
+
+ if (tries < 0) {
+ dev_err(&data->client->dev,
+ "adjd_s311_req_data() failed, data not ready\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int adjd_s311_read_data(struct iio_dev *indio_dev, u8 reg, int *val)
+{
+ struct adjd_s311_data *data = iio_priv(indio_dev);
+
+ int ret = adjd_s311_req_data(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(data->client, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret & ADJD_S311_DATA_MASK;
+
+ return 0;
+}
+
+static ssize_t adjd_s311_read_int_time(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct adjd_s311_data *data = iio_priv(indio_dev);
+ s32 ret;
+
+ ret = i2c_smbus_read_word_data(data->client,
+ ADJD_S311_INT_REG(chan->address));
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", ret & ADJD_S311_INT_MASK);
+}
+
+static ssize_t adjd_s311_write_int_time(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ struct adjd_s311_data *data = iio_priv(indio_dev);
+ unsigned long int_time;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &int_time);
+ if (ret)
+ return ret;
+
+ if (int_time > ADJD_S311_INT_MASK)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_word_data(data->client,
+ ADJD_S311_INT_REG(chan->address), int_time);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adjd_s311_data *data = iio_priv(indio_dev);
+ s64 time_ns = iio_get_time_ns();
+ int len = 0;
+ int i, j = 0;
+
+ int ret = adjd_s311_req_data(indio_dev);
+ if (ret < 0)
+ goto done;
+
+ for_each_set_bit(i, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ ret = i2c_smbus_read_word_data(data->client,
+ ADJD_S311_DATA_REG(i));
+ if (ret < 0)
+ goto done;
+
+ data->buffer[j++] = ret & ADJD_S311_DATA_MASK;
+ len += 2;
+ }
+
+ if (indio_dev->scan_timestamp)
+ *(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64)))
+ = time_ns;
+ iio_push_to_buffers(indio_dev, (u8 *)data->buffer);
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_chan_spec_ext_info adjd_s311_ext_info[] = {
+ {
+ .name = "integration_time",
+ .read = adjd_s311_read_int_time,
+ .write = adjd_s311_write_int_time,
+ },
+ { }
+};
+
+#define ADJD_S311_CHANNEL(_color, _scan_idx) { \
+ .type = IIO_INTENSITY, \
+ .modified = 1, \
+ .address = (IDX_##_color), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+ .channel2 = (IIO_MOD_LIGHT_##_color), \
+ .scan_index = (_scan_idx), \
+ .scan_type = IIO_ST('u', 10, 16, 0), \
+ .ext_info = adjd_s311_ext_info, \
+}
+
+static const struct iio_chan_spec adjd_s311_channels[] = {
+ ADJD_S311_CHANNEL(RED, 0),
+ ADJD_S311_CHANNEL(GREEN, 1),
+ ADJD_S311_CHANNEL(BLUE, 2),
+ ADJD_S311_CHANNEL(CLEAR, 3),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static int adjd_s311_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct adjd_s311_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = adjd_s311_read_data(indio_dev, chan->address, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ ret = i2c_smbus_read_byte_data(data->client,
+ ADJD_S311_CAP_REG(chan->address));
+ if (ret < 0)
+ return ret;
+ *val = ret & ADJD_S311_CAP_MASK;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int adjd_s311_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct adjd_s311_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ if (val < 0 || val > ADJD_S311_CAP_MASK)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ ADJD_S311_CAP_REG(chan->address), val);
+ return ret;
+ }
+ return -EINVAL;
+}
+
+static int adjd_s311_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct adjd_s311_data *data = iio_priv(indio_dev);
+
+ kfree(data->buffer);
+ data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (data->buffer == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const struct iio_info adjd_s311_info = {
+ .read_raw = adjd_s311_read_raw,
+ .write_raw = adjd_s311_write_raw,
+ .update_scan_mode = adjd_s311_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static int adjd_s311_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adjd_s311_data *data;
+ struct iio_dev *indio_dev;
+ int err;
+
+ indio_dev = iio_device_alloc(sizeof(*data));
+ if (indio_dev == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &adjd_s311_info;
+ indio_dev->name = ADJD_S311_DRV_NAME;
+ indio_dev->channels = adjd_s311_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adjd_s311_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ err = iio_triggered_buffer_setup(indio_dev, NULL,
+ adjd_s311_trigger_handler, NULL);
+ if (err < 0)
+ goto exit_free_device;
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto exit_unreg_buffer;
+
+ dev_info(&client->dev, "ADJD-S311 color sensor registered\n");
+
+ return 0;
+
+exit_unreg_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+exit_free_device:
+ iio_device_free(indio_dev);
+exit:
+ return err;
+}
+
+static int adjd_s311_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct adjd_s311_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ kfree(data->buffer);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id adjd_s311_id[] = {
+ { "adjd_s311", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adjd_s311_id);
+
+static struct i2c_driver adjd_s311_driver = {
+ .driver = {
+ .name = ADJD_S311_DRV_NAME,
+ },
+ .probe = adjd_s311_probe,
+ .remove = adjd_s311_remove,
+ .id_table = adjd_s311_id,
+};
+module_i2c_driver(adjd_s311_driver);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("ADJD-S311 color sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
new file mode 100644
index 00000000..80d68ff0
--- /dev/null
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -0,0 +1,370 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Accelerometer-3D: 0x200041*/
+#define DRIVER_NAME "HID-SENSOR-200041"
+
+#define CHANNEL_SCAN_INDEX_ILLUM 0
+
+struct als_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_common common_attributes;
+ struct hid_sensor_hub_attribute_info als_illum;
+ u32 illum;
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec als_channels[] = {
+ {
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_HYSTERESIS),
+ .scan_index = CHANNEL_SCAN_INDEX_ILLUM,
+ }
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void als_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+ int channel, int size)
+{
+ channels[channel].scan_type.sign = 's';
+ /* Real storage bits will change based on the report desc. */
+ channels[channel].scan_type.realbits = size * 8;
+ /* Maximum size of a sample to capture is u32 */
+ channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int als_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct als_state *als_state = iio_priv(indio_dev);
+ int report_id = -1;
+ u32 address;
+ int ret;
+ int ret_type;
+
+ *val = 0;
+ *val2 = 0;
+ switch (mask) {
+ case 0:
+ switch (chan->scan_index) {
+ case CHANNEL_SCAN_INDEX_ILLUM:
+ report_id = als_state->als_illum.report_id;
+ address =
+ HID_USAGE_SENSOR_LIGHT_ILLUM;
+ break;
+ default:
+ report_id = -1;
+ break;
+ }
+ if (report_id >= 0)
+ *val = sensor_hub_input_attr_get_raw_value(
+ als_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_ALS, address,
+ report_id);
+ else {
+ *val = 0;
+ return -EINVAL;
+ }
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = als_state->als_illum.units;
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = hid_sensor_convert_exponent(
+ als_state->als_illum.unit_expo);
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_read_samp_freq_value(
+ &als_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_read_raw_hyst_value(
+ &als_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret_type = -EINVAL;
+ break;
+ }
+
+ return ret_type;
+}
+
+/* Channel write_raw handler */
+static int als_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct als_state *als_state = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_write_samp_freq_value(
+ &als_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_write_raw_hyst_value(
+ &als_state->common_attributes, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int als_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info als_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &als_read_raw,
+ .write_raw = &als_write_raw,
+ .write_raw_get_fmt = &als_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ iio_push_to_buffers(indio_dev, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int als_proc_event(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct als_state *als_state = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "als_proc_event [%d]\n",
+ als_state->common_attributes.data_ready);
+ if (als_state->common_attributes.data_ready)
+ hid_sensor_push_data(indio_dev,
+ (u8 *)&als_state->illum,
+ sizeof(als_state->illum));
+
+ return 0;
+}
+
+/* Capture samples in local storage */
+static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct als_state *als_state = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_LIGHT_ILLUM:
+ als_state->illum = *(u32 *)raw_data;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int als_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ unsigned usage_id,
+ struct als_state *st)
+{
+ int ret;
+
+ ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
+ usage_id,
+ HID_USAGE_SENSOR_LIGHT_ILLUM,
+ &st->als_illum);
+ if (ret < 0)
+ return ret;
+ als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM,
+ st->als_illum.size);
+
+ dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index,
+ st->als_illum.report_id);
+
+ return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int hid_als_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ static const char *name = "als";
+ struct iio_dev *indio_dev;
+ struct als_state *als_state;
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_chan_spec *channels;
+
+ indio_dev = iio_device_alloc(sizeof(struct als_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+
+ als_state = iio_priv(indio_dev);
+ als_state->common_attributes.hsdev = hsdev;
+ als_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS,
+ &als_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes\n");
+ goto error_free_dev;
+ }
+
+ channels = kmemdup(als_channels, sizeof(als_channels), GFP_KERNEL);
+ if (!channels) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "failed to duplicate channels\n");
+ goto error_free_dev;
+ }
+
+ ret = als_parse_report(pdev, hsdev, channels,
+ HID_USAGE_SENSOR_ALS, als_state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup attributes\n");
+ goto error_free_dev_mem;
+ }
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels =
+ ARRAY_SIZE(als_channels);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &als_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ NULL, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+ goto error_free_dev_mem;
+ }
+ als_state->common_attributes.data_ready = false;
+ ret = hid_sensor_setup_trigger(indio_dev, name,
+ &als_state->common_attributes);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "trigger setup failed\n");
+ goto error_unreg_buffer_funcs;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_trigger;
+ }
+
+ als_state->callbacks.send_event = als_proc_event;
+ als_state->callbacks.capture_sample = als_capture_sample;
+ als_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ALS,
+ &als_state->callbacks);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "callback reg failed\n");
+ goto error_iio_unreg;
+ }
+
+ return ret;
+
+error_iio_unreg:
+ iio_device_unregister(indio_dev);
+error_remove_trigger:
+ hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+ kfree(indio_dev->channels);
+error_free_dev:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int hid_als_remove(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);
+ iio_device_unregister(indio_dev);
+ hid_sensor_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ kfree(indio_dev->channels);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver hid_als_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = hid_als_probe,
+ .remove = hid_als_remove,
+};
+module_platform_driver(hid_als_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor ALS");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
new file mode 100644
index 00000000..5fa31a4e
--- /dev/null
+++ b/drivers/iio/light/lm3533-als.c
@@ -0,0 +1,931 @@
+/*
+ * lm3533-als.c -- LM3533 Ambient Light Sensor driver
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/atomic.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_ALS_RESISTOR_MIN 1
+#define LM3533_ALS_RESISTOR_MAX 127
+#define LM3533_ALS_CHANNEL_CURRENT_MAX 2
+#define LM3533_ALS_THRESH_MAX 3
+#define LM3533_ALS_ZONE_MAX 4
+
+#define LM3533_REG_ALS_RESISTOR_SELECT 0x30
+#define LM3533_REG_ALS_CONF 0x31
+#define LM3533_REG_ALS_ZONE_INFO 0x34
+#define LM3533_REG_ALS_READ_ADC_RAW 0x37
+#define LM3533_REG_ALS_READ_ADC_AVERAGE 0x38
+#define LM3533_REG_ALS_BOUNDARY_BASE 0x50
+#define LM3533_REG_ALS_TARGET_BASE 0x60
+
+#define LM3533_ALS_ENABLE_MASK 0x01
+#define LM3533_ALS_INPUT_MODE_MASK 0x02
+#define LM3533_ALS_INT_ENABLE_MASK 0x01
+
+#define LM3533_ALS_ZONE_SHIFT 2
+#define LM3533_ALS_ZONE_MASK 0x1c
+
+#define LM3533_ALS_FLAG_INT_ENABLED 1
+
+
+struct lm3533_als {
+ struct lm3533 *lm3533;
+ struct platform_device *pdev;
+
+ unsigned long flags;
+ int irq;
+
+ atomic_t zone;
+ struct mutex thresh_mutex;
+};
+
+
+static int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average,
+ int *adc)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 reg;
+ u8 val;
+ int ret;
+
+ if (average)
+ reg = LM3533_REG_ALS_READ_ADC_AVERAGE;
+ else
+ reg = LM3533_REG_ALS_READ_ADC_RAW;
+
+ ret = lm3533_read(als->lm3533, reg, &val);
+ if (ret) {
+ dev_err(&indio_dev->dev, "failed to read adc\n");
+ return ret;
+ }
+
+ *adc = val;
+
+ return 0;
+}
+
+static int _lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 val;
+ int ret;
+
+ ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val);
+ if (ret) {
+ dev_err(&indio_dev->dev, "failed to read zone\n");
+ return ret;
+ }
+
+ val = (val & LM3533_ALS_ZONE_MASK) >> LM3533_ALS_ZONE_SHIFT;
+ *zone = min_t(u8, val, LM3533_ALS_ZONE_MAX);
+
+ return 0;
+}
+
+static int lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ int ret;
+
+ if (test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags)) {
+ *zone = atomic_read(&als->zone);
+ } else {
+ ret = _lm3533_als_get_zone(indio_dev, zone);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * channel output channel 0..2
+ * zone zone 0..4
+ */
+static inline u8 lm3533_als_get_target_reg(unsigned channel, unsigned zone)
+{
+ return LM3533_REG_ALS_TARGET_BASE + 5 * channel + zone;
+}
+
+static int lm3533_als_get_target(struct iio_dev *indio_dev, unsigned channel,
+ unsigned zone, u8 *val)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 reg;
+ int ret;
+
+ if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX)
+ return -EINVAL;
+
+ if (zone > LM3533_ALS_ZONE_MAX)
+ return -EINVAL;
+
+ reg = lm3533_als_get_target_reg(channel, zone);
+ ret = lm3533_read(als->lm3533, reg, val);
+ if (ret)
+ dev_err(&indio_dev->dev, "failed to get target current\n");
+
+ return ret;
+}
+
+static int lm3533_als_set_target(struct iio_dev *indio_dev, unsigned channel,
+ unsigned zone, u8 val)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 reg;
+ int ret;
+
+ if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX)
+ return -EINVAL;
+
+ if (zone > LM3533_ALS_ZONE_MAX)
+ return -EINVAL;
+
+ reg = lm3533_als_get_target_reg(channel, zone);
+ ret = lm3533_write(als->lm3533, reg, val);
+ if (ret)
+ dev_err(&indio_dev->dev, "failed to set target current\n");
+
+ return ret;
+}
+
+static int lm3533_als_get_current(struct iio_dev *indio_dev, unsigned channel,
+ int *val)
+{
+ u8 zone;
+ u8 target;
+ int ret;
+
+ ret = lm3533_als_get_zone(indio_dev, &zone);
+ if (ret)
+ return ret;
+
+ ret = lm3533_als_get_target(indio_dev, channel, zone, &target);
+ if (ret)
+ return ret;
+
+ *val = target;
+
+ return 0;
+}
+
+static int lm3533_als_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+
+ switch (mask) {
+ case 0:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = lm3533_als_get_adc(indio_dev, false, val);
+ break;
+ case IIO_CURRENT:
+ ret = lm3533_als_get_current(indio_dev, chan->channel,
+ val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_AVERAGE_RAW:
+ ret = lm3533_als_get_adc(indio_dev, true, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+}
+
+#define CHANNEL_CURRENT(_channel) \
+ { \
+ .type = IIO_CURRENT, \
+ .channel = _channel, \
+ .indexed = true, \
+ .output = true, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ }
+
+static const struct iio_chan_spec lm3533_als_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .channel = 0,
+ .indexed = true,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) |
+ BIT(IIO_CHAN_INFO_RAW),
+ },
+ CHANNEL_CURRENT(0),
+ CHANNEL_CURRENT(1),
+ CHANNEL_CURRENT(2),
+};
+
+static irqreturn_t lm3533_als_isr(int irq, void *dev_id)
+{
+
+ struct iio_dev *indio_dev = dev_id;
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 zone;
+ int ret;
+
+ /* Clear interrupt by reading the ALS zone register. */
+ ret = _lm3533_als_get_zone(indio_dev, &zone);
+ if (ret)
+ goto out;
+
+ atomic_set(&als->zone, zone);
+
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns());
+out:
+ return IRQ_HANDLED;
+}
+
+static int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 mask = LM3533_ALS_INT_ENABLE_MASK;
+ u8 val;
+ int ret;
+
+ if (enable)
+ val = mask;
+ else
+ val = 0;
+
+ ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, val, mask);
+ if (ret) {
+ dev_err(&indio_dev->dev, "failed to set int mode %d\n",
+ enable);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lm3533_als_get_int_mode(struct iio_dev *indio_dev, int *enable)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 mask = LM3533_ALS_INT_ENABLE_MASK;
+ u8 val;
+ int ret;
+
+ ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val);
+ if (ret) {
+ dev_err(&indio_dev->dev, "failed to get int mode\n");
+ return ret;
+ }
+
+ *enable = !!(val & mask);
+
+ return 0;
+}
+
+static inline u8 lm3533_als_get_threshold_reg(unsigned nr, bool raising)
+{
+ u8 offset = !raising;
+
+ return LM3533_REG_ALS_BOUNDARY_BASE + 2 * nr + offset;
+}
+
+static int lm3533_als_get_threshold(struct iio_dev *indio_dev, unsigned nr,
+ bool raising, u8 *val)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 reg;
+ int ret;
+
+ if (nr > LM3533_ALS_THRESH_MAX)
+ return -EINVAL;
+
+ reg = lm3533_als_get_threshold_reg(nr, raising);
+ ret = lm3533_read(als->lm3533, reg, val);
+ if (ret)
+ dev_err(&indio_dev->dev, "failed to get threshold\n");
+
+ return ret;
+}
+
+static int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr,
+ bool raising, u8 val)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 val2;
+ u8 reg, reg2;
+ int ret;
+
+ if (nr > LM3533_ALS_THRESH_MAX)
+ return -EINVAL;
+
+ reg = lm3533_als_get_threshold_reg(nr, raising);
+ reg2 = lm3533_als_get_threshold_reg(nr, !raising);
+
+ mutex_lock(&als->thresh_mutex);
+ ret = lm3533_read(als->lm3533, reg2, &val2);
+ if (ret) {
+ dev_err(&indio_dev->dev, "failed to get threshold\n");
+ goto out;
+ }
+ /*
+ * This device does not allow negative hysteresis (in fact, it uses
+ * whichever value is smaller as the lower bound) so we need to make
+ * sure that thresh_falling <= thresh_raising.
+ */
+ if ((raising && (val < val2)) || (!raising && (val > val2))) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = lm3533_write(als->lm3533, reg, val);
+ if (ret) {
+ dev_err(&indio_dev->dev, "failed to set threshold\n");
+ goto out;
+ }
+out:
+ mutex_unlock(&als->thresh_mutex);
+
+ return ret;
+}
+
+static int lm3533_als_get_hysteresis(struct iio_dev *indio_dev, unsigned nr,
+ u8 *val)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 falling;
+ u8 raising;
+ int ret;
+
+ if (nr > LM3533_ALS_THRESH_MAX)
+ return -EINVAL;
+
+ mutex_lock(&als->thresh_mutex);
+ ret = lm3533_als_get_threshold(indio_dev, nr, false, &falling);
+ if (ret)
+ goto out;
+ ret = lm3533_als_get_threshold(indio_dev, nr, true, &raising);
+ if (ret)
+ goto out;
+
+ *val = raising - falling;
+out:
+ mutex_unlock(&als->thresh_mutex);
+
+ return ret;
+}
+
+static ssize_t show_thresh_either_en(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct lm3533_als *als = iio_priv(indio_dev);
+ int enable;
+ int ret;
+
+ if (als->irq) {
+ ret = lm3533_als_get_int_mode(indio_dev, &enable);
+ if (ret)
+ return ret;
+ } else {
+ enable = 0;
+ }
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", enable);
+}
+
+static ssize_t store_thresh_either_en(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct lm3533_als *als = iio_priv(indio_dev);
+ unsigned long enable;
+ bool int_enabled;
+ u8 zone;
+ int ret;
+
+ if (!als->irq)
+ return -EBUSY;
+
+ if (kstrtoul(buf, 0, &enable))
+ return -EINVAL;
+
+ int_enabled = test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
+
+ if (enable && !int_enabled) {
+ ret = lm3533_als_get_zone(indio_dev, &zone);
+ if (ret)
+ return ret;
+
+ atomic_set(&als->zone, zone);
+
+ set_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
+ }
+
+ ret = lm3533_als_set_int_mode(indio_dev, enable);
+ if (ret) {
+ if (!int_enabled)
+ clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
+
+ return ret;
+ }
+
+ if (!enable)
+ clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
+
+ return len;
+}
+
+static ssize_t show_zone(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ u8 zone;
+ int ret;
+
+ ret = lm3533_als_get_zone(indio_dev, &zone);
+ if (ret)
+ return ret;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", zone);
+}
+
+enum lm3533_als_attribute_type {
+ LM3533_ATTR_TYPE_HYSTERESIS,
+ LM3533_ATTR_TYPE_TARGET,
+ LM3533_ATTR_TYPE_THRESH_FALLING,
+ LM3533_ATTR_TYPE_THRESH_RAISING,
+};
+
+struct lm3533_als_attribute {
+ struct device_attribute dev_attr;
+ enum lm3533_als_attribute_type type;
+ u8 val1;
+ u8 val2;
+};
+
+static inline struct lm3533_als_attribute *
+to_lm3533_als_attr(struct device_attribute *attr)
+{
+ return container_of(attr, struct lm3533_als_attribute, dev_attr);
+}
+
+static ssize_t show_als_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr);
+ u8 val;
+ int ret;
+
+ switch (als_attr->type) {
+ case LM3533_ATTR_TYPE_HYSTERESIS:
+ ret = lm3533_als_get_hysteresis(indio_dev, als_attr->val1,
+ &val);
+ break;
+ case LM3533_ATTR_TYPE_TARGET:
+ ret = lm3533_als_get_target(indio_dev, als_attr->val1,
+ als_attr->val2, &val);
+ break;
+ case LM3533_ATTR_TYPE_THRESH_FALLING:
+ ret = lm3533_als_get_threshold(indio_dev, als_attr->val1,
+ false, &val);
+ break;
+ case LM3533_ATTR_TYPE_THRESH_RAISING:
+ ret = lm3533_als_get_threshold(indio_dev, als_attr->val1,
+ true, &val);
+ break;
+ default:
+ ret = -ENXIO;
+ }
+
+ if (ret)
+ return ret;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_als_attr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr);
+ u8 val;
+ int ret;
+
+ if (kstrtou8(buf, 0, &val))
+ return -EINVAL;
+
+ switch (als_attr->type) {
+ case LM3533_ATTR_TYPE_TARGET:
+ ret = lm3533_als_set_target(indio_dev, als_attr->val1,
+ als_attr->val2, val);
+ break;
+ case LM3533_ATTR_TYPE_THRESH_FALLING:
+ ret = lm3533_als_set_threshold(indio_dev, als_attr->val1,
+ false, val);
+ break;
+ case LM3533_ATTR_TYPE_THRESH_RAISING:
+ ret = lm3533_als_set_threshold(indio_dev, als_attr->val1,
+ true, val);
+ break;
+ default:
+ ret = -ENXIO;
+ }
+
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+#define ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \
+ { .dev_attr = __ATTR(_name, _mode, _show, _store), \
+ .type = _type, \
+ .val1 = _val1, \
+ .val2 = _val2 }
+
+#define LM3533_ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \
+ struct lm3533_als_attribute lm3533_als_attr_##_name = \
+ ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2)
+
+#define ALS_TARGET_ATTR_RW(_channel, _zone) \
+ LM3533_ALS_ATTR(out_current##_channel##_current##_zone##_raw, \
+ S_IRUGO | S_IWUSR, \
+ show_als_attr, store_als_attr, \
+ LM3533_ATTR_TYPE_TARGET, _channel, _zone)
+/*
+ * ALS output current values (ALS mapper targets)
+ *
+ * out_current[0-2]_current[0-4]_raw 0-255
+ */
+static ALS_TARGET_ATTR_RW(0, 0);
+static ALS_TARGET_ATTR_RW(0, 1);
+static ALS_TARGET_ATTR_RW(0, 2);
+static ALS_TARGET_ATTR_RW(0, 3);
+static ALS_TARGET_ATTR_RW(0, 4);
+
+static ALS_TARGET_ATTR_RW(1, 0);
+static ALS_TARGET_ATTR_RW(1, 1);
+static ALS_TARGET_ATTR_RW(1, 2);
+static ALS_TARGET_ATTR_RW(1, 3);
+static ALS_TARGET_ATTR_RW(1, 4);
+
+static ALS_TARGET_ATTR_RW(2, 0);
+static ALS_TARGET_ATTR_RW(2, 1);
+static ALS_TARGET_ATTR_RW(2, 2);
+static ALS_TARGET_ATTR_RW(2, 3);
+static ALS_TARGET_ATTR_RW(2, 4);
+
+#define ALS_THRESH_FALLING_ATTR_RW(_nr) \
+ LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_falling_value, \
+ S_IRUGO | S_IWUSR, \
+ show_als_attr, store_als_attr, \
+ LM3533_ATTR_TYPE_THRESH_FALLING, _nr, 0)
+
+#define ALS_THRESH_RAISING_ATTR_RW(_nr) \
+ LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_raising_value, \
+ S_IRUGO | S_IWUSR, \
+ show_als_attr, store_als_attr, \
+ LM3533_ATTR_TYPE_THRESH_RAISING, _nr, 0)
+/*
+ * ALS Zone thresholds (boundaries)
+ *
+ * in_illuminance0_thresh[0-3]_falling_value 0-255
+ * in_illuminance0_thresh[0-3]_raising_value 0-255
+ */
+static ALS_THRESH_FALLING_ATTR_RW(0);
+static ALS_THRESH_FALLING_ATTR_RW(1);
+static ALS_THRESH_FALLING_ATTR_RW(2);
+static ALS_THRESH_FALLING_ATTR_RW(3);
+
+static ALS_THRESH_RAISING_ATTR_RW(0);
+static ALS_THRESH_RAISING_ATTR_RW(1);
+static ALS_THRESH_RAISING_ATTR_RW(2);
+static ALS_THRESH_RAISING_ATTR_RW(3);
+
+#define ALS_HYSTERESIS_ATTR_RO(_nr) \
+ LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_hysteresis, \
+ S_IRUGO, show_als_attr, NULL, \
+ LM3533_ATTR_TYPE_HYSTERESIS, _nr, 0)
+/*
+ * ALS Zone threshold hysteresis
+ *
+ * threshY_hysteresis = threshY_raising - threshY_falling
+ *
+ * in_illuminance0_thresh[0-3]_hysteresis 0-255
+ * in_illuminance0_thresh[0-3]_hysteresis 0-255
+ */
+static ALS_HYSTERESIS_ATTR_RO(0);
+static ALS_HYSTERESIS_ATTR_RO(1);
+static ALS_HYSTERESIS_ATTR_RO(2);
+static ALS_HYSTERESIS_ATTR_RO(3);
+
+#define ILLUMINANCE_ATTR_RO(_name) \
+ DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL)
+#define ILLUMINANCE_ATTR_RW(_name) \
+ DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR , \
+ show_##_name, store_##_name)
+/*
+ * ALS Zone threshold-event enable
+ *
+ * in_illuminance0_thresh_either_en 0,1
+ */
+static ILLUMINANCE_ATTR_RW(thresh_either_en);
+
+/*
+ * ALS Current Zone
+ *
+ * in_illuminance0_zone 0-4
+ */
+static ILLUMINANCE_ATTR_RO(zone);
+
+static struct attribute *lm3533_als_event_attributes[] = {
+ &dev_attr_in_illuminance0_thresh_either_en.attr,
+ &lm3533_als_attr_in_illuminance0_thresh0_falling_value.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh0_hysteresis.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh0_raising_value.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh1_falling_value.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh1_hysteresis.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh1_raising_value.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh2_falling_value.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh2_hysteresis.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh2_raising_value.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh3_falling_value.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh3_hysteresis.dev_attr.attr,
+ &lm3533_als_attr_in_illuminance0_thresh3_raising_value.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group lm3533_als_event_attribute_group = {
+ .attrs = lm3533_als_event_attributes
+};
+
+static struct attribute *lm3533_als_attributes[] = {
+ &dev_attr_in_illuminance0_zone.attr,
+ &lm3533_als_attr_out_current0_current0_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current0_current1_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current0_current2_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current0_current3_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current0_current4_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current1_current0_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current1_current1_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current1_current2_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current1_current3_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current1_current4_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current2_current0_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current2_current1_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current2_current2_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current2_current3_raw.dev_attr.attr,
+ &lm3533_als_attr_out_current2_current4_raw.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group lm3533_als_attribute_group = {
+ .attrs = lm3533_als_attributes
+};
+
+static int lm3533_als_set_input_mode(struct lm3533_als *als, bool pwm_mode)
+{
+ u8 mask = LM3533_ALS_INPUT_MODE_MASK;
+ u8 val;
+ int ret;
+
+ if (pwm_mode)
+ val = mask; /* pwm input */
+ else
+ val = 0; /* analog input */
+
+ ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, val, mask);
+ if (ret) {
+ dev_err(&als->pdev->dev, "failed to set input mode %d\n",
+ pwm_mode);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val)
+{
+ int ret;
+
+ if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX)
+ return -EINVAL;
+
+ ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val);
+ if (ret) {
+ dev_err(&als->pdev->dev, "failed to set resistor\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lm3533_als_setup(struct lm3533_als *als,
+ struct lm3533_als_platform_data *pdata)
+{
+ int ret;
+
+ ret = lm3533_als_set_input_mode(als, pdata->pwm_mode);
+ if (ret)
+ return ret;
+
+ /* ALS input is always high impedance in PWM-mode. */
+ if (!pdata->pwm_mode) {
+ ret = lm3533_als_set_resistor(als, pdata->r_select);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lm3533_als_setup_irq(struct lm3533_als *als, void *dev)
+{
+ u8 mask = LM3533_ALS_INT_ENABLE_MASK;
+ int ret;
+
+ /* Make sure interrupts are disabled. */
+ ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, 0, mask);
+ if (ret) {
+ dev_err(&als->pdev->dev, "failed to disable interrupts\n");
+ return ret;
+ }
+
+ ret = request_threaded_irq(als->irq, NULL, lm3533_als_isr,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ dev_name(&als->pdev->dev), dev);
+ if (ret) {
+ dev_err(&als->pdev->dev, "failed to request irq %d\n",
+ als->irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lm3533_als_enable(struct lm3533_als *als)
+{
+ u8 mask = LM3533_ALS_ENABLE_MASK;
+ int ret;
+
+ ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, mask, mask);
+ if (ret)
+ dev_err(&als->pdev->dev, "failed to enable ALS\n");
+
+ return ret;
+}
+
+static int lm3533_als_disable(struct lm3533_als *als)
+{
+ u8 mask = LM3533_ALS_ENABLE_MASK;
+ int ret;
+
+ ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, 0, mask);
+ if (ret)
+ dev_err(&als->pdev->dev, "failed to disable ALS\n");
+
+ return ret;
+}
+
+static const struct iio_info lm3533_als_info = {
+ .attrs = &lm3533_als_attribute_group,
+ .event_attrs = &lm3533_als_event_attribute_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = &lm3533_als_read_raw,
+};
+
+static int lm3533_als_probe(struct platform_device *pdev)
+{
+ struct lm3533 *lm3533;
+ struct lm3533_als_platform_data *pdata;
+ struct lm3533_als *als;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ lm3533 = dev_get_drvdata(pdev->dev.parent);
+ if (!lm3533)
+ return -EINVAL;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ indio_dev = iio_device_alloc(sizeof(*als));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &lm3533_als_info;
+ indio_dev->channels = lm3533_als_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lm3533_als_channels);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = pdev->dev.parent;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ als = iio_priv(indio_dev);
+ als->lm3533 = lm3533;
+ als->pdev = pdev;
+ als->irq = lm3533->irq;
+ atomic_set(&als->zone, 0);
+ mutex_init(&als->thresh_mutex);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ if (als->irq) {
+ ret = lm3533_als_setup_irq(als, indio_dev);
+ if (ret)
+ goto err_free_dev;
+ }
+
+ ret = lm3533_als_setup(als, pdata);
+ if (ret)
+ goto err_free_irq;
+
+ ret = lm3533_als_enable(als);
+ if (ret)
+ goto err_free_irq;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ALS\n");
+ goto err_disable;
+ }
+
+ return 0;
+
+err_disable:
+ lm3533_als_disable(als);
+err_free_irq:
+ if (als->irq)
+ free_irq(als->irq, indio_dev);
+err_free_dev:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int lm3533_als_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct lm3533_als *als = iio_priv(indio_dev);
+
+ lm3533_als_set_int_mode(indio_dev, false);
+ iio_device_unregister(indio_dev);
+ lm3533_als_disable(als);
+ if (als->irq)
+ free_irq(als->irq, indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver lm3533_als_driver = {
+ .driver = {
+ .name = "lm3533-als",
+ .owner = THIS_MODULE,
+ },
+ .probe = lm3533_als_probe,
+ .remove = lm3533_als_remove,
+};
+module_platform_driver(lm3533_als_driver);
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3533-als");
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
new file mode 100644
index 00000000..1f529f36
--- /dev/null
+++ b/drivers/iio/light/tsl2563.c
@@ -0,0 +1,887 @@
+/*
+ * drivers/iio/light/tsl2563.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
+ * Contact: Amit Kucheria <amit.kucheria@verdurent.com>
+ *
+ * Converted to IIO driver
+ * Amit Kucheria <amit.kucheria@verdurent.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/platform_data/tsl2563.h>
+
+/* Use this many bits for fraction part. */
+#define ADC_FRAC_BITS 14
+
+/* Given number of 1/10000's in ADC_FRAC_BITS precision. */
+#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000))
+
+/* Bits used for fraction in calibration coefficients.*/
+#define CALIB_FRAC_BITS 10
+/* 0.5 in CALIB_FRAC_BITS precision */
+#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1))
+/* Make a fraction from a number n that was multiplied with b. */
+#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b))
+/* Decimal 10^(digits in sysfs presentation) */
+#define CALIB_BASE_SYSFS 1000
+
+#define TSL2563_CMD 0x80
+#define TSL2563_CLEARINT 0x40
+
+#define TSL2563_REG_CTRL 0x00
+#define TSL2563_REG_TIMING 0x01
+#define TSL2563_REG_LOWLOW 0x02 /* data0 low threshold, 2 bytes */
+#define TSL2563_REG_LOWHIGH 0x03
+#define TSL2563_REG_HIGHLOW 0x04 /* data0 high threshold, 2 bytes */
+#define TSL2563_REG_HIGHHIGH 0x05
+#define TSL2563_REG_INT 0x06
+#define TSL2563_REG_ID 0x0a
+#define TSL2563_REG_DATA0LOW 0x0c /* broadband sensor value, 2 bytes */
+#define TSL2563_REG_DATA0HIGH 0x0d
+#define TSL2563_REG_DATA1LOW 0x0e /* infrared sensor value, 2 bytes */
+#define TSL2563_REG_DATA1HIGH 0x0f
+
+#define TSL2563_CMD_POWER_ON 0x03
+#define TSL2563_CMD_POWER_OFF 0x00
+#define TSL2563_CTRL_POWER_MASK 0x03
+
+#define TSL2563_TIMING_13MS 0x00
+#define TSL2563_TIMING_100MS 0x01
+#define TSL2563_TIMING_400MS 0x02
+#define TSL2563_TIMING_MASK 0x03
+#define TSL2563_TIMING_GAIN16 0x10
+#define TSL2563_TIMING_GAIN1 0x00
+
+#define TSL2563_INT_DISBLED 0x00
+#define TSL2563_INT_LEVEL 0x10
+#define TSL2563_INT_PERSIST(n) ((n) & 0x0F)
+
+struct tsl2563_gainlevel_coeff {
+ u8 gaintime;
+ u16 min;
+ u16 max;
+};
+
+static const struct tsl2563_gainlevel_coeff tsl2563_gainlevel_table[] = {
+ {
+ .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN16,
+ .min = 0,
+ .max = 65534,
+ }, {
+ .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN1,
+ .min = 2048,
+ .max = 65534,
+ }, {
+ .gaintime = TSL2563_TIMING_100MS | TSL2563_TIMING_GAIN1,
+ .min = 4095,
+ .max = 37177,
+ }, {
+ .gaintime = TSL2563_TIMING_13MS | TSL2563_TIMING_GAIN1,
+ .min = 3000,
+ .max = 65535,
+ },
+};
+
+struct tsl2563_chip {
+ struct mutex lock;
+ struct i2c_client *client;
+ struct delayed_work poweroff_work;
+
+ /* Remember state for suspend and resume functions */
+ bool suspended;
+
+ struct tsl2563_gainlevel_coeff const *gainlevel;
+
+ u16 low_thres;
+ u16 high_thres;
+ u8 intr;
+ bool int_enabled;
+
+ /* Calibration coefficients */
+ u32 calib0;
+ u32 calib1;
+ int cover_comp_gain;
+
+ /* Cache current values, to be returned while suspended */
+ u32 data0;
+ u32 data1;
+};
+
+static int tsl2563_set_power(struct tsl2563_chip *chip, int on)
+{
+ struct i2c_client *client = chip->client;
+ u8 cmd;
+
+ cmd = on ? TSL2563_CMD_POWER_ON : TSL2563_CMD_POWER_OFF;
+ return i2c_smbus_write_byte_data(client,
+ TSL2563_CMD | TSL2563_REG_CTRL, cmd);
+}
+
+/*
+ * Return value is 0 for off, 1 for on, or a negative error
+ * code if reading failed.
+ */
+static int tsl2563_get_power(struct tsl2563_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, TSL2563_CMD | TSL2563_REG_CTRL);
+ if (ret < 0)
+ return ret;
+
+ return (ret & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON;
+}
+
+static int tsl2563_configure(struct tsl2563_chip *chip)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_TIMING,
+ chip->gainlevel->gaintime);
+ if (ret)
+ goto error_ret;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_HIGHLOW,
+ chip->high_thres & 0xFF);
+ if (ret)
+ goto error_ret;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_HIGHHIGH,
+ (chip->high_thres >> 8) & 0xFF);
+ if (ret)
+ goto error_ret;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_LOWLOW,
+ chip->low_thres & 0xFF);
+ if (ret)
+ goto error_ret;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_LOWHIGH,
+ (chip->low_thres >> 8) & 0xFF);
+/*
+ * Interrupt register is automatically written anyway if it is relevant
+ * so is not here.
+ */
+error_ret:
+ return ret;
+}
+
+static void tsl2563_poweroff_work(struct work_struct *work)
+{
+ struct tsl2563_chip *chip =
+ container_of(work, struct tsl2563_chip, poweroff_work.work);
+ tsl2563_set_power(chip, 0);
+}
+
+static int tsl2563_detect(struct tsl2563_chip *chip)
+{
+ int ret;
+
+ ret = tsl2563_set_power(chip, 1);
+ if (ret)
+ return ret;
+
+ ret = tsl2563_get_power(chip);
+ if (ret < 0)
+ return ret;
+
+ return ret ? 0 : -ENODEV;
+}
+
+static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, TSL2563_CMD | TSL2563_REG_ID);
+ if (ret < 0)
+ return ret;
+
+ *id = ret;
+
+ return 0;
+}
+
+/*
+ * "Normalized" ADC value is one obtained with 400ms of integration time and
+ * 16x gain. This function returns the number of bits of shift needed to
+ * convert between normalized values and HW values obtained using given
+ * timing and gain settings.
+ */
+static int adc_shiftbits(u8 timing)
+{
+ int shift = 0;
+
+ switch (timing & TSL2563_TIMING_MASK) {
+ case TSL2563_TIMING_13MS:
+ shift += 5;
+ break;
+ case TSL2563_TIMING_100MS:
+ shift += 2;
+ break;
+ case TSL2563_TIMING_400MS:
+ /* no-op */
+ break;
+ }
+
+ if (!(timing & TSL2563_TIMING_GAIN16))
+ shift += 4;
+
+ return shift;
+}
+
+/* Convert a HW ADC value to normalized scale. */
+static u32 normalize_adc(u16 adc, u8 timing)
+{
+ return adc << adc_shiftbits(timing);
+}
+
+static void tsl2563_wait_adc(struct tsl2563_chip *chip)
+{
+ unsigned int delay;
+
+ switch (chip->gainlevel->gaintime & TSL2563_TIMING_MASK) {
+ case TSL2563_TIMING_13MS:
+ delay = 14;
+ break;
+ case TSL2563_TIMING_100MS:
+ delay = 101;
+ break;
+ default:
+ delay = 402;
+ }
+ /*
+ * TODO: Make sure that we wait at least required delay but why we
+ * have to extend it one tick more?
+ */
+ schedule_timeout_interruptible(msecs_to_jiffies(delay) + 2);
+}
+
+static int tsl2563_adjust_gainlevel(struct tsl2563_chip *chip, u16 adc)
+{
+ struct i2c_client *client = chip->client;
+
+ if (adc > chip->gainlevel->max || adc < chip->gainlevel->min) {
+
+ (adc > chip->gainlevel->max) ?
+ chip->gainlevel++ : chip->gainlevel--;
+
+ i2c_smbus_write_byte_data(client,
+ TSL2563_CMD | TSL2563_REG_TIMING,
+ chip->gainlevel->gaintime);
+
+ tsl2563_wait_adc(chip);
+ tsl2563_wait_adc(chip);
+
+ return 1;
+ } else
+ return 0;
+}
+
+static int tsl2563_get_adc(struct tsl2563_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ u16 adc0, adc1;
+ int retry = 1;
+ int ret = 0;
+
+ if (chip->suspended)
+ goto out;
+
+ if (!chip->int_enabled) {
+ cancel_delayed_work(&chip->poweroff_work);
+
+ if (!tsl2563_get_power(chip)) {
+ ret = tsl2563_set_power(chip, 1);
+ if (ret)
+ goto out;
+ ret = tsl2563_configure(chip);
+ if (ret)
+ goto out;
+ tsl2563_wait_adc(chip);
+ }
+ }
+
+ while (retry) {
+ ret = i2c_smbus_read_word_data(client,
+ TSL2563_CMD | TSL2563_REG_DATA0LOW);
+ if (ret < 0)
+ goto out;
+ adc0 = ret;
+
+ ret = i2c_smbus_read_word_data(client,
+ TSL2563_CMD | TSL2563_REG_DATA1LOW);
+ if (ret < 0)
+ goto out;
+ adc1 = ret;
+
+ retry = tsl2563_adjust_gainlevel(chip, adc0);
+ }
+
+ chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
+ chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
+
+ if (!chip->int_enabled)
+ schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static inline int calib_to_sysfs(u32 calib)
+{
+ return (int) (((calib * CALIB_BASE_SYSFS) +
+ CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
+}
+
+static inline u32 calib_from_sysfs(int value)
+{
+ return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
+}
+
+/*
+ * Conversions between lux and ADC values.
+ *
+ * The basic formula is lux = c0 * adc0 - c1 * adc1, where c0 and c1 are
+ * appropriate constants. Different constants are needed for different
+ * kinds of light, determined by the ratio adc1/adc0 (basically the ratio
+ * of the intensities in infrared and visible wavelengths). lux_table below
+ * lists the upper threshold of the adc1/adc0 ratio and the corresponding
+ * constants.
+ */
+
+struct tsl2563_lux_coeff {
+ unsigned long ch_ratio;
+ unsigned long ch0_coeff;
+ unsigned long ch1_coeff;
+};
+
+static const struct tsl2563_lux_coeff lux_table[] = {
+ {
+ .ch_ratio = FRAC10K(1300),
+ .ch0_coeff = FRAC10K(315),
+ .ch1_coeff = FRAC10K(262),
+ }, {
+ .ch_ratio = FRAC10K(2600),
+ .ch0_coeff = FRAC10K(337),
+ .ch1_coeff = FRAC10K(430),
+ }, {
+ .ch_ratio = FRAC10K(3900),
+ .ch0_coeff = FRAC10K(363),
+ .ch1_coeff = FRAC10K(529),
+ }, {
+ .ch_ratio = FRAC10K(5200),
+ .ch0_coeff = FRAC10K(392),
+ .ch1_coeff = FRAC10K(605),
+ }, {
+ .ch_ratio = FRAC10K(6500),
+ .ch0_coeff = FRAC10K(229),
+ .ch1_coeff = FRAC10K(291),
+ }, {
+ .ch_ratio = FRAC10K(8000),
+ .ch0_coeff = FRAC10K(157),
+ .ch1_coeff = FRAC10K(180),
+ }, {
+ .ch_ratio = FRAC10K(13000),
+ .ch0_coeff = FRAC10K(34),
+ .ch1_coeff = FRAC10K(26),
+ }, {
+ .ch_ratio = ULONG_MAX,
+ .ch0_coeff = 0,
+ .ch1_coeff = 0,
+ },
+};
+
+/* Convert normalized, scaled ADC values to lux. */
+static unsigned int adc_to_lux(u32 adc0, u32 adc1)
+{
+ const struct tsl2563_lux_coeff *lp = lux_table;
+ unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
+
+ ratio = ch0 ? ((ch1 << ADC_FRAC_BITS) / ch0) : ULONG_MAX;
+
+ while (lp->ch_ratio < ratio)
+ lp++;
+
+ lux = ch0 * lp->ch0_coeff - ch1 * lp->ch1_coeff;
+
+ return (unsigned int) (lux >> ADC_FRAC_BITS);
+}
+
+/* Apply calibration coefficient to ADC count. */
+static u32 calib_adc(u32 adc, u32 calib)
+{
+ unsigned long scaled = adc;
+
+ scaled *= calib;
+ scaled >>= CALIB_FRAC_BITS;
+
+ return (u32) scaled;
+}
+
+static int tsl2563_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+
+ if (chan->channel == IIO_MOD_LIGHT_BOTH)
+ chip->calib0 = calib_from_sysfs(val);
+ else
+ chip->calib1 = calib_from_sysfs(val);
+
+ return 0;
+}
+
+static int tsl2563_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret = -EINVAL;
+ u32 calib0, calib1;
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+
+ mutex_lock(&chip->lock);
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = tsl2563_get_adc(chip);
+ if (ret)
+ goto error_ret;
+ calib0 = calib_adc(chip->data0, chip->calib0) *
+ chip->cover_comp_gain;
+ calib1 = calib_adc(chip->data1, chip->calib1) *
+ chip->cover_comp_gain;
+ *val = adc_to_lux(calib0, calib1);
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_INTENSITY:
+ ret = tsl2563_get_adc(chip);
+ if (ret)
+ goto error_ret;
+ if (chan->channel == 0)
+ *val = chip->data0;
+ else
+ *val = chip->data1;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->channel == 0)
+ *val = calib_to_sysfs(chip->calib0);
+ else
+ *val = calib_to_sysfs(chip->calib1);
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+error_ret:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static const struct iio_chan_spec tsl2563_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .channel = 0,
+ }, {
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE),
+ .event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING) |
+ IIO_EV_BIT(IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING)),
+ }, {
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_IR,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE),
+ }
+};
+
+static int tsl2563_read_thresh(struct iio_dev *indio_dev,
+ u64 event_code,
+ int *val)
+{
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+
+ switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+ case IIO_EV_DIR_RISING:
+ *val = chip->high_thres;
+ break;
+ case IIO_EV_DIR_FALLING:
+ *val = chip->low_thres;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tsl2563_write_thresh(struct iio_dev *indio_dev,
+ u64 event_code,
+ int val)
+{
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+ int ret;
+ u8 address;
+
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING)
+ address = TSL2563_REG_HIGHLOW;
+ else
+ address = TSL2563_REG_LOWLOW;
+ mutex_lock(&chip->lock);
+ ret = i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | address,
+ val & 0xFF);
+ if (ret)
+ goto error_ret;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | (address + 1),
+ (val >> 8) & 0xFF);
+ if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING)
+ chip->high_thres = val;
+ else
+ chip->low_thres = val;
+
+error_ret:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static irqreturn_t tsl2563_event_handler(int irq, void *private)
+{
+ struct iio_dev *dev_info = private;
+ struct tsl2563_chip *chip = iio_priv(dev_info);
+
+ iio_push_event(dev_info,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns());
+
+ /* clear the interrupt and push the event */
+ i2c_smbus_write_byte(chip->client, TSL2563_CMD | TSL2563_CLEARINT);
+ return IRQ_HANDLED;
+}
+
+static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev,
+ u64 event_code,
+ int state)
+{
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+
+ mutex_lock(&chip->lock);
+ if (state && !(chip->intr & 0x30)) {
+ chip->intr &= ~0x30;
+ chip->intr |= 0x10;
+ /* ensure the chip is actually on */
+ cancel_delayed_work(&chip->poweroff_work);
+ if (!tsl2563_get_power(chip)) {
+ ret = tsl2563_set_power(chip, 1);
+ if (ret)
+ goto out;
+ ret = tsl2563_configure(chip);
+ if (ret)
+ goto out;
+ }
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_INT,
+ chip->intr);
+ chip->int_enabled = true;
+ }
+
+ if (!state && (chip->intr & 0x30)) {
+ chip->intr &= ~0x30;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_INT,
+ chip->intr);
+ chip->int_enabled = false;
+ /* now the interrupt is not enabled, we can go to sleep */
+ schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
+ }
+out:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev,
+ u64 event_code)
+{
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = i2c_smbus_read_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_INT);
+ mutex_unlock(&chip->lock);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & 0x30);
+}
+
+static const struct iio_info tsl2563_info_no_irq = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2563_read_raw,
+ .write_raw = &tsl2563_write_raw,
+};
+
+static const struct iio_info tsl2563_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2563_read_raw,
+ .write_raw = &tsl2563_write_raw,
+ .read_event_value = &tsl2563_read_thresh,
+ .write_event_value = &tsl2563_write_thresh,
+ .read_event_config = &tsl2563_read_interrupt_config,
+ .write_event_config = &tsl2563_write_interrupt_config,
+};
+
+static int tsl2563_probe(struct i2c_client *client,
+ const struct i2c_device_id *device_id)
+{
+ struct iio_dev *indio_dev;
+ struct tsl2563_chip *chip;
+ struct tsl2563_platform_data *pdata = client->dev.platform_data;
+ int err = 0;
+ u8 id = 0;
+
+ indio_dev = iio_device_alloc(sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+
+ i2c_set_clientdata(client, chip);
+ chip->client = client;
+
+ err = tsl2563_detect(chip);
+ if (err) {
+ dev_err(&client->dev, "detect error %d\n", -err);
+ goto fail1;
+ }
+
+ err = tsl2563_read_id(chip, &id);
+ if (err) {
+ dev_err(&client->dev, "read id error %d\n", -err);
+ goto fail1;
+ }
+
+ mutex_init(&chip->lock);
+
+ /* Default values used until userspace says otherwise */
+ chip->low_thres = 0x0;
+ chip->high_thres = 0xffff;
+ chip->gainlevel = tsl2563_gainlevel_table;
+ chip->intr = TSL2563_INT_PERSIST(4);
+ chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
+ chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
+
+ if (pdata)
+ chip->cover_comp_gain = pdata->cover_comp_gain;
+ else
+ chip->cover_comp_gain = 1;
+
+ dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f);
+ indio_dev->name = client->name;
+ indio_dev->channels = tsl2563_channels;
+ indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (client->irq)
+ indio_dev->info = &tsl2563_info;
+ else
+ indio_dev->info = &tsl2563_info_no_irq;
+
+ if (client->irq) {
+ err = request_threaded_irq(client->irq,
+ NULL,
+ &tsl2563_event_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "tsl2563_event",
+ indio_dev);
+ if (err) {
+ dev_err(&client->dev, "irq request error %d\n", -err);
+ goto fail1;
+ }
+ }
+
+ err = tsl2563_configure(chip);
+ if (err) {
+ dev_err(&client->dev, "configure error %d\n", -err);
+ goto fail2;
+ }
+
+ INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work);
+
+ /* The interrupt cannot yet be enabled so this is fine without lock */
+ schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
+
+ err = iio_device_register(indio_dev);
+ if (err) {
+ dev_err(&client->dev, "iio registration error %d\n", -err);
+ goto fail3;
+ }
+
+ return 0;
+
+fail3:
+ cancel_delayed_work(&chip->poweroff_work);
+ flush_scheduled_work();
+fail2:
+ if (client->irq)
+ free_irq(client->irq, indio_dev);
+fail1:
+ iio_device_free(indio_dev);
+ return err;
+}
+
+static int tsl2563_remove(struct i2c_client *client)
+{
+ struct tsl2563_chip *chip = i2c_get_clientdata(client);
+ struct iio_dev *indio_dev = iio_priv_to_dev(chip);
+
+ iio_device_unregister(indio_dev);
+ if (!chip->int_enabled)
+ cancel_delayed_work(&chip->poweroff_work);
+ /* Ensure that interrupts are disabled - then flush any bottom halves */
+ chip->intr &= ~0x30;
+ i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | TSL2563_REG_INT,
+ chip->intr);
+ flush_scheduled_work();
+ tsl2563_set_power(chip, 0);
+ if (client->irq)
+ free_irq(client->irq, indio_dev);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tsl2563_suspend(struct device *dev)
+{
+ struct tsl2563_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = tsl2563_set_power(chip, 0);
+ if (ret)
+ goto out;
+
+ chip->suspended = true;
+
+out:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static int tsl2563_resume(struct device *dev)
+{
+ struct tsl2563_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = tsl2563_set_power(chip, 1);
+ if (ret)
+ goto out;
+
+ ret = tsl2563_configure(chip);
+ if (ret)
+ goto out;
+
+ chip->suspended = false;
+
+out:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(tsl2563_pm_ops, tsl2563_suspend, tsl2563_resume);
+#define TSL2563_PM_OPS (&tsl2563_pm_ops)
+#else
+#define TSL2563_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id tsl2563_id[] = {
+ { "tsl2560", 0 },
+ { "tsl2561", 1 },
+ { "tsl2562", 2 },
+ { "tsl2563", 3 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tsl2563_id);
+
+static struct i2c_driver tsl2563_i2c_driver = {
+ .driver = {
+ .name = "tsl2563",
+ .pm = TSL2563_PM_OPS,
+ },
+ .probe = tsl2563_probe,
+ .remove = tsl2563_remove,
+ .id_table = tsl2563_id,
+};
+module_i2c_driver(tsl2563_i2c_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("tsl2563 light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
new file mode 100644
index 00000000..1014943d
--- /dev/null
+++ b/drivers/iio/light/vcnl4000.c
@@ -0,0 +1,217 @@
+/*
+ * vcnl4000.c - Support for Vishay VCNL4000 combined ambient light and
+ * proximity sensor
+ *
+ * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO driver for VCNL4000 (7-bit I2C slave address 0x13)
+ *
+ * TODO:
+ * allow to adjust IR current
+ * proximity threshold and event handling
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define VCNL4000_DRV_NAME "vcnl4000"
+
+#define VCNL4000_COMMAND 0x80 /* Command register */
+#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
+#define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */
+#define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */
+#define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */
+#define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */
+#define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
+#define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
+#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
+#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
+
+/* Bit masks for COMMAND register */
+#define VCNL4000_AL_RDY 0x40 /* ALS data ready? */
+#define VCNL4000_PS_RDY 0x20 /* proximity data ready? */
+#define VCNL4000_AL_OD 0x10 /* start on-demand ALS measurement */
+#define VCNL4000_PS_OD 0x08 /* start on-demand proximity measurement */
+
+struct vcnl4000_data {
+ struct i2c_client *client;
+};
+
+static const struct i2c_device_id vcnl4000_id[] = {
+ { "vcnl4000", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
+
+static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
+ u8 rdy_mask, u8 data_reg, int *val)
+{
+ int tries = 20;
+ u16 buf;
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
+ req_mask);
+ if (ret < 0)
+ return ret;
+
+ /* wait for data to become ready */
+ while (tries--) {
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
+ if (ret < 0)
+ return ret;
+ if (ret & rdy_mask)
+ break;
+ msleep(20); /* measurement takes up to 100 ms */
+ }
+
+ if (tries < 0) {
+ dev_err(&data->client->dev,
+ "vcnl4000_measure() failed, data not ready\n");
+ return -EIO;
+ }
+
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ data_reg, sizeof(buf), (u8 *) &buf);
+ if (ret < 0)
+ return ret;
+
+ *val = be16_to_cpu(buf);
+
+ return 0;
+}
+
+static const struct iio_chan_spec vcnl4000_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }
+};
+
+static int vcnl4000_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret = -EINVAL;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = vcnl4000_measure(data,
+ VCNL4000_AL_OD, VCNL4000_AL_RDY,
+ VCNL4000_AL_RESULT_HI, val);
+ if (ret < 0)
+ return ret;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_PROXIMITY:
+ ret = vcnl4000_measure(data,
+ VCNL4000_PS_OD, VCNL4000_PS_RDY,
+ VCNL4000_PS_RESULT_HI, val);
+ if (ret < 0)
+ return ret;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_LIGHT) {
+ *val = 0;
+ *val2 = 250000;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static const struct iio_info vcnl4000_info = {
+ .read_raw = vcnl4000_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int vcnl4000_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct vcnl4000_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
+ if (ret < 0)
+ goto error_free_dev;
+
+ dev_info(&client->dev, "VCNL4000 Ambient light/proximity sensor, Prod %02x, Rev: %02x\n",
+ ret >> 4, ret & 0xf);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &vcnl4000_info;
+ indio_dev->channels = vcnl4000_channels;
+ indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
+ indio_dev->name = VCNL4000_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto error_free_dev;
+
+ return 0;
+
+error_free_dev:
+ iio_device_free(indio_dev);
+ return ret;
+}
+
+static int vcnl4000_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct i2c_driver vcnl4000_driver = {
+ .driver = {
+ .name = VCNL4000_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = vcnl4000_probe,
+ .remove = vcnl4000_remove,
+ .id_table = vcnl4000_id,
+};
+
+module_i2c_driver(vcnl4000_driver);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
new file mode 100644
index 00000000..4025222c
--- /dev/null
+++ b/drivers/iio/magnetometer/Kconfig
@@ -0,0 +1,5 @@
+
+source "drivers/staging/iio/magnetometer/inv_compass/Kconfig"
+
+
+
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
new file mode 100644
index 00000000..a274a4ea
--- /dev/null
+++ b/drivers/iio/magnetometer/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the industrial I/O core.
+#
+
+obj-y += inv_compass/
+
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
new file mode 100644
index 00000000..53f82900
--- /dev/null
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -0,0 +1,478 @@
+/*
+ * A sensor driver for the magnetometer AK8975.
+ *
+ * Magnetic compass sensor driver for monitoring magnetic flux information.
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#include <linux/gpio.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+/*
+ * Register definitions, as well as various shifts and masks to get at the
+ * individual fields of the registers.
+ */
+#define AK8975_REG_WIA 0x00
+#define AK8975_DEVICE_ID 0x48
+
+#define AK8975_REG_INFO 0x01
+
+#define AK8975_REG_ST1 0x02
+#define AK8975_REG_ST1_DRDY_SHIFT 0
+#define AK8975_REG_ST1_DRDY_MASK (1 << AK8975_REG_ST1_DRDY_SHIFT)
+
+#define AK8975_REG_HXL 0x03
+#define AK8975_REG_HXH 0x04
+#define AK8975_REG_HYL 0x05
+#define AK8975_REG_HYH 0x06
+#define AK8975_REG_HZL 0x07
+#define AK8975_REG_HZH 0x08
+#define AK8975_REG_ST2 0x09
+#define AK8975_REG_ST2_DERR_SHIFT 2
+#define AK8975_REG_ST2_DERR_MASK (1 << AK8975_REG_ST2_DERR_SHIFT)
+
+#define AK8975_REG_ST2_HOFL_SHIFT 3
+#define AK8975_REG_ST2_HOFL_MASK (1 << AK8975_REG_ST2_HOFL_SHIFT)
+
+#define AK8975_REG_CNTL 0x0A
+#define AK8975_REG_CNTL_MODE_SHIFT 0
+#define AK8975_REG_CNTL_MODE_MASK (0xF << AK8975_REG_CNTL_MODE_SHIFT)
+#define AK8975_REG_CNTL_MODE_POWER_DOWN 0
+#define AK8975_REG_CNTL_MODE_ONCE 1
+#define AK8975_REG_CNTL_MODE_SELF_TEST 8
+#define AK8975_REG_CNTL_MODE_FUSE_ROM 0xF
+
+#define AK8975_REG_RSVC 0x0B
+#define AK8975_REG_ASTC 0x0C
+#define AK8975_REG_TS1 0x0D
+#define AK8975_REG_TS2 0x0E
+#define AK8975_REG_I2CDIS 0x0F
+#define AK8975_REG_ASAX 0x10
+#define AK8975_REG_ASAY 0x11
+#define AK8975_REG_ASAZ 0x12
+
+#define AK8975_MAX_REGS AK8975_REG_ASAZ
+
+/*
+ * Miscellaneous values.
+ */
+#define AK8975_MAX_CONVERSION_TIMEOUT 500
+#define AK8975_CONVERSION_DONE_POLL_TIME 10
+
+/*
+ * Per-instance context data for the device.
+ */
+struct ak8975_data {
+ struct i2c_client *client;
+ struct attribute_group attrs;
+ struct mutex lock;
+ u8 asa[3];
+ long raw_to_gauss[3];
+ u8 reg_cache[AK8975_MAX_REGS];
+ int eoc_gpio;
+};
+
+static const int ak8975_index_to_reg[] = {
+ AK8975_REG_HXL, AK8975_REG_HYL, AK8975_REG_HZL,
+};
+
+/*
+ * Helper function to write to the I2C device's registers.
+ */
+static int ak8975_write_data(struct i2c_client *client,
+ u8 reg, u8 val, u8 mask, u8 shift)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ak8975_data *data = iio_priv(indio_dev);
+ u8 regval;
+ int ret;
+
+ regval = (data->reg_cache[reg] & ~mask) | (val << shift);
+ ret = i2c_smbus_write_byte_data(client, reg, regval);
+ if (ret < 0) {
+ dev_err(&client->dev, "Write to device fails status %x\n", ret);
+ return ret;
+ }
+ data->reg_cache[reg] = regval;
+
+ return 0;
+}
+
+/*
+ * Perform some start-of-day setup, including reading the asa calibration
+ * values and caching them.
+ */
+static int ak8975_setup(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ak8975_data *data = iio_priv(indio_dev);
+ u8 device_id;
+ int ret;
+
+ /* Confirm that the device we're talking to is really an AK8975. */
+ ret = i2c_smbus_read_byte_data(client, AK8975_REG_WIA);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error reading WIA\n");
+ return ret;
+ }
+ device_id = ret;
+ if (device_id != AK8975_DEVICE_ID) {
+ dev_err(&client->dev, "Device ak8975 not found\n");
+ return -ENODEV;
+ }
+
+ /* Write the fused rom access mode. */
+ ret = ak8975_write_data(client,
+ AK8975_REG_CNTL,
+ AK8975_REG_CNTL_MODE_FUSE_ROM,
+ AK8975_REG_CNTL_MODE_MASK,
+ AK8975_REG_CNTL_MODE_SHIFT);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error in setting fuse access mode\n");
+ return ret;
+ }
+
+ /* Get asa data and store in the device data. */
+ ret = i2c_smbus_read_i2c_block_data(client, AK8975_REG_ASAX,
+ 3, data->asa);
+ if (ret < 0) {
+ dev_err(&client->dev, "Not able to read asa data\n");
+ return ret;
+ }
+
+ /* After reading fuse ROM data set power-down mode */
+ ret = ak8975_write_data(client,
+ AK8975_REG_CNTL,
+ AK8975_REG_CNTL_MODE_POWER_DOWN,
+ AK8975_REG_CNTL_MODE_MASK,
+ AK8975_REG_CNTL_MODE_SHIFT);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error in setting power-down mode\n");
+ return ret;
+ }
+
+/*
+ * Precalculate scale factor (in Gauss units) for each axis and
+ * store in the device data.
+ *
+ * This scale factor is axis-dependent, and is derived from 3 calibration
+ * factors ASA(x), ASA(y), and ASA(z).
+ *
+ * These ASA values are read from the sensor device at start of day, and
+ * cached in the device context struct.
+ *
+ * Adjusting the flux value with the sensitivity adjustment value should be
+ * done via the following formula:
+ *
+ * Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 )
+ *
+ * where H is the raw value, ASA is the sensitivity adjustment, and Hadj
+ * is the resultant adjusted value.
+ *
+ * We reduce the formula to:
+ *
+ * Hadj = H * (ASA + 128) / 256
+ *
+ * H is in the range of -4096 to 4095. The magnetometer has a range of
+ * +-1229uT. To go from the raw value to uT is:
+ *
+ * HuT = H * 1229/4096, or roughly, 3/10.
+ *
+ * Since 1uT = 100 gauss, our final scale factor becomes:
+ *
+ * Hadj = H * ((ASA + 128) / 256) * 3/10 * 100
+ * Hadj = H * ((ASA + 128) * 30 / 256
+ *
+ * Since ASA doesn't change, we cache the resultant scale factor into the
+ * device context in ak8975_setup().
+ */
+ data->raw_to_gauss[0] = ((data->asa[0] + 128) * 30) >> 8;
+ data->raw_to_gauss[1] = ((data->asa[1] + 128) * 30) >> 8;
+ data->raw_to_gauss[2] = ((data->asa[2] + 128) * 30) >> 8;
+
+ return 0;
+}
+
+static int wait_conversion_complete_gpio(struct ak8975_data *data)
+{
+ struct i2c_client *client = data->client;
+ u32 timeout_ms = AK8975_MAX_CONVERSION_TIMEOUT;
+ int ret;
+
+ /* Wait for the conversion to complete. */
+ while (timeout_ms) {
+ msleep(AK8975_CONVERSION_DONE_POLL_TIME);
+ if (gpio_get_value(data->eoc_gpio))
+ break;
+ timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME;
+ }
+ if (!timeout_ms) {
+ dev_err(&client->dev, "Conversion timeout happened\n");
+ return -EINVAL;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
+ if (ret < 0)
+ dev_err(&client->dev, "Error in reading ST1\n");
+
+ return ret;
+}
+
+static int wait_conversion_complete_polled(struct ak8975_data *data)
+{
+ struct i2c_client *client = data->client;
+ u8 read_status;
+ u32 timeout_ms = AK8975_MAX_CONVERSION_TIMEOUT;
+ int ret;
+
+ /* Wait for the conversion to complete. */
+ while (timeout_ms) {
+ msleep(AK8975_CONVERSION_DONE_POLL_TIME);
+ ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error in reading ST1\n");
+ return ret;
+ }
+ read_status = ret;
+ if (read_status)
+ break;
+ timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME;
+ }
+ if (!timeout_ms) {
+ dev_err(&client->dev, "Conversion timeout happened\n");
+ return -EINVAL;
+ }
+ return read_status;
+}
+
+/*
+ * Emits the raw flux value for the x, y, or z axis.
+ */
+static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
+{
+ struct ak8975_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ mutex_lock(&data->lock);
+
+ /* Set up the device for taking a sample. */
+ ret = ak8975_write_data(client,
+ AK8975_REG_CNTL,
+ AK8975_REG_CNTL_MODE_ONCE,
+ AK8975_REG_CNTL_MODE_MASK,
+ AK8975_REG_CNTL_MODE_SHIFT);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error in setting operating mode\n");
+ goto exit;
+ }
+
+ /* Wait for the conversion to complete. */
+ if (gpio_is_valid(data->eoc_gpio))
+ ret = wait_conversion_complete_gpio(data);
+ else
+ ret = wait_conversion_complete_polled(data);
+ if (ret < 0)
+ goto exit;
+
+ if (ret & AK8975_REG_ST1_DRDY_MASK) {
+ ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST2);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error in reading ST2\n");
+ goto exit;
+ }
+ if (ret & (AK8975_REG_ST2_DERR_MASK |
+ AK8975_REG_ST2_HOFL_MASK)) {
+ dev_err(&client->dev, "ST2 status error 0x%x\n", ret);
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+
+ /* Read the flux value from the appropriate register
+ (the register is specified in the iio device attributes). */
+ ret = i2c_smbus_read_word_data(client, ak8975_index_to_reg[index]);
+ if (ret < 0) {
+ dev_err(&client->dev, "Read axis data fails\n");
+ goto exit;
+ }
+
+ mutex_unlock(&data->lock);
+
+ /* Clamp to valid range. */
+ *val = clamp_t(s16, ret, -4096, 4095);
+ return IIO_VAL_INT;
+
+exit:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int ak8975_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct ak8975_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return ak8975_read_axis(indio_dev, chan->address, val);
+ case IIO_CHAN_INFO_SCALE:
+ *val = data->raw_to_gauss[chan->address];
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+#define AK8975_CHANNEL(axis, index) \
+ { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .address = index, \
+ }
+
+static const struct iio_chan_spec ak8975_channels[] = {
+ AK8975_CHANNEL(X, 0), AK8975_CHANNEL(Y, 1), AK8975_CHANNEL(Z, 2),
+};
+
+static const struct iio_info ak8975_info = {
+ .read_raw = &ak8975_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ak8975_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ak8975_data *data;
+ struct iio_dev *indio_dev;
+ int eoc_gpio;
+ int err;
+
+ /* Grab and set up the supplied GPIO. */
+ if (client->dev.platform_data == NULL)
+ eoc_gpio = -1;
+ else
+ eoc_gpio = *(int *)(client->dev.platform_data);
+
+ /* We may not have a GPIO based IRQ to scan, that is fine, we will
+ poll if so */
+ if (gpio_is_valid(eoc_gpio)) {
+ err = gpio_request_one(eoc_gpio, GPIOF_IN, "ak_8975");
+ if (err < 0) {
+ dev_err(&client->dev,
+ "failed to request GPIO %d, error %d\n",
+ eoc_gpio, err);
+ goto exit;
+ }
+ }
+
+ /* Register with IIO */
+ indio_dev = iio_device_alloc(sizeof(*data));
+ if (indio_dev == NULL) {
+ err = -ENOMEM;
+ goto exit_gpio;
+ }
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ /* Perform some basic start-of-day setup of the device. */
+ err = ak8975_setup(client);
+ if (err < 0) {
+ dev_err(&client->dev, "AK8975 initialization fails\n");
+ goto exit_free_iio;
+ }
+
+ data->client = client;
+ mutex_init(&data->lock);
+ data->eoc_gpio = eoc_gpio;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = ak8975_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ak8975_channels);
+ indio_dev->info = &ak8975_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ err = iio_device_register(indio_dev);
+ if (err < 0)
+ goto exit_free_iio;
+
+ return 0;
+
+exit_free_iio:
+ iio_device_free(indio_dev);
+exit_gpio:
+ if (gpio_is_valid(eoc_gpio))
+ gpio_free(eoc_gpio);
+exit:
+ return err;
+}
+
+static int ak8975_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ak8975_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (gpio_is_valid(data->eoc_gpio))
+ gpio_free(data->eoc_gpio);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id ak8975_id[] = {
+ {"ak8975", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ak8975_id);
+
+static const struct of_device_id ak8975_of_match[] = {
+ { .compatible = "asahi-kasei,ak8975", },
+ { .compatible = "ak8975", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak8975_of_match);
+
+static struct i2c_driver ak8975_driver = {
+ .driver = {
+ .name = "ak8975",
+ .of_match_table = ak8975_of_match,
+ },
+ .probe = ak8975_probe,
+ .remove = ak8975_remove,
+ .id_table = ak8975_id,
+};
+module_i2c_driver(ak8975_driver);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("AK8975 magnetometer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/inv_compass/Kconfig b/drivers/iio/magnetometer/inv_compass/Kconfig
new file mode 100644
index 00000000..ea1ef709
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/Kconfig
@@ -0,0 +1,25 @@
+#
+# Kconfig for Invensense IIO compass drivers of 3rd party compass devices.
+#
+
+# Yamaha YAS530/YAS532/YAS533
+config INV_YAS53X_IIO
+ tristate "Invensense IIO driver for Yamaha YAS530/YAS532/YAS533 compass"
+ depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF
+ default n
+ help
+ This driver supports the Yamaha YAS530/YAS532/YAS533. It is the Invensense
+ implementation of YAS53x series compass devices.
+ This driver can be built as a module. The module will be called
+ inv_yas53x_iio.
+
+# Aichi AMI306
+config INV_AMI306_IIO
+ tristate "Invensense IIO driver for Aichi AMI306 compass"
+ depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF
+ default n
+ help
+ This driver supports the Aichi AMI306 compass. It is the Invensense
+ IIO implementation for the AMI306 compass device.
+ This driver can be built as a module. The module will be called
+ inv-ami306-iio.
diff --git a/drivers/iio/magnetometer/inv_compass/Makefile b/drivers/iio/magnetometer/inv_compass/Makefile
new file mode 100644
index 00000000..adc7dd93
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/Makefile
@@ -0,0 +1,25 @@
+#
+# Makefile for Invensense IIO compass drivers of 3rd party compass devices.
+#
+
+# Yamaha YAS530/YAS532/YAS533
+obj-$(CONFIG_INV_YAS53X_IIO) += inv_yas53x.o
+
+inv_yas53x-objs := inv_yas53x_core.o
+inv_yas53x-objs += inv_yas53x_ring.o
+inv_yas53x-objs += inv_yas53x_trigger.o
+
+CFLAGS_inv_yas53x_core.o += -Idrivers/staging/iio
+CFLAGS_inv_yas53x_ring.o += -Idrivers/staging/iio
+CFLAGS_inv_yas53x_trigger.o += -Idrivers/staging/iio
+
+# Aichi AMI306
+obj-$(CONFIG_INV_AMI306_IIO) += inv-ami306-iio.o
+
+inv-ami306-iio-objs := inv_ami306_core.o
+inv-ami306-iio-objs += inv_ami306_ring.o
+inv-ami306-iio-objs += inv_ami306_trigger.o
+
+CFLAGS_inv_ami306_core.o += -Idrivers/staging/iio
+CFLAGS_inv_ami306_ring.o += -Idrivers/staging/iio
+CFLAGS_inv_ami306_trigger.o += -Idrivers/staging/iio
diff --git a/drivers/iio/magnetometer/inv_compass/README b/drivers/iio/magnetometer/inv_compass/README
new file mode 100644
index 00000000..c5445e5b
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/README
@@ -0,0 +1,176 @@
+Kernel driver
+Author: Invensense <http://invensense.com>
+
+Table of Contents:
+==================
+- Description
+- Integrating the Driver in the Linux Kernel
+- Board and Platform Data
+ > Platform Data
+- Board File Modifications for compass
+ > AMI306
+ > YAS530/532/533
+- IIO Subsystem
+ > Communicating with the Driver in Userspace
+- Streaming Data to an Userspace Application
+- Test Applications
+ > Running Test Applications with AMI306 or YAS53x
+
+Description
+===========
+This document describes how to install the Invensense device driver for AMI306
+and YAS53x series compass chip into a Linux kernel. The Invensense driver
+currently supports the following sensors:
+- AMI306
+- YAS530
+- YAS532
+- YAS533
+
+Please refer to the appropriate product specification
+document for further information regarding the slave address.
+
+The following files are included in this package:
+- Kconfig
+- Makefile
+- inv_ami306_core.c
+- inv_ami306_ring.c
+- inv_ami306_trigger.c
+- inv_ami306_iio.h
+- inv_yas53x_core.c
+- inv_yas53x_ring.c
+- inv_yas53x_trigger.c
+- inv_yas53x_iio.h
+
+Integrating the Driver in the Linux Kernel
+==========================================
+Please add the files as follows:
+- Add all above files to drivers/staging/iio/magnetometer/inv_compass
+(another directory is acceptable, but this is the recommended destination)
+
+In order to see the driver in menuconfig when building the kernel, please
+make modifications as shown below:
+
+ modify "drivers/staging/iio/magnetometer/Kconfig" with:
+ >> source "drivers/staging/iio/magnetometer/inv_compass/Kconfig"
+
+ modify "drivers/staging/iio/magnetometer/Makefile" with:
+ >> obj-y += inv_compass/
+
+
+Board and Platform Data
+=======================
+In order to recognize the Invensense device on the I2C bus, the board file must
+be modified.
+The i2c_board_info instance must be defined as shown below.
+
+Platform Data
+-------------
+The platform data (orientation matrix and secondary bus configurations) must be
+modified as show below, according to your particular platform configuration.
+
+Board File Modifications for Secondary I2C Configuration
+========================================================
+For the Panda Board, the board file can be found at
+arch/arm/mach-omap2/board-omap4panda.c.
+Please modify the pertinent baord file in your system according to the examples
+shown below:
+
+AMI306
+-------------------------------------------------
+static struct mpu_platform_data compass_data = {
+ .orientation = { 0, 0, 1,
+ 0, 1, 0,
+ 1, 0, 0 },
+};
+
+static struct i2c_board_info __initdata chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("ami306", 0x0E),
+ .platform_data = &compass_data,
+ },
+};
+
+YAS53x(Use YAS532 as an example)
+-------------------------------------------------
+static struct mpu_platform_data compass_data = {
+ .orientation = { 0, -1, 0,
+ 1, 0, 0,
+ 0, 0, 1 },
+};
+
+static struct i2c_board_info __initdata compass_board_info[] = {
+ {
+ I2C_BOARD_INFO("yas532", 0x2E),
+ .platform_data = &compass_data,
+ },
+};
+
+IIO subsystem
+=============
+A successful installation will create the following two new directories under
+/sys/bus/iio/devices:
+ - iio:device0
+ - trigger0
+
+Also, a new file, "iio:device0", will be created in the /dev/ diretory.
+(if you have more than one IIO device, the file will be named "iio:deviceX",
+where X is a number)
+
+
+Communicating with the Driver in Userspace
+------------------------------------------
+The driver generates several files in sysfs upon installation.
+These files are used to communicate with the driver. The files can be found
+at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above).
+
+A brief description of the pertinent files for each Invensense device is shown
+below:
+
+AMI306
+--------
+compass_matrix (read-only)
+--show the orientation matrix obtained from the board file.
+
+sampling_frequency(read and write)
+--show and change the sampling rate of the sensor.
+
+YAS53x
+---------------------
+YAS53x has all the attributes AMI306 has. It has one more additional attribute:
+
+overunderflow(read-only)
+--value 1 shows an overflow or underflow happens. Need to write into it to make
+ it zero.
+
+Streaming Data to an Userspace Application
+==========================================
+When streaming data to an userspace application, we recommend that you access
+compass data via /dev/iio:device0.
+
+Please follow the steps below to read data at a constant rate from the driver:
+
+1. Write the desired output rate to sampling_frequency.
+2. Write 1 to enable to turn on the event.
+3. Read /dev/iio:device0 to get a string of gyro/accel/compass data.
+4. Parse this string to obtain each compass element.
+
+Test Applications
+=================
+A test application is located under software/simple_apps/mpu_iio.
+This application is stand-alone in that it cannot be run concurrently with other
+entities trying to access the device node(s) or sysfs entries; in particular,
+the
+
+Running Test Applications
+---------------------------------------------------------
+To run test applications with AMI306 or YAS53x devices,
+please use the following commands:
+
+1. for ami306:
+ mpu_iio -n ami306 -c 10 -l 3
+
+2. for yas532:
+ mpu_iio -n yas532 -c 10 -l 3
+
+Please use mpu_iio.c and iio_utils.h as example code for your development
+purposes.
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_core.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_core.c
new file mode 100644
index 00000000..612ba72b
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_core.c
@@ -0,0 +1,570 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_ami306_core.c
+ * @brief Invensense implementation for AMI306
+ * @details This driver currently works for the AMI306
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_ami306_iio.h"
+#include "sysfs.h"
+#include "inv_test/inv_counters.h"
+
+static unsigned char late_initialize = true;
+
+s32 i2c_write(const struct i2c_client *client,
+ u8 command, u8 length, const u8 *values)
+{
+ INV_I2C_INC_COMPASSWRITE(3);
+ return i2c_smbus_write_i2c_block_data(client, command, length, values);
+}
+
+s32 i2c_read(const struct i2c_client *client,
+ u8 command, u8 length, u8 *values)
+{
+ INV_I2C_INC_COMPASSWRITE(3);
+ INV_I2C_INC_COMPASSREAD(length);
+ return i2c_smbus_read_i2c_block_data(client, command, length, values);
+}
+
+static int ami306_read_param(struct inv_ami306_state_s *st)
+{
+ int result = 0;
+ unsigned char regs[AMI_PARAM_LEN];
+ struct ami_sensor_parametor *param = &st->param;
+
+ result = i2c_read(st->i2c, REG_AMI_SENX,
+ AMI_PARAM_LEN, regs);
+ if (result < 0)
+ return result;
+
+ /* Little endian 16 bit registers */
+ param->m_gain.x = le16_to_cpup((__le16 *)(&regs[0]));
+ param->m_gain.y = le16_to_cpup((__le16 *)(&regs[2]));
+ param->m_gain.z = le16_to_cpup((__le16 *)(&regs[4]));
+
+ param->m_interference.xy = regs[7];
+ param->m_interference.xz = regs[6];
+ param->m_interference.yx = regs[9];
+ param->m_interference.yz = regs[8];
+ param->m_interference.zx = regs[11];
+ param->m_interference.zy = regs[10];
+
+ param->m_offset.x = AMI_STANDARD_OFFSET;
+ param->m_offset.y = AMI_STANDARD_OFFSET;
+ param->m_offset.z = AMI_STANDARD_OFFSET;
+
+ param->m_gain_cor.x = AMI_GAIN_COR_DEFAULT;
+ param->m_gain_cor.y = AMI_GAIN_COR_DEFAULT;
+ param->m_gain_cor.z = AMI_GAIN_COR_DEFAULT;
+
+ return 0;
+}
+
+static int ami306_write_offset(const struct i2c_client *client,
+ unsigned char *fine)
+{
+ int result = 0;
+ unsigned char dat[3];
+ dat[0] = (0x7f & fine[0]);
+ dat[1] = 0;
+ result = i2c_write(client, REG_AMI_OFFX, 2, dat);
+ dat[0] = (0x7f & fine[1]);
+ dat[1] = 0;
+ result = i2c_write(client, REG_AMI_OFFY, 2, dat);
+ dat[0] = (0x7f & fine[2]);
+ dat[1] = 0;
+ result = i2c_write(client, REG_AMI_OFFZ, 2, dat);
+
+ return result;
+}
+
+static int ami306_wait_data_ready(struct inv_ami306_state_s *st,
+ unsigned long usecs, unsigned long times)
+{
+ int result = 0;
+ unsigned char buf;
+
+ for (; 0 < times; --times) {
+ udelay(usecs);
+ result = i2c_read(st->i2c, REG_AMI_STA1, 1, &buf);
+ if (result < 0)
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+ if (buf & AMI_STA1_DRDY_BIT)
+ return 0;
+ else if (buf & AMI_STA1_DOR_BIT)
+ return INV_ERROR_COMPASS_DATA_OVERFLOW;
+ }
+
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+}
+int ami306_read_raw_data(struct inv_ami306_state_s *st,
+ short dat[3])
+{
+ int result;
+ unsigned char buf[6];
+ result = i2c_read(st->i2c, REG_AMI_DATAX, sizeof(buf), buf);
+ if (result < 0)
+ return result;
+ dat[0] = le16_to_cpup((__le16 *)(&buf[0]));
+ dat[1] = le16_to_cpup((__le16 *)(&buf[2]));
+ dat[2] = le16_to_cpup((__le16 *)(&buf[4]));
+
+ return 0;
+}
+
+#define AMI_WAIT_DATAREADY_RETRY 3 /* retry times */
+#define AMI_DRDYWAIT 800 /* u(micro) sec */
+static int ami306_force_measurement(struct inv_ami306_state_s *st,
+ short ver[3])
+{
+ int result;
+ int status;
+ char buf;
+ buf = AMI_CTRL3_FORCE_BIT;
+ result = i2c_write(st->i2c, REG_AMI_CTRL3, 1, &buf);
+ if (result < 0)
+ return result;
+
+ result = ami306_wait_data_ready(st,
+ AMI_DRDYWAIT, AMI_WAIT_DATAREADY_RETRY);
+ if (result && result != INV_ERROR_COMPASS_DATA_OVERFLOW)
+ return result;
+ /* READ DATA X,Y,Z */
+ status = ami306_read_raw_data(st, ver);
+ if (status)
+ return status;
+
+ return result;
+}
+
+static int ami306_initial_b0_adjust(struct inv_ami306_state_s *st)
+{
+ int result;
+ unsigned char fine[3] = { 0 };
+ short data[3];
+ int diff[3] = { 0x7fff, 0x7fff, 0x7fff };
+ int fn = 0;
+ int ax = 0;
+ unsigned char buf[3];
+
+ buf[0] = AMI_CTRL2_DREN;
+ result = i2c_write(st->i2c, REG_AMI_CTRL2, 1, buf);
+ if (result)
+ return result;
+
+ buf[0] = AMI_CTRL4_HS & 0xFF;
+ buf[1] = (AMI_CTRL4_HS >> 8) & 0xFF;
+ result = i2c_write(st->i2c, REG_AMI_CTRL4, 2, buf);
+ if (result < 0)
+ return result;
+
+ for (fn = 0; fn < AMI_FINE_MAX; ++fn) { /* fine 0 -> 95 */
+ fine[0] = fine[1] = fine[2] = fn;
+ result = ami306_write_offset(st->i2c, fine);
+ if (result)
+ return result;
+
+ result = ami306_force_measurement(st, data);
+ if (result)
+ return result;
+
+ for (ax = 0; ax < 3; ax++) {
+ /* search point most close to zero. */
+ if (diff[ax] > abs(data[ax])) {
+ st->fine[ax] = fn;
+ diff[ax] = abs(data[ax]);
+ }
+ }
+ }
+ result = ami306_write_offset(st->i2c, st->fine);
+ if (result)
+ return result;
+
+ /* Software Reset */
+ buf[0] = AMI_CTRL3_SRST_BIT;
+ result = i2c_write(st->i2c, REG_AMI_CTRL3, 1, buf);
+ if (result < 0)
+ return result;
+ else
+ return 0;
+}
+
+static int ami306_start_sensor(struct inv_ami306_state_s *st)
+{
+ int result = 0;
+ unsigned char buf[2];
+
+ /* Step 1 */
+ buf[0] = (AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
+ result = i2c_write(st->i2c, REG_AMI_CTRL1, 1, buf);
+ if (result < 0)
+ return result;
+ /* Step 2 */
+ buf[0] = AMI_CTRL2_DREN;
+ result = i2c_write(st->i2c, REG_AMI_CTRL2, 1, buf);
+ if (result < 0)
+ return result;
+ /* Step 3 */
+ buf[0] = (AMI_CTRL4_HS & 0xFF);
+ buf[1] = (AMI_CTRL4_HS >> 8) & 0xFF;
+
+ result = i2c_write(st->i2c, REG_AMI_CTRL4, 2, buf);
+ if (result < 0)
+ return result;
+
+ /* Step 4 */
+ result = ami306_write_offset(st->i2c, st->fine);
+
+ return result;
+}
+
+int set_ami306_enable(struct iio_dev *indio_dev, int state)
+{
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ int result;
+ char buf;
+
+ buf = (AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
+ result = i2c_write(st->i2c, REG_AMI_CTRL1, 1, &buf);
+ if (result < 0)
+ return result;
+
+ result = ami306_read_param(st);
+ if (result)
+ return result;
+ if (late_initialize) {
+ result = ami306_initial_b0_adjust(st);
+ if (result)
+ return result;
+ late_initialize = false;
+ }
+ result = ami306_start_sensor(st);
+ if (result)
+ return result;
+ buf = AMI_CTRL3_FORCE_BIT;
+ st->timestamp = iio_get_time_ns();
+ result = i2c_write(st->i2c, REG_AMI_CTRL3, 1, &buf);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+/**
+ * ami306_read_raw() - read raw method.
+ */
+static int ami306_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask) {
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case 0:
+ if (!(iio_buffer_enabled(indio_dev)))
+ return -EINVAL;
+ if (chan->type == IIO_MAGN) {
+ *val = st->compass_data[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_MAGN) {
+ *val = AMI_SCALE;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * inv_compass_matrix_show() - show orientation matrix
+ */
+static ssize_t inv_compass_matrix_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ signed char *m;
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ m = st->plat_data.orientation;
+ return sprintf(buf,
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+static ssize_t ami306_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (0 == data)
+ return -EINVAL;
+ /* transform rate to delay in ms */
+ data = 1000 / data;
+ if (data > AMI_MAX_DELAY)
+ data = AMI_MAX_DELAY;
+ if (data < AMI_MIN_DELAY)
+ data = AMI_MIN_DELAY;
+ st->delay = data;
+ return count;
+}
+
+static ssize_t ami306_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ /* transform delay in ms to rate */
+ return sprintf(buf, "%d\n", 1000 / st->delay);
+}
+
+
+static void ami306_work_func(struct work_struct *work)
+{
+ struct inv_ami306_state_s *st =
+ container_of((struct delayed_work *)work,
+ struct inv_ami306_state_s, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ unsigned long delay = msecs_to_jiffies(st->delay);
+
+ mutex_lock(&indio_dev->mlock);
+ if (!(iio_buffer_enabled(indio_dev)))
+ goto error_ret;
+
+ st->timestamp = iio_get_time_ns();
+ schedule_delayed_work(&st->work, delay);
+ inv_read_ami306_fifo(indio_dev);
+ INV_I2C_INC_COMPASSIRQ();
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+}
+
+static const struct iio_chan_spec compass_channels[] = {
+ {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AMI306_SCAN_MAGN_X,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AMI306_SCAN_MAGN_Y,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AMI306_SCAN_MAGN_Z,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(INV_AMI306_SCAN_TIMESTAMP)
+};
+
+static DEVICE_ATTR(compass_matrix, S_IRUGO, inv_compass_matrix_show, NULL);
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, ami306_rate_show,
+ ami306_rate_store);
+
+static struct attribute *inv_ami306_attributes[] = {
+ &dev_attr_compass_matrix.attr,
+ &dev_attr_sampling_frequency.attr,
+ NULL,
+};
+static const struct attribute_group inv_attribute_group = {
+ .name = "ami306",
+ .attrs = inv_ami306_attributes
+};
+
+static const struct iio_info ami306_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ami306_read_raw,
+ .attrs = &inv_attribute_group,
+};
+
+/*constant IIO attribute */
+/**
+ * inv_ami306_probe() - probe function.
+ */
+static int inv_ami306_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_ami306_state_s *st;
+ struct iio_dev *indio_dev;
+ int result;
+ char data;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+ indio_dev = iio_allocate_device(sizeof(*st));
+ if (indio_dev == NULL) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->i2c = client;
+ st->plat_data =
+ *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+ st->delay = 10;
+
+ /* Make state variables available to all _show and _store functions. */
+ i2c_set_clientdata(client, indio_dev);
+ result = i2c_read(st->i2c, REG_AMI_WIA, 1, &data);
+ if (result < 0)
+ goto out_free;
+ if (data != DATA_WIA)
+ goto out_free;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = compass_channels;
+ indio_dev->num_channels = ARRAY_SIZE(compass_channels);
+ indio_dev->info = &ami306_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ result = inv_ami306_configure_ring(indio_dev);
+ if (result)
+ goto out_free;
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result)
+ goto out_unreg_ring;
+ result = inv_ami306_probe_trigger(indio_dev);
+ if (result)
+ goto out_remove_ring;
+
+ result = iio_device_register(indio_dev);
+ if (result)
+ goto out_remove_trigger;
+ INIT_DELAYED_WORK(&st->work, ami306_work_func);
+ pr_info("%s: Probe name %s\n", __func__, id->name);
+ return 0;
+out_remove_trigger:
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_ami306_remove_trigger(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_ami306_unconfigure_ring(indio_dev);
+out_free:
+ iio_free_device(indio_dev);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return -EIO;
+}
+
+/**
+ * inv_ami306_remove() - remove function.
+ */
+static int inv_ami306_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ cancel_delayed_work_sync(&st->work);
+ iio_device_unregister(indio_dev);
+ inv_ami306_remove_trigger(indio_dev);
+ iio_buffer_unregister(indio_dev);
+ inv_ami306_unconfigure_ring(indio_dev);
+ iio_free_device(indio_dev);
+
+ dev_info(&client->adapter->dev, "inv-ami306-iio module removed.\n");
+ return 0;
+}
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_ami306_id[] = {
+ {"ami306", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_ami306_id);
+
+static struct i2c_driver inv_ami306_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = inv_ami306_probe,
+ .remove = inv_ami306_remove,
+ .id_table = inv_ami306_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-ami306-iio",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init inv_ami306_init(void)
+{
+ int result = i2c_add_driver(&inv_ami306_driver);
+ if (result) {
+ pr_err("%s failed\n", __func__);
+ return result;
+ }
+ return 0;
+}
+
+static void __exit inv_ami306_exit(void)
+{
+ i2c_del_driver(&inv_ami306_driver);
+}
+
+module_init(inv_ami306_init);
+module_exit(inv_ami306_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv-ami306-iio");
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h b/drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h
new file mode 100644
index 00000000..fa4f4ee1
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h
@@ -0,0 +1,159 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_ami306_iio.h
+ * @brief Struct definitions for the Invensense implementation
+ * of ami306 driver.
+ */
+
+#ifndef _INV_GYRO_H_
+#define _INV_GYRO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include <linux/mpu.h>
+
+#include "iio.h"
+#include "buffer.h"
+#include "trigger.h"
+
+/** axis sensitivity(gain) calibration parameter information */
+struct ami_vector3d {
+ signed short x; /**< X-axis */
+ signed short y; /**< Y-axis */
+ signed short z; /**< Z-axis */
+};
+
+/** axis interference information */
+struct ami_interference {
+ /**< Y-axis magnetic field for X-axis correction value */
+ signed short xy;
+ /**< Z-axis magnetic field for X-axis correction value */
+ signed short xz;
+ /**< X-axis magnetic field for Y-axis correction value */
+ signed short yx;
+ /**< Z-axis magnetic field for Y-axis correction value */
+ signed short yz;
+ /**< X-axis magnetic field for Z-axis correction value */
+ signed short zx;
+ /**< Y-axis magnetic field for Z-axis correction value */
+ signed short zy;
+};
+
+/** sensor calibration Parameter information */
+struct ami_sensor_parametor {
+ /**< geomagnetic field sensor gain */
+ struct ami_vector3d m_gain;
+ /**< geomagnetic field sensor gain correction parameter */
+ struct ami_vector3d m_gain_cor;
+ /**< geomagnetic field sensor offset */
+ struct ami_vector3d m_offset;
+ /**< geomagnetic field sensor axis interference parameter */
+ struct ami_interference m_interference;
+};
+
+/**
+ * struct inv_ami306_state_s - Driver state variables.
+ * @plat_data: board file platform data.
+ * @i2c: i2c client handle.
+ * @trig: not used. for compatibility.
+ * @param: ami specific sensor data.
+ * @work: work data structure.
+ * @delay: delay between each scheduled work.
+ * @fine: fine tunign parameters.
+ * @compass_data: compass data store.
+ * @timestamp: time stamp.
+ */
+struct inv_ami306_state_s {
+ struct mpu_platform_data plat_data;
+ struct i2c_client *i2c;
+ struct iio_trigger *trig;
+ struct ami_sensor_parametor param;
+ struct delayed_work work;
+ int delay;
+ s8 fine[3];
+ short compass_data[3];
+ s64 timestamp;
+};
+/* scan element definition */
+enum inv_mpu_scan {
+ INV_AMI306_SCAN_MAGN_X,
+ INV_AMI306_SCAN_MAGN_Y,
+ INV_AMI306_SCAN_MAGN_Z,
+ INV_AMI306_SCAN_TIMESTAMP,
+};
+
+#define REG_AMI_WIA 0x0f
+#define REG_AMI_DATAX 0x10
+#define REG_AMI_STA1 0x18
+#define REG_AMI_CTRL1 0x1b
+#define REG_AMI_CTRL2 0x1c
+#define REG_AMI_CTRL3 0x1d
+#define REG_AMI_B0X 0x20
+#define REG_AMI_B0Y 0x22
+#define REG_AMI_B0Z 0x24
+#define REG_AMI_CTRL5 0x40
+#define REG_AMI_CTRL4 0x5c
+#define REG_AMI_TEMP 0x60
+#define REG_AMI_SENX 0x96
+#define REG_AMI_OFFX 0x6c
+#define REG_AMI_OFFY 0x72
+#define REG_AMI_OFFZ 0x78
+
+
+#define DATA_WIA 0x46
+#define AMI_CTRL1_PC1 0x80
+#define AMI_CTRL1_FS1_FORCE 0x02
+#define AMI_CTRL1_ODR1 0x10
+#define AMI_CTRL2_DREN 0x08
+#define AMI_CTRL2_DRP 0x04
+#define AMI_CTRL3_FORCE_BIT 0x40
+#define AMI_CTRL3_B0_LO_BIT 0x10
+#define AMI_CTRL3_SRST_BIT 0x80
+#define AMI_CTRL4_HS 0xa07e
+#define AMI_CTRL4_AB 0x0001
+#define AMI_STA1_DRDY_BIT 0x40
+#define AMI_STA1_DOR_BIT 0x20
+
+#define AMI_PARAM_LEN 12
+#define AMI_STANDARD_OFFSET 0x800
+#define AMI_GAIN_COR_DEFAULT 1000
+#define AMI_FINE_MAX 96
+#define AMI_MAX_DELAY 1000
+#define AMI_MIN_DELAY 10
+#define AMI_SCALE (5461 * (1<<15))
+
+#define INV_ERROR_COMPASS_DATA_OVERFLOW (-1)
+#define INV_ERROR_COMPASS_DATA_NOT_READY (-2)
+
+
+int inv_ami306_configure_ring(struct iio_dev *indio_dev);
+void inv_ami306_unconfigure_ring(struct iio_dev *indio_dev);
+int inv_ami306_probe_trigger(struct iio_dev *indio_dev);
+void inv_ami306_remove_trigger(struct iio_dev *indio_dev);
+int set_ami306_enable(struct iio_dev *indio_dev, int state);
+int ami306_read_raw_data(struct inv_ami306_state_s *st,
+ short dat[3]);
+int inv_read_ami306_fifo(struct iio_dev *indio_dev);
+
+#endif /* #ifndef _INV_GYRO_H_ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c
new file mode 100644
index 00000000..ed91cf49
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c
@@ -0,0 +1,163 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_ami306_ring.c
+ * @brief Invensense implementation for AMI306
+ * @details This driver currently works for the AMI306
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+
+#include "iio.h"
+#include "kfifo_buf.h"
+#include "trigger_consumer.h"
+#include "sysfs.h"
+
+#include "inv_ami306_iio.h"
+
+static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d,
+ short *s, int scan_index) {
+ struct iio_buffer *ring = indio_dev->buffer;
+ int st;
+ int i, d_ind;
+ d_ind = 0;
+ for (i = 0; i < 3; i++) {
+ st = iio_scan_mask_query(indio_dev, ring, scan_index + i);
+ if (st) {
+ memcpy(&d[d_ind], &s[i], sizeof(s[i]));
+ d_ind += sizeof(s[i]);
+ }
+ }
+ return d_ind;
+}
+
+/**
+ * inv_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+int inv_read_ami306_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int result, status, d_ind;
+ char b;
+ char *tmp;
+ s64 tmp_buf[2];
+
+ result = i2c_smbus_read_i2c_block_data(st->i2c, REG_AMI_STA1, 1, &b);
+ if (result < 0)
+ goto end_session;
+ if (b & AMI_STA1_DRDY_BIT) {
+ status = ami306_read_raw_data(st, st->compass_data);
+ if (status) {
+ pr_err("error reading raw\n");
+ goto end_session;
+ }
+ tmp = (unsigned char *)tmp_buf;
+ d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data,
+ INV_AMI306_SCAN_MAGN_X);
+ if (ring->scan_timestamp)
+ tmp_buf[(d_ind + 7)/8] = st->timestamp;
+ ring->access->store_to(indio_dev->buffer, tmp, st->timestamp);
+ } else if (b & AMI_STA1_DOR_BIT)
+ pr_err("not ready\n");
+end_session:
+ b = AMI_CTRL3_FORCE_BIT;
+ result = i2c_smbus_write_i2c_block_data(st->i2c, REG_AMI_CTRL3, 1, &b);
+
+ return IRQ_HANDLED;
+}
+
+void inv_ami306_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_kfifo_free(indio_dev->buffer);
+};
+static int inv_ami306_postenable(struct iio_dev *indio_dev)
+{
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int result;
+
+ /* when all the outputs are disabled, even though buffer/enable is on,
+ do nothing */
+ if (!(iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_Z)))
+ return 0;
+
+ result = set_ami306_enable(indio_dev, true);
+ if (result)
+ return result;
+ schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay));
+
+ return 0;
+}
+
+static int inv_ami306_predisable(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *ring = indio_dev->buffer;
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ cancel_delayed_work_sync(&st->work);
+ clear_bit(INV_AMI306_SCAN_MAGN_X, ring->scan_mask);
+ clear_bit(INV_AMI306_SCAN_MAGN_Y, ring->scan_mask);
+ clear_bit(INV_AMI306_SCAN_MAGN_Z, ring->scan_mask);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_ami306_ring_setup_ops = {
+ .preenable = &iio_sw_buffer_preenable,
+ .postenable = &inv_ami306_postenable,
+ .predisable = &inv_ami306_predisable,
+};
+
+int inv_ami306_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret = 0;
+ struct iio_buffer *ring;
+
+ ring = iio_kfifo_allocate(indio_dev);
+ if (!ring) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ indio_dev->buffer = ring;
+ /* setup ring buffer */
+ ring->scan_timestamp = true;
+ indio_dev->setup_ops = &inv_ami306_ring_setup_ops;
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+ return 0;
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c
new file mode 100644
index 00000000..f7fe59ef
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_ami306_trigger.c
+ * @brief Invensense implementation for AMI306
+ * @details This driver currently works for the AMI306
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "iio.h"
+#include "sysfs.h"
+#include "trigger.h"
+#include "inv_ami306_iio.h"
+
+static const struct iio_trigger_ops inv_ami306_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+int inv_ami306_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ st->trig = iio_allocate_trigger("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ /* select default trigger */
+ st->trig->dev.parent = &st->i2c->dev;
+ st->trig->private_data = indio_dev;
+ st->trig->ops = &inv_ami306_trigger_ops;
+ ret = iio_trigger_register(st->trig);
+
+ /* select default trigger */
+ indio_dev->trig = st->trig;
+ if (ret)
+ goto error_free_trig;
+
+ return 0;
+
+error_free_trig:
+ iio_free_trigger(st->trig);
+error_ret:
+ return ret;
+}
+
+void inv_ami306_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_free_trigger(st->trig);
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c b/drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c
new file mode 100644
index 00000000..6af420bb
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c
@@ -0,0 +1,969 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_yas53x_core.c
+ * @brief Invensense implementation for yas530/yas532/yas533.
+ * @details This driver currently works for yas530/yas532/yas533.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_yas53x_iio.h"
+#include "sysfs.h"
+#include "inv_test/inv_counters.h"
+
+/* -------------------------------------------------------------------------- */
+static int Cx, Cy1, Cy2;
+static int /*a1, */ a2, a3, a4, a5, a6, a7, a8, a9;
+static int k;
+
+static u8 dx, dy1, dy2;
+static u8 d2, d3, d4, d5, d6, d7, d8, d9, d0;
+static u8 dck, ver;
+
+/**
+ * inv_serial_read() - Read one or more bytes from the device registers.
+ * @st: Device driver instance.
+ * @reg: First device register to be read from.
+ * @length: Number of bytes to read.
+ * @data: Data read from device.
+ * NOTE: The slave register will not increment when reading from the FIFO.
+ */
+int inv_serial_read(struct inv_compass_state *st, u8 reg, u16 length, u8 *data)
+{
+ int result;
+ INV_I2C_INC_COMPASSWRITE(3);
+ INV_I2C_INC_COMPASSREAD(length);
+ result = i2c_smbus_read_i2c_block_data(st->client, reg, length, data);
+ if (result != length) {
+ if (result < 0)
+ return result;
+ else
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * inv_serial_single_write() - Write a byte to a device register.
+ * @st: Device driver instance.
+ * @reg: Device register to be written to.
+ * @data: Byte to write to device.
+ */
+int inv_serial_single_write(struct inv_compass_state *st, u8 reg, u8 data)
+{
+ u8 d[1];
+ d[0] = data;
+ INV_I2C_INC_COMPASSWRITE(3);
+
+ return i2c_smbus_write_i2c_block_data(st->client, reg, 1, d);
+}
+
+static int set_hardware_offset(struct inv_compass_state *st,
+ char offset_x, char offset_y1, char offset_y2)
+{
+ char data;
+ int result = 0;
+
+ data = offset_x & 0x3f;
+ result = inv_serial_single_write(st, YAS530_REGADDR_OFFSET_X, data);
+ if (result)
+ return result;
+
+ data = offset_y1 & 0x3f;
+ result = inv_serial_single_write(st, YAS530_REGADDR_OFFSET_Y1, data);
+ if (result)
+ return result;
+
+ data = offset_y2 & 0x3f;
+ result = inv_serial_single_write(st, YAS530_REGADDR_OFFSET_Y2, data);
+ return result;
+}
+
+static int set_measure_command(struct inv_compass_state *st)
+{
+ int result = 0;
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_MEASURE_COMMAND, 0x01);
+ return result;
+}
+
+static int measure_normal(struct inv_compass_state *st,
+ int *busy, unsigned short *t,
+ unsigned short *x, unsigned short *y1,
+ unsigned short *y2)
+{
+ int result;
+ ktime_t sleeptime;
+ result = set_measure_command(st);
+ sleeptime = ktime_set(0, 2 * NSEC_PER_MSEC);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_hrtimeout(&sleeptime, HRTIMER_MODE_REL);
+
+ result = st->read_data(st, busy, t, x, y1, y2);
+
+ return result;
+}
+
+static int measure_int(struct inv_compass_state *st,
+ int *busy, unsigned short *t,
+ unsigned short *x, unsigned short *y1,
+ unsigned short *y2)
+{
+ int result;
+ if (st->first_read_after_reset) {
+ st->first_read_after_reset = 0;
+ result = 1;
+ } else {
+ result = st->read_data(st, busy, t, x, y1, y2);
+ }
+ result |= set_measure_command(st);
+
+ return result;
+}
+
+static int yas530_read_data(struct inv_compass_state *st,
+ int *busy, u16 *t, u16 *x, u16 *y1, u16 *y2)
+{
+ u8 data[8];
+ u16 b, to, xo, y1o, y2o;
+ int result;
+
+ result = inv_serial_read(st,
+ YAS530_REGADDR_MEASURE_DATA, 8, data);
+ if (result)
+ return result;
+
+ b = (data[0] >> 7) & 0x01;
+ to = (s16)(((data[0] << 2) & 0x1fc) | ((data[1] >> 6) & 0x03));
+ xo = (s16)(((data[2] << 5) & 0xfe0) | ((data[3] >> 3) & 0x1f));
+ y1o = (s16)(((data[4] << 5) & 0xfe0) | ((data[5] >> 3) & 0x1f));
+ y2o = (s16)(((data[6] << 5) & 0xfe0) | ((data[7] >> 3) & 0x1f));
+
+ *busy = b;
+ *t = to;
+ *x = xo;
+ *y1 = y1o;
+ *y2 = y2o;
+
+ return 0;
+}
+
+static int yas532_533_read_data(struct inv_compass_state *st,
+ int *busy, u16 *t, u16 *x, u16 *y1, u16 *y2)
+{
+ u8 data[8];
+ u16 b, to, xo, y1o, y2o;
+ int result;
+
+ result = inv_serial_read(st,
+ YAS530_REGADDR_MEASURE_DATA, 8, data);
+ if (result)
+ return result;
+
+ b = (data[0] >> 7) & 0x01;
+ to = (s16)((((s32)data[0] << 3) & 0x3f8) | ((data[1] >> 5) & 0x07));
+ xo = (s16)((((s32)data[2] << 6) & 0x1fc0) | ((data[3] >> 2) & 0x3f));
+ y1o = (s16)((((s32)data[4] << 6) & 0x1fc0) | ((data[5] >> 2) & 0x3f));
+ y2o = (s16)((((s32)data[6] << 6) & 0x1fc0) | ((data[7] >> 2) & 0x3f));
+
+ *busy = b;
+ *t = to;
+ *x = xo;
+ *y1 = y1o;
+ *y2 = y2o;
+
+ return 0;
+}
+
+static int check_offset(struct inv_compass_state *st,
+ char offset_x, char offset_y1, char offset_y2,
+ int *flag_x, int *flag_y1, int *flag_y2)
+{
+ int result;
+ int busy;
+ short t, x, y1, y2;
+
+ result = set_hardware_offset(st, offset_x, offset_y1, offset_y2);
+ if (result)
+ return result;
+ result = measure_normal(st, &busy, &t, &x, &y1, &y2);
+ if (result)
+ return result;
+ *flag_x = 0;
+ *flag_y1 = 0;
+ *flag_y2 = 0;
+
+ if (x > st->center)
+ *flag_x = 1;
+ if (y1 > st->center)
+ *flag_y1 = 1;
+ if (y2 > st->center)
+ *flag_y2 = 1;
+ if (x < st->center)
+ *flag_x = -1;
+ if (y1 < st->center)
+ *flag_y1 = -1;
+ if (y2 < st->center)
+ *flag_y2 = -1;
+
+ return result;
+}
+
+static int measure_and_set_offset(struct inv_compass_state *st,
+ char *offset)
+{
+ int i;
+ int result = 0;
+ char offset_x = 0, offset_y1 = 0, offset_y2 = 0;
+ int flag_x = 0, flag_y1 = 0, flag_y2 = 0;
+ static const int correct[5] = {16, 8, 4, 2, 1};
+
+ for (i = 0; i < 5; i++) {
+ result = check_offset(st,
+ offset_x, offset_y1, offset_y2,
+ &flag_x, &flag_y1, &flag_y2);
+ if (result)
+ return result;
+ if (flag_x)
+ offset_x += flag_x * correct[i];
+ if (flag_y1)
+ offset_y1 += flag_y1 * correct[i];
+ if (flag_y2)
+ offset_y2 += flag_y2 * correct[i];
+ }
+
+ result = set_hardware_offset(st, offset_x, offset_y1, offset_y2);
+ if (result)
+ return result;
+ offset[0] = offset_x;
+ offset[1] = offset_y1;
+ offset[2] = offset_y2;
+
+ return result;
+}
+
+static void coordinate_conversion(short x, short y1, short y2, short t,
+ int *xo, int *yo, int *zo)
+{
+ int sx, sy1, sy2, sy, sz;
+ int hx, hy, hz;
+
+ sx = x - (Cx * t) / 100;
+ sy1 = y1 - (Cy1 * t) / 100;
+ sy2 = y2 - (Cy2 * t) / 100;
+
+ sy = sy1 - sy2;
+ sz = -sy1 - sy2;
+
+ hx = k * ((100 * sx + a2 * sy + a3 * sz) / 10);
+ hy = k * ((a4 * sx + a5 * sy + a6 * sz) / 10);
+ hz = k * ((a7 * sx + a8 * sy + a9 * sz) / 10);
+
+ *xo = hx;
+ *yo = hy;
+ *zo = hz;
+}
+
+static int get_cal_data_yas532_533(struct inv_compass_state *st)
+{
+ u8 data[YAS_YAS532_533_CAL_DATA_SIZE];
+ int result;
+
+ result = inv_serial_read(st, YAS530_REGADDR_CAL,
+ YAS_YAS532_533_CAL_DATA_SIZE, data);
+ if (result)
+ return result;
+ /* CAL data Second Read */
+ result = inv_serial_read(st, YAS530_REGADDR_CAL,
+ YAS_YAS532_533_CAL_DATA_SIZE, data);
+ if (result)
+ return result;
+
+ dx = data[0];
+ dy1 = data[1];
+ dy2 = data[2];
+ d2 = (data[3] >> 2) & 0x03f;
+ d3 = (u8)(((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03));
+ d4 = (u8)(data[4] & 0x3f);
+ d5 = (data[5] >> 2) & 0x3f;
+ d6 = (u8)(((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f));
+ d7 = (u8)(((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07));
+ d8 = (u8)(((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01));
+ d9 = (u8)(((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01));
+ d0 = (u8)((data[9] >> 2) & 0x1f);
+ dck = (u8)(((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01));
+ ver = (u8)((data[13]) & 0x01);
+
+ Cx = dx * 10 - 1280;
+ Cy1 = dy1 * 10 - 1280;
+ Cy2 = dy2 * 10 - 1280;
+ a2 = d2 - 32;
+ a3 = d3 - 8;
+ a4 = d4 - 32;
+ a5 = d5 + 38;
+ a6 = d6 - 32;
+ a7 = d7 - 64;
+ a8 = d8 - 32;
+ a9 = d9;
+ k = d0;
+
+ return 0;
+}
+
+static int get_cal_data_yas530(struct inv_compass_state *st)
+{
+ u8 data[YAS_YAS530_CAL_DATA_SIZE];
+ int result;
+ /* CAL data read */
+ result = inv_serial_read(st, YAS530_REGADDR_CAL,
+ YAS_YAS530_CAL_DATA_SIZE, data);
+ if (result)
+ return result;
+ /* CAL data Second Read */
+ result = inv_serial_read(st, YAS530_REGADDR_CAL,
+ YAS_YAS530_CAL_DATA_SIZE, data);
+ if (result)
+ return result;
+ /*Cal data */
+ dx = data[0];
+ dy1 = data[1];
+ dy2 = data[2];
+ d2 = (data[3] >> 2) & 0x03f;
+ d3 = ((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03);
+ d4 = data[4] & 0x3f;
+ d5 = (data[5] >> 2) & 0x3f;
+ d6 = ((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f);
+ d7 = ((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07);
+ d8 = ((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01);
+ d9 = ((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01);
+ d0 = (data[9] >> 2) & 0x1f;
+ dck = ((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01);
+ ver = (u8)((data[15]) & 0x03);
+
+ /*Correction Data */
+ Cx = (int)dx * 6 - 768;
+ Cy1 = (int)dy1 * 6 - 768;
+ Cy2 = (int)dy2 * 6 - 768;
+ a2 = (int)d2 - 32;
+ a3 = (int)d3 - 8;
+ a4 = (int)d4 - 32;
+ a5 = (int)d5 + 38;
+ a6 = (int)d6 - 32;
+ a7 = (int)d7 - 64;
+ a8 = (int)d8 - 32;
+ a9 = (int)d9;
+ k = (int)d0 + 10;
+
+ return 0;
+}
+
+
+static void thresh_filter_init(struct yas_thresh_filter *thresh_filter,
+ int threshold)
+{
+ thresh_filter->threshold = threshold;
+ thresh_filter->last = 0;
+}
+
+static void
+adaptive_filter_init(struct yas_adaptive_filter *adap_filter, int len,
+ int noise)
+{
+ int i;
+
+ adap_filter->num = 0;
+ adap_filter->index = 0;
+ adap_filter->filter_noise = noise;
+ adap_filter->filter_len = len;
+
+ for (i = 0; i < adap_filter->filter_len; ++i)
+ adap_filter->sequence[i] = 0;
+}
+
+static void yas_init_adap_filter(struct inv_compass_state *st)
+{
+ struct yas_filter *f;
+ int i;
+ int noise[] = {YAS_MAG_DEFAULT_FILTER_NOISE_X,
+ YAS_MAG_DEFAULT_FILTER_NOISE_Y,
+ YAS_MAG_DEFAULT_FILTER_NOISE_Z};
+
+ f = &st->filter;
+ f->filter_len = YAS_MAG_DEFAULT_FILTER_LEN;
+ for (i = 0; i < 3; i++)
+ f->filter_noise[i] = noise[i];
+
+ for (i = 0; i < 3; i++) {
+ adaptive_filter_init(&f->adap_filter[i], f->filter_len,
+ f->filter_noise[i]);
+ thresh_filter_init(&f->thresh_filter[i], f->filter_thresh);
+ }
+}
+
+int yas53x_resume(struct inv_compass_state *st)
+{
+ int result = 0;
+
+ unsigned char dummyData = 0x00;
+ unsigned char read_reg[1];
+
+ /* =============================================== */
+
+ /* Step 1 - Test register initialization */
+ dummyData = 0x00;
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_TEST1, dummyData);
+ if (result)
+ return result;
+ result =
+ inv_serial_single_write(st,
+ YAS530_REGADDR_TEST2, dummyData);
+ if (result)
+ return result;
+ /* Device ID read */
+ result = inv_serial_read(st,
+ YAS530_REGADDR_DEVICE_ID, 1, read_reg);
+
+ /*Step 2 Read the CAL register */
+ st->get_cal_data(st);
+
+ /*Obtain the [49:47] bits */
+ dck &= 0x07;
+
+ /*Step 3 : Storing the CONFIG with the CLK value */
+ dummyData = 0x00 | (dck << 2);
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_CONFIG, dummyData);
+ if (result)
+ return result;
+ /*Step 4 : Set Acquisition Interval Register */
+ dummyData = 0x00;
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_MEASURE_INTERVAL,
+ dummyData);
+ if (result)
+ return result;
+
+ /*Step 5 : Reset Coil */
+ dummyData = 0x00;
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_ACTUATE_INIT_COIL,
+ dummyData);
+ if (result)
+ return result;
+ /* Offset Measurement and Set */
+ result = measure_and_set_offset(st, st->offset);
+ if (result)
+ return result;
+ st->first_measure_after_reset = 1;
+ st->first_read_after_reset = 1;
+ st->reset_timer = 0;
+
+ yas_init_adap_filter(st);
+
+ return result;
+}
+
+static int inv_check_range(struct inv_compass_state *st, s16 x, s16 y1, s16 y2)
+{
+ int result = 0;
+
+ if (x == 0)
+ result |= 0x01;
+ if (x == st->overflow_bound)
+ result |= 0x02;
+ if (y1 == 0)
+ result |= 0x04;
+ if (y1 == st->overflow_bound)
+ result |= 0x08;
+ if (y2 == 0)
+ result |= 0x10;
+ if (y2 == st->overflow_bound)
+ result |= 0x20;
+
+ return result;
+}
+static int square(int data)
+{
+ return data * data;
+}
+
+static int
+adaptive_filter_filter(struct yas_adaptive_filter *adap_filter, int in)
+{
+ int avg, sum;
+ int i;
+
+ if (adap_filter->filter_len == 0)
+ return in;
+ if (adap_filter->num < adap_filter->filter_len) {
+ adap_filter->sequence[adap_filter->index++] = in / 100;
+ adap_filter->num++;
+ return in;
+ }
+ if (adap_filter->filter_len <= adap_filter->index)
+ adap_filter->index = 0;
+ adap_filter->sequence[adap_filter->index++] = in / 100;
+
+ avg = 0;
+ for (i = 0; i < adap_filter->filter_len; i++)
+ avg += adap_filter->sequence[i];
+ avg /= adap_filter->filter_len;
+
+ sum = 0;
+ for (i = 0; i < adap_filter->filter_len; i++)
+ sum += square(avg - adap_filter->sequence[i]);
+ sum /= adap_filter->filter_len;
+
+ if (sum <= adap_filter->filter_noise)
+ return avg * 100;
+
+ return ((in/100 - avg) * (sum - adap_filter->filter_noise) / sum + avg)
+ * 100;
+}
+
+static int
+thresh_filter_filter(struct yas_thresh_filter *thresh_filter, int in)
+{
+ if (in < thresh_filter->last - thresh_filter->threshold
+ || thresh_filter->last
+ + thresh_filter->threshold < in) {
+ thresh_filter->last = in;
+ return in;
+ } else {
+ return thresh_filter->last;
+ }
+}
+
+static void
+filter_filter(struct yas_filter *d, int *orig, int *filtered)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ filtered[i] = adaptive_filter_filter(&d->adap_filter[i],
+ orig[i]);
+ filtered[i] = thresh_filter_filter(&d->thresh_filter[i],
+ filtered[i]);
+ }
+}
+
+int yas53x_read(struct inv_compass_state *st, short rawfixed[3],
+ int *overunderflow)
+{
+ int result = 0;
+
+ int busy, i, ov;
+ short t, x, y1, y2;
+ s32 xyz[3], disturb[3];
+
+ result = measure_int(st, &busy, &t, &x, &y1, &y2);
+ if (result)
+ return result;
+ if (busy)
+ return -1;
+ coordinate_conversion(x, y1, y2, t, &xyz[0], &xyz[1], &xyz[2]);
+ filter_filter(&st->filter, xyz, xyz);
+ for (i = 0; i < 3; i++)
+ rawfixed[i] = (short)(xyz[i] / 100);
+
+ if (st->first_measure_after_reset) {
+ for (i = 0; i < 3; i++)
+ st->base_compass_data[i] = rawfixed[i];
+ st->first_measure_after_reset = 0;
+ }
+ ov = 0;
+ for (i = 0; i < 3; i++) {
+ disturb[i] = abs(st->base_compass_data[i] - rawfixed[i]);
+ if (disturb[i] > YAS_MAG_DISTURBURNCE_THRESHOLD)
+ ov = 1;
+ }
+ if (ov)
+ st->reset_timer += st->delay;
+ else
+ st->reset_timer = 0;
+
+ if (st->reset_timer > YAS_RESET_COIL_TIME_THRESHOLD)
+ *overunderflow = (1<<8);
+ else
+ *overunderflow = 0;
+ *overunderflow |= inv_check_range(st, x, y1, y2);
+
+ return 0;
+}
+
+/**
+ * yas53x_read_raw() - read raw method.
+ */
+static int yas53x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask) {
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case 0:
+ if (!(iio_buffer_enabled(indio_dev)))
+ return -EINVAL;
+ if (chan->type == IIO_MAGN) {
+ *val = st->compass_data[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_MAGN) {
+ *val = YAS530_SCALE;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * inv_compass_matrix_show() - show orientation matrix
+ */
+static ssize_t inv_compass_matrix_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ signed char *m;
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ m = st->plat_data.orientation;
+ return sprintf(buf,
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+static ssize_t yas53x_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 data;
+ int error;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ error = kstrtoint(buf, 10, &data);
+ if (error)
+ return error;
+ if (0 == data)
+ return -EINVAL;
+ /* transform rate to delay in ms */
+ data = MSEC_PER_SEC / data;
+
+ if (data > YAS530_MAX_DELAY)
+ data = YAS530_MAX_DELAY;
+ if (data < YAS530_MIN_DELAY)
+ data = YAS530_MIN_DELAY;
+ st->delay = data;
+
+ return count;
+}
+
+static ssize_t yas53x_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ /* transform delay in ms to rate */
+ return sprintf(buf, "%d\n", (int)MSEC_PER_SEC / st->delay);
+}
+
+static ssize_t yas53x_overunderflow_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 data;
+ int error;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ error = kstrtoint(buf, 10, &data);
+ if (error)
+ return error;
+ if (data)
+ return -EINVAL;
+ st->overunderflow = data;
+
+ return count;
+}
+
+static ssize_t yas53x_overunderflow_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->overunderflow);
+}
+
+void set_yas53x_enable(struct iio_dev *indio_dev, bool enable)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ yas_init_adap_filter(st);
+ st->first_measure_after_reset = 1;
+ st->first_read_after_reset = 1;
+ schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay));
+}
+
+static void yas53x_work_func(struct work_struct *work)
+{
+ struct inv_compass_state *st =
+ container_of((struct delayed_work *)work,
+ struct inv_compass_state, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u32 delay = msecs_to_jiffies(st->delay);
+
+ mutex_lock(&indio_dev->mlock);
+ if (!(iio_buffer_enabled(indio_dev)))
+ goto error_ret;
+
+ schedule_delayed_work(&st->work, delay);
+ inv_read_yas53x_fifo(indio_dev);
+ INV_I2C_INC_COMPASSIRQ();
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+}
+
+static const struct iio_chan_spec compass_channels[] = {
+ {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_YAS53X_SCAN_MAGN_X,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_YAS53X_SCAN_MAGN_Y,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_YAS53X_SCAN_MAGN_Z,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(INV_YAS53X_SCAN_TIMESTAMP)
+};
+
+static DEVICE_ATTR(compass_matrix, S_IRUGO, inv_compass_matrix_show, NULL);
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, yas53x_rate_show,
+ yas53x_rate_store);
+static DEVICE_ATTR(overunderflow, S_IRUGO | S_IWUSR,
+ yas53x_overunderflow_show, yas53x_overunderflow_store);
+
+static struct attribute *inv_yas53x_attributes[] = {
+ &dev_attr_compass_matrix.attr,
+ &dev_attr_sampling_frequency.attr,
+ &dev_attr_overunderflow.attr,
+ NULL,
+};
+static const struct attribute_group inv_attribute_group = {
+ .name = "yas53x",
+ .attrs = inv_yas53x_attributes
+};
+
+static const struct iio_info yas53x_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &yas53x_read_raw,
+ .attrs = &inv_attribute_group,
+};
+
+/*constant IIO attribute */
+/**
+ * inv_yas53x_probe() - probe function.
+ */
+static int inv_yas53x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_compass_state *st;
+ struct iio_dev *indio_dev;
+ int result;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+ indio_dev = iio_allocate_device(sizeof(*st));
+ if (indio_dev == NULL) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->client = client;
+ st->plat_data =
+ *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+ st->delay = 10;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ if (!strcmp(id->name, "yas530")) {
+ st->read_data = yas530_read_data;
+ st->get_cal_data = get_cal_data_yas530;
+ st->overflow_bound = YAS_YAS530_DATA_OVERFLOW;
+ st->center = YAS_YAS530_DATA_CENTER;
+ st->filter.filter_thresh = YAS530_MAG_DEFAULT_FILTER_THRESH;
+ } else {
+ st->read_data = yas532_533_read_data;
+ st->get_cal_data = get_cal_data_yas532_533;
+ st->overflow_bound = YAS_YAS532_533_DATA_OVERFLOW;
+ st->center = YAS_YAS532_533_DATA_CENTER;
+ st->filter.filter_thresh = YAS532_MAG_DEFAULT_FILTER_THRESH;
+ }
+ st->upper_bound = st->center + (st->center >> 1);
+ st->lower_bound = (st->center >> 1);
+
+ result = yas53x_resume(st);
+ if (result)
+ goto out_free;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = compass_channels;
+ indio_dev->num_channels = ARRAY_SIZE(compass_channels);
+ indio_dev->info = &yas53x_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ result = inv_yas53x_configure_ring(indio_dev);
+ if (result)
+ goto out_free;
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result)
+ goto out_unreg_ring;
+ result = inv_yas53x_probe_trigger(indio_dev);
+ if (result)
+ goto out_remove_ring;
+
+ result = iio_device_register(indio_dev);
+ if (result)
+ goto out_remove_trigger;
+ INIT_DELAYED_WORK(&st->work, yas53x_work_func);
+ pr_info("%s: Probe name %s\n", __func__, id->name);
+
+ return 0;
+out_remove_trigger:
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_yas53x_remove_trigger(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_yas53x_unconfigure_ring(indio_dev);
+out_free:
+ iio_free_device(indio_dev);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return -EIO;
+}
+
+/**
+ * inv_yas53x_remove() - remove function.
+ */
+static int inv_yas53x_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ cancel_delayed_work_sync(&st->work);
+ iio_device_unregister(indio_dev);
+ inv_yas53x_remove_trigger(indio_dev);
+ iio_buffer_unregister(indio_dev);
+ inv_yas53x_unconfigure_ring(indio_dev);
+ iio_free_device(indio_dev);
+
+ dev_info(&client->adapter->dev, "inv_yas53x_iio module removed.\n");
+ return 0;
+}
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_yas53x_id[] = {
+ {"yas530", 0},
+ {"yas532", 0},
+ {"yas533", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_yas53x_id);
+
+static struct i2c_driver inv_yas53x_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = inv_yas53x_probe,
+ .remove = inv_yas53x_remove,
+ .id_table = inv_yas53x_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv_yas53x_iio",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init inv_yas53x_init(void)
+{
+ int result = i2c_add_driver(&inv_yas53x_driver);
+ if (result) {
+ pr_err("%s failed\n", __func__);
+ return result;
+ }
+ return 0;
+}
+
+static void __exit inv_yas53x_exit(void)
+{
+ i2c_del_driver(&inv_yas53x_driver);
+}
+
+module_init(inv_yas53x_init);
+module_exit(inv_yas53x_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv_yas53x_iio");
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h b/drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h
new file mode 100644
index 00000000..92bf0af7
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h
@@ -0,0 +1,172 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_yas53x_iio.h
+ * @brief Struct definitions for the Invensense implementation
+ * of yas53x driver.
+ */
+
+#ifndef _INV_GYRO_H_
+#define _INV_GYRO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include <linux/mpu.h>
+
+#include "iio.h"
+#include "buffer.h"
+#include "trigger.h"
+
+#define YAS_MAG_MAX_FILTER_LEN 30
+struct yas_adaptive_filter {
+ int num;
+ int index;
+ int filter_len;
+ int filter_noise;
+ int sequence[YAS_MAG_MAX_FILTER_LEN];
+};
+
+struct yas_thresh_filter {
+ int threshold;
+ int last;
+};
+
+struct yas_filter {
+ int filter_len;
+ int filter_thresh;
+ int filter_noise[3];
+ struct yas_adaptive_filter adap_filter[3];
+ struct yas_thresh_filter thresh_filter[3];
+};
+
+/**
+ * struct inv_compass_state - Driver state variables.
+ * @plat_data: mpu platform data from board file.
+ * @client: i2c client handle.
+ * @chan_info: channel information.
+ * @trig: IIO trigger.
+ * @work: work structure.
+ * @delay: delay to schedule the next work.
+ * @overflow_bound: bound to determine overflow.
+ * @center: center of the measurement.
+ * @compass_data[3]: compass data store.
+ * @offset[3]: yas530 specific data.
+ * @base_compass_data[3]: first measure data after reset.
+ * @first_measure_after_reset:1: flag for first measurement after reset.
+ * @first_read_after_reset:1: flag for first read after reset.
+ * @reset_timer: timer to accumulate overflow conditions.
+ * @overunderflow:1: overflow and underflow flag.
+ * @filter: filter data structure.
+ * @read_data: function pointer of reading data from device.
+ * @get_cal_data: function pointer of reading cal data.
+ */
+struct inv_compass_state {
+ struct mpu_platform_data plat_data;
+ struct i2c_client *client;
+ struct iio_trigger *trig;
+ struct delayed_work work;
+ s16 delay;
+ s16 overflow_bound;
+ s16 upper_bound;
+ s16 lower_bound;
+ s16 center;
+ s16 compass_data[3];
+ s8 offset[3];
+ s16 base_compass_data[3];
+ u8 first_measure_after_reset:1;
+ u8 first_read_after_reset:1;
+ u8 overunderflow:1;
+ s32 reset_timer;
+ struct yas_filter filter;
+ int (*read_data)(struct inv_compass_state *st,
+ int *, u16 *, u16 *, u16 *, u16 *);
+ int (*get_cal_data)(struct inv_compass_state *);
+};
+
+/* scan element definition */
+enum inv_mpu_scan {
+ INV_YAS53X_SCAN_MAGN_X,
+ INV_YAS53X_SCAN_MAGN_Y,
+ INV_YAS53X_SCAN_MAGN_Z,
+ INV_YAS53X_SCAN_TIMESTAMP,
+};
+
+#define YAS530_REGADDR_DEVICE_ID 0x80
+#define YAS530_REGADDR_ACTUATE_INIT_COIL 0x81
+#define YAS530_REGADDR_MEASURE_COMMAND 0x82
+#define YAS530_REGADDR_CONFIG 0x83
+#define YAS530_REGADDR_MEASURE_INTERVAL 0x84
+#define YAS530_REGADDR_OFFSET_X 0x85
+#define YAS530_REGADDR_OFFSET_Y1 0x86
+#define YAS530_REGADDR_OFFSET_Y2 0x87
+#define YAS530_REGADDR_TEST1 0x88
+#define YAS530_REGADDR_TEST2 0x89
+#define YAS530_REGADDR_CAL 0x90
+#define YAS530_REGADDR_MEASURE_DATA 0xb0
+
+#define YAS530_MAX_DELAY 200
+#define YAS530_MIN_DELAY 5
+#define YAS530_SCALE 107374182L
+
+#define YAS_YAS530_VERSION_A 0 /* YAS530 (MS-3E Aver) */
+#define YAS_YAS530_VERSION_B 1 /* YAS530B (MS-3E Bver) */
+#define YAS_YAS530_VERSION_A_COEF 380
+#define YAS_YAS530_VERSION_B_COEF 550
+#define YAS_YAS530_DATA_CENTER 2048
+#define YAS_YAS530_DATA_OVERFLOW 4095
+#define YAS_YAS530_CAL_DATA_SIZE 16
+
+/*filter related defines */
+#define YAS_MAG_DEFAULT_FILTER_NOISE_X 144 /* sd: 1200 nT */
+#define YAS_MAG_DEFAULT_FILTER_NOISE_Y 144 /* sd: 1200 nT */
+#define YAS_MAG_DEFAULT_FILTER_NOISE_Z 144 /* sd: 1200 nT */
+#define YAS_MAG_DEFAULT_FILTER_LEN 20
+
+#define YAS530_MAG_DEFAULT_FILTER_THRESH 100
+#define YAS532_MAG_DEFAULT_FILTER_THRESH 300
+
+#define YAS_YAS532_533_VERSION_AB 0 /* YAS532_533AB (MS-3R/3F ABver) */
+#define YAS_YAS532_533_VERSION_AC 1 /* YAS532_533AC (MS-3R/3F ACver) */
+#define YAS_YAS532_533_VERSION_AB_COEF 1800
+#define YAS_YAS532_533_VERSION_AC_COEF 900
+#define YAS_YAS532_533_DATA_CENTER 4096
+#define YAS_YAS532_533_DATA_OVERFLOW 8190
+#define YAS_YAS532_533_CAL_DATA_SIZE 14
+
+#define YAS_MAG_DISTURBURNCE_THRESHOLD 1600
+#define YAS_RESET_COIL_TIME_THRESHOLD 3000
+
+#define INV_ERROR_COMPASS_DATA_OVERFLOW (-1)
+#define INV_ERROR_COMPASS_DATA_NOT_READY (-2)
+
+int inv_yas53x_configure_ring(struct iio_dev *indio_dev);
+void inv_yas53x_unconfigure_ring(struct iio_dev *indio_dev);
+int inv_yas53x_probe_trigger(struct iio_dev *indio_dev);
+void inv_yas53x_remove_trigger(struct iio_dev *indio_dev);
+void set_yas53x_enable(struct iio_dev *indio_dev, bool enable);
+void inv_read_yas53x_fifo(struct iio_dev *indio_dev);
+int yas53x_read(struct inv_compass_state *st, short rawfixed[3],
+ s32 *overunderflow);
+int yas53x_resume(struct inv_compass_state *st);
+
+#endif /* #ifndef _INV_GYRO_H_ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c b/drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c
new file mode 100644
index 00000000..efcf49c6
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c
@@ -0,0 +1,165 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_yas53x_ring.c
+ * @brief Invensense implementation for yas530/yas532/yas533.
+ * @details This driver currently works for the yas530/yas532/yas533.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+
+#include "iio.h"
+#include "kfifo_buf.h"
+#include "trigger_consumer.h"
+#include "sysfs.h"
+
+#include "inv_yas53x_iio.h"
+
+static s64 get_time_ns(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+
+ return timespec_to_ns(&ts);
+}
+
+static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d,
+ short *s, int scan_index)
+{
+ struct iio_buffer *ring = indio_dev->buffer;
+ int st;
+ int i, d_ind;
+
+ d_ind = 0;
+ for (i = 0; i < 3; i++) {
+ st = iio_scan_mask_query(indio_dev, ring, scan_index + i);
+ if (st) {
+ memcpy(&d[d_ind], &s[i], sizeof(s[i]));
+ d_ind += sizeof(s[i]);
+ }
+ }
+
+ return d_ind;
+}
+
+/**
+ * inv_read_yas53x_fifo() - Transfer data from FIFO to ring buffer.
+ */
+void inv_read_yas53x_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int d_ind;
+ s32 overunderflow;
+ s8 *tmp;
+ s64 tmp_buf[2];
+
+ if (!yas53x_read(st, st->compass_data, &overunderflow)) {
+ tmp = (u8 *)tmp_buf;
+ d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data,
+ INV_YAS53X_SCAN_MAGN_X);
+ if (ring->scan_timestamp)
+ tmp_buf[(d_ind + 7) / 8] = get_time_ns();
+ ring->access->store_to(indio_dev->buffer, tmp, 0);
+
+ if (overunderflow) {
+ yas53x_resume(st);
+ if (!st->overunderflow)
+ st->overunderflow = 1;
+ }
+ }
+}
+
+void inv_yas53x_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_kfifo_free(indio_dev->buffer);
+};
+
+static int inv_yas53x_postenable(struct iio_dev *indio_dev)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+
+ /* when all the outputs are disabled, even though buffer/enable is on,
+ do nothing */
+ if (!(iio_scan_mask_query(indio_dev, ring, INV_YAS53X_SCAN_MAGN_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_YAS53X_SCAN_MAGN_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_YAS53X_SCAN_MAGN_Z)))
+ return 0;
+
+ set_yas53x_enable(indio_dev, true);
+ schedule_delayed_work(&st->work,
+ msecs_to_jiffies(st->delay));
+
+ return 0;
+}
+
+static int inv_yas53x_predisable(struct iio_dev *indio_dev)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+
+ cancel_delayed_work_sync(&st->work);
+ clear_bit(INV_YAS53X_SCAN_MAGN_X, ring->scan_mask);
+ clear_bit(INV_YAS53X_SCAN_MAGN_Y, ring->scan_mask);
+ clear_bit(INV_YAS53X_SCAN_MAGN_Z, ring->scan_mask);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_yas53x_ring_setup_ops = {
+ .preenable = &iio_sw_buffer_preenable,
+ .postenable = &inv_yas53x_postenable,
+ .predisable = &inv_yas53x_predisable,
+};
+
+int inv_yas53x_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret = 0;
+ struct iio_buffer *ring;
+
+ ring = iio_kfifo_allocate(indio_dev);
+ if (!ring) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ indio_dev->buffer = ring;
+ /* setup ring buffer */
+ ring->scan_timestamp = true;
+ indio_dev->setup_ops = &inv_yas53x_ring_setup_ops;
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+ return 0;
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c
new file mode 100644
index 00000000..a20ce2ba
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c
@@ -0,0 +1,91 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_yas53x_trigger.c
+ * @brief Invensense implementation for yas530/yas532/yas533
+ * @details This driver currently works for the yas530/yas532/yas533
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "iio.h"
+#include "sysfs.h"
+#include "trigger.h"
+
+#include "inv_yas53x_iio.h"
+
+static const struct iio_trigger_ops inv_yas53x_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+int inv_yas53x_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ st->trig = iio_allocate_trigger("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ /* select default trigger */
+ st->trig->dev.parent = &st->client->dev;
+ st->trig->private_data = indio_dev;
+ st->trig->ops = &inv_yas53x_trigger_ops;
+ ret = iio_trigger_register(st->trig);
+
+ /* select default trigger */
+ indio_dev->trig = st->trig;
+ if (ret)
+ goto error_free_trig;
+
+ return 0;
+
+error_free_trig:
+ iio_free_trigger(st->trig);
+error_ret:
+ return ret;
+}
+
+void inv_yas53x_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_free_trigger(st->trig);
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
new file mode 100644
index 00000000..3ce3769c
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -0,0 +1,408 @@
+/*
+ * 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/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_12000MG 12000
+#define ST_MAGN_FS_AVL_16000MG 16000
+
+/* 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 909
+#define ST_MAGN_1_FS_AVL_1900_GAIN_XY 1169
+#define ST_MAGN_1_FS_AVL_2500_GAIN_XY 1492
+#define ST_MAGN_1_FS_AVL_4000_GAIN_XY 2222
+#define ST_MAGN_1_FS_AVL_4700_GAIN_XY 2500
+#define ST_MAGN_1_FS_AVL_5600_GAIN_XY 3030
+#define ST_MAGN_1_FS_AVL_8100_GAIN_XY 4347
+#define ST_MAGN_1_FS_AVL_1300_GAIN_Z 1020
+#define ST_MAGN_1_FS_AVL_1900_GAIN_Z 1315
+#define ST_MAGN_1_FS_AVL_2500_GAIN_Z 1666
+#define ST_MAGN_1_FS_AVL_4000_GAIN_Z 2500
+#define ST_MAGN_1_FS_AVL_4700_GAIN_Z 2816
+#define ST_MAGN_1_FS_AVL_5600_GAIN_Z 3389
+#define ST_MAGN_1_FS_AVL_8100_GAIN_Z 4878
+#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_12000_VAL 0x02
+#define ST_MAGN_2_FS_AVL_16000_VAL 0x03
+#define ST_MAGN_2_FS_AVL_4000_GAIN 146
+#define ST_MAGN_2_FS_AVL_8000_GAIN 292
+#define ST_MAGN_2_FS_AVL_12000_GAIN 438
+#define ST_MAGN_2_FS_AVL_16000_GAIN 584
+#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_12000MG,
+ .value = ST_MAGN_2_FS_AVL_12000_VAL,
+ .gain = ST_MAGN_2_FS_AVL_12000_GAIN,
+ },
+ [3] = {
+ .num = ST_MAGN_FS_AVL_16000MG,
+ .value = ST_MAGN_2_FS_AVL_16000_VAL,
+ .gain = ST_MAGN_2_FS_AVL_16000_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");