aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/Kconfig7
-rw-r--r--drivers/iio/Makefile5
-rw-r--r--drivers/iio/accel/Kconfig16
-rw-r--r--drivers/iio/accel/Makefile5
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c418
-rw-r--r--drivers/iio/adc/Kconfig38
-rw-r--r--drivers/iio/adc/Makefile4
-rw-r--r--drivers/iio/adc/ad7266.c2
-rw-r--r--drivers/iio/adc/ad7476.c332
-rw-r--r--drivers/iio/adc/ad7791.c460
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c558
-rw-r--r--drivers/iio/adc/at91_adc.c79
-rw-r--r--drivers/iio/adc/lp8788_adc.c264
-rw-r--r--drivers/iio/common/Kconfig5
-rw-r--r--drivers/iio/common/Makefile9
-rw-r--r--drivers/iio/common/hid-sensors/Kconfig26
-rw-r--r--drivers/iio/common/hid-sensors/Makefile6
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c250
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.h57
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c103
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.h26
-rw-r--r--drivers/iio/dac/Kconfig20
-rw-r--r--drivers/iio/dac/Makefile1
-rw-r--r--drivers/iio/dac/ad5446.c450
-rw-r--r--drivers/iio/dac/ad5446.h91
-rw-r--r--drivers/iio/dac/ad5755.c650
-rw-r--r--drivers/iio/gyro/Kconfig16
-rw-r--r--drivers/iio/gyro/Makefile5
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c418
-rw-r--r--drivers/iio/industrialio-buffer.c15
-rw-r--r--drivers/iio/industrialio-core.c13
-rw-r--r--drivers/iio/inkern.c139
-rw-r--r--drivers/iio/kfifo_buf.c31
-rw-r--r--drivers/iio/light/Kconfig10
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/adjd_s311.c2
-rw-r--r--drivers/iio/light/hid-sensor-als.c385
-rw-r--r--drivers/iio/magnetometer/Kconfig16
-rw-r--r--drivers/iio/magnetometer/Makefile5
-rw-r--r--drivers/iio/magnetometer/hid-sensor-magn-3d.c419
40 files changed, 5057 insertions, 300 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index d4984c8be973..6e3f143fc71d 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -1,5 +1,5 @@
#
-# Industrial I/O subsytem configuration
+# Industrial I/O subsystem configuration
#
menuconfig IIO
@@ -54,10 +54,15 @@ config IIO_CONSUMERS_PER_TRIGGER
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/light/Kconfig"
source "drivers/iio/frequency/Kconfig"
source "drivers/iio/dac/Kconfig"
+source "drivers/iio/common/Kconfig"
+source "drivers/iio/gyro/Kconfig"
+source "drivers/iio/light/Kconfig"
+source "drivers/iio/magnetometer/Kconfig"
endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 34309abb7979..f7fa3c0867b4 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -10,8 +10,13 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.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 += light/
obj-y += frequency/
obj-y += dac/
+obj-y += common/
+obj-y += gyro/
+obj-y += light/
+obj-y += magnetometer/
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
new file mode 100644
index 000000000000..b2510c4d9a5a
--- /dev/null
+++ b/drivers/iio/accel/Kconfig
@@ -0,0 +1,16 @@
+#
+# 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
+ tristate "HID Acelerometers 3D"
+ help
+ Say yes here to build support for the HID SENSOR
+ accelerometers 3D.
+
+endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
new file mode 100644
index 000000000000..5bc6855a973e
--- /dev/null
+++ b/drivers/iio/accel/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.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 000000000000..314a4057879e
--- /dev/null
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -0,0 +1,418 @@
+/*
+ * 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-attributes.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_iio_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 = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_X,
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Y,
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .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)
+{
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int datum_sz;
+
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ if (!buffer) {
+ dev_err(&indio_dev->dev, "Buffer == NULL\n");
+ return;
+ }
+ datum_sz = buffer->access->get_bytes_per_datum(buffer);
+ if (len > datum_sz) {
+ dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+ datum_sz);
+ return;
+ }
+ iio_push_to_buffer(buffer, (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 __devinit 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) {
+ 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 __devinit 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/adc/Kconfig b/drivers/iio/adc/Kconfig
index 8a78b4f3ef58..492758120338 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -3,6 +3,11 @@
#
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
@@ -13,6 +18,33 @@ config AD7266
Say yes here to build support for Analog Devices AD7265 and AD7266
ADCs.
+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 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 AT91_ADC
tristate "Atmel AT91 ADC"
depends on ARCH_AT91
@@ -22,4 +54,10 @@ config AT91_ADC
help
Say yes here to build support for Atmel AT91 ADC.
+config LP8788_ADC
+ bool "LP8788 ADC driver"
+ depends on MFD_LP8788
+ help
+ Say yes here to build support for TI LP8788 ADC.
+
endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 52eec254c38c..900995d5e179 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -2,5 +2,9 @@
# Makefile for IIO ADC drivers
#
+obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD7266) += ad7266.o
+obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index 5c3f1ba5a06d..b11f214779a2 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -99,7 +99,7 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p)
if (ret == 0) {
if (indio_dev->scan_timestamp)
((s64 *)st->data)[1] = pf->timestamp;
- iio_push_to_buffer(buffer, (u8 *)st->data, pf->timestamp);
+ iio_push_to_buffer(buffer, (u8 *)st->data);
}
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
new file mode 100644
index 000000000000..7f2f45a0a48d
--- /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_buffer(indio_dev->buffer, 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) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .info_mask = _info_mask | \
+ IIO_CHAN_INFO_SCALE_SHARED_BIT, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = (_shift), \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
+ IIO_CHAN_INFO_RAW_SEPARATE_BIT)
+#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
+ IIO_CHAN_INFO_RAW_SEPARATE_BIT)
+#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 __devinit 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 __devexit 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 = __devexit_p(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 000000000000..e93740843b2b
--- /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 __devinit 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 __devinit 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 __devexit 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 = __devexit_p(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/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
new file mode 100644
index 000000000000..67baa1363d7a
--- /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_buffer(indio_dev->buffer, (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;
+ sigma_delta->trig->private_data = sigma_delta;
+
+ ret = iio_trigger_register(sigma_delta->trig);
+ if (ret)
+ goto error_free_irq;
+
+ /* select default trigger */
+ indio_dev->trig = 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
index f61780a02374..3ed94bf80596 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -82,7 +82,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
*timestamp = pf->timestamp;
}
- buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp);
+ buffer->access->store_to(buffer, (u8 *)st->buffer);
iio_trigger_notify_done(idev->trig);
st->irq_enabled = true;
@@ -545,13 +545,6 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
goto error_free_device;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "No resource defined\n");
- ret = -ENXIO;
- goto error_ret;
- }
-
platform_set_drvdata(pdev, idev);
idev->dev.parent = &pdev->dev;
@@ -566,18 +559,12 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
goto error_free_device;
}
- if (!request_mem_region(res->start, resource_size(res),
- "AT91 adc registers")) {
- dev_err(&pdev->dev, "Resources are unavailable.\n");
- ret = -EBUSY;
- goto error_free_device;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- st->reg_base = ioremap(res->start, resource_size(res));
+ st->reg_base = devm_request_and_ioremap(&pdev->dev, res);
if (!st->reg_base) {
- dev_err(&pdev->dev, "Failed to map registers.\n");
ret = -ENOMEM;
- goto error_release_mem;
+ goto error_free_device;
}
/*
@@ -592,45 +579,35 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
idev);
if (ret) {
dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
- goto error_unmap_reg;
+ goto error_free_device;
}
- st->clk = clk_get(&pdev->dev, "adc_clk");
+ 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(st->clk);
+ ret = clk_prepare_enable(st->clk);
if (ret) {
- dev_err(&pdev->dev, "Could not prepare the clock.\n");
- goto error_free_clk;
- }
-
- ret = clk_enable(st->clk);
- if (ret) {
- dev_err(&pdev->dev, "Could not enable the clock.\n");
- goto error_unprepare_clk;
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the clock.\n");
+ goto error_free_irq;
}
- st->adc_clk = clk_get(&pdev->dev, "adc_op_clk");
+ 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->clk);
+ ret = PTR_ERR(st->adc_clk);
goto error_disable_clk;
}
- ret = clk_prepare(st->adc_clk);
+ ret = clk_prepare_enable(st->adc_clk);
if (ret) {
- dev_err(&pdev->dev, "Could not prepare the ADC clock.\n");
- goto error_free_adc_clk;
- }
-
- ret = clk_enable(st->adc_clk);
- if (ret) {
- dev_err(&pdev->dev, "Could not enable the ADC clock.\n");
- goto error_unprepare_adc_clk;
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the ADC clock.\n");
+ goto error_disable_clk;
}
/*
@@ -694,23 +671,11 @@ error_remove_triggers:
error_unregister_buffer:
at91_adc_buffer_remove(idev);
error_disable_adc_clk:
- clk_disable(st->adc_clk);
-error_unprepare_adc_clk:
- clk_unprepare(st->adc_clk);
-error_free_adc_clk:
- clk_put(st->adc_clk);
+ clk_disable_unprepare(st->adc_clk);
error_disable_clk:
- clk_disable(st->clk);
-error_unprepare_clk:
- clk_unprepare(st->clk);
-error_free_clk:
- clk_put(st->clk);
+ clk_disable_unprepare(st->clk);
error_free_irq:
free_irq(st->irq, idev);
-error_unmap_reg:
- iounmap(st->reg_base);
-error_release_mem:
- release_mem_region(res->start, resource_size(res));
error_free_device:
iio_device_free(idev);
error_ret:
@@ -720,20 +685,14 @@ error_ret:
static int __devexit at91_adc_remove(struct platform_device *pdev)
{
struct iio_dev *idev = platform_get_drvdata(pdev);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
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_put(st->adc_clk);
- clk_disable(st->clk);
- clk_unprepare(st->clk);
- clk_put(st->clk);
+ clk_disable_unprepare(st->clk);
free_irq(st->irq, idev);
- iounmap(st->reg_base);
- release_mem_region(res->start, resource_size(res));
iio_device_free(idev);
return 0;
diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c
new file mode 100644
index 000000000000..a93aaf0bb841
--- /dev/null
+++ b/drivers/iio/adc/lp8788_adc.c
@@ -0,0 +1,264 @@
+/*
+ * 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 = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+ IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+ .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(adc->lp->dev, "iio map err: %d\n", ret);
+ return ret;
+ }
+
+ adc->map = map;
+ return 0;
+}
+
+static inline void lp8788_iio_map_unregister(struct iio_dev *indio_dev,
+ struct lp8788_adc *adc)
+{
+ iio_map_array_unregister(indio_dev, adc->map);
+}
+
+static int __devinit 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);
+
+ ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc);
+ if (ret)
+ goto err_iio_map;
+
+ mutex_init(&adc->lock);
+
+ indio_dev->dev.parent = lp->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(lp->dev, "iio dev register err: %d\n", ret);
+ goto err_iio_device;
+ }
+
+ return 0;
+
+err_iio_device:
+ lp8788_iio_map_unregister(indio_dev, adc);
+err_iio_map:
+ iio_device_free(indio_dev);
+ return ret;
+}
+
+static int __devexit lp8788_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct lp8788_adc *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ lp8788_iio_map_unregister(indio_dev, adc);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver lp8788_adc_driver = {
+ .probe = lp8788_adc_probe,
+ .remove = __devexit_p(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/common/Kconfig b/drivers/iio/common/Kconfig
new file mode 100644
index 000000000000..ed45ee54500c
--- /dev/null
+++ b/drivers/iio/common/Kconfig
@@ -0,0 +1,5 @@
+#
+# IIO common modules
+#
+
+source "drivers/iio/common/hid-sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
new file mode 100644
index 000000000000..81584009b21b
--- /dev/null
+++ b/drivers/iio/common/Makefile
@@ -0,0 +1,9 @@
+#
+# 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/
diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig
new file mode 100644
index 000000000000..8e63d81d652a
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/Kconfig
@@ -0,0 +1,26 @@
+#
+# 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 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_ENUM_BASE_QUIRKS
+ tristate "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 000000000000..1f463e00c242
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Hid sensor common modules.
+#
+
+obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o
+hid-sensor-iio-common-y := hid-sensor-attributes.o hid-sensor-trigger.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 000000000000..75374955caba
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -0,0 +1,250 @@
+/*
+ * 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 "hid-sensor-attributes.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_iio_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_iio_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_iio_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_iio_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_iio_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-attributes.h b/drivers/iio/common/hid-sensors/hid-sensor-attributes.h
new file mode 100644
index 000000000000..a4676a0c3de5
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.h
@@ -0,0 +1,57 @@
+/*
+ * 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_SENSORS_ATTRIBUTES_H
+#define _HID_SENSORS_ATTRIBUTES_H
+
+/* Common hid sensor iio structure */
+struct hid_sensor_iio_common {
+ struct hid_sensor_hub_device *hsdev;
+ struct platform_device *pdev;
+ unsigned usage_id;
+ bool data_ready;
+ struct hid_sensor_hub_attribute_info poll;
+ struct hid_sensor_hub_attribute_info report_state;
+ struct hid_sensor_hub_attribute_info power_state;
+ struct hid_sensor_hub_attribute_info sensitivity;
+};
+
+/*Convert from hid unit expo to regular exponent*/
+static inline int hid_sensor_convert_exponent(int unit_expo)
+{
+ if (unit_expo < 0x08)
+ return unit_expo;
+ else if (unit_expo <= 0x0f)
+ return -(0x0f-unit_expo+1);
+ else
+ return 0;
+}
+
+int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ struct hid_sensor_iio_common *st);
+int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st,
+ int val1, int val2);
+int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st,
+ int *val1, int *val2);
+int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st,
+ int val1, int val2);
+int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st,
+ int *val1, int *val2);
+
+#endif
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 000000000000..d4b790d18efb
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -0,0 +1,103 @@
+/*
+ * 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-attributes.h"
+#include "hid-sensor-trigger.h"
+
+static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct hid_sensor_iio_common *st = trig->private_data;
+ int state_val;
+
+ state_val = state ? 1 : 0;
+#if (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS) || \
+ (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS_MODULE)
+ ++state_val;
+#endif
+ 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_iio_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;
+ trig->private_data = 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 000000000000..fd982971b1b8
--- /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_iio_common *attrb);
+void hid_sensor_remove_trigger(struct iio_dev *indio_dev);
+
+#endif
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 1be15fa9d618..b1c0ee5294ca 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -57,11 +57,12 @@ config AD5624R_SPI
config AD5446
tristate "Analog Devices AD5446 and similar single channel DACs driver"
- depends on SPI
+ depends on (SPI_MASTER || I2C)
help
- Say yes here to build support for Analog Devices AD5444, AD5446, AD5450,
- AD5451, AD5452, AD5453, AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601,
- AD5611, AD5620, AD5621, AD5640, AD5660, AD5662 DACs.
+ 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.
@@ -76,6 +77,17 @@ config AD5504
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
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 9ea3ceeefc07..c0d333b23ba3 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
obj-$(CONFIG_AD5064) += ad5064.o
obj-$(CONFIG_AD5504) += ad5504.o
obj-$(CONFIG_AD5446) += ad5446.o
+obj-$(CONFIG_AD5755) += ad5755.o
obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c
index 2ca5059ef89e..3310cbbd41e7 100644
--- a/drivers/iio/dac/ad5446.c
+++ b/drivers/iio/dac/ad5446.c
@@ -14,6 +14,7 @@
#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>
@@ -21,24 +22,40 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#include "ad5446.h"
+#define MODE_PWRDWN_1k 0x1
+#define MODE_PWRDWN_100k 0x2
+#define MODE_PWRDWN_TRISTATE 0x3
-static int ad5446_write(struct ad5446_state *st, unsigned val)
-{
- __be16 data = cpu_to_be16(val);
- return spi_write(st->spi, &data, sizeof(data));
-}
+/**
+ * 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
+ */
-static int ad5660_write(struct ad5446_state *st, unsigned val)
-{
- uint8_t data[3];
+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;
+};
- data[0] = (val >> 16) & 0xFF;
- data[1] = (val >> 8) & 0xFF;
- data[2] = val & 0xFF;
+/**
+ * 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
+ */
- return spi_write(st->spi, data, sizeof(data));
-}
+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"
@@ -110,7 +127,7 @@ static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev,
return ret ? ret : len;
}
-static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = {
+static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
{
.name = "powerdown",
.read = ad5446_read_dac_powerdown,
@@ -136,84 +153,7 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = {
_AD5446_CHANNEL(bits, storage, shift, NULL)
#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \
- _AD5446_CHANNEL(bits, storage, shift, ad5064_ext_info_powerdown)
-
-static const struct ad5446_chip_info ad5446_chip_info_tbl[] = {
- [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,
- },
-};
+ _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,
@@ -272,14 +212,15 @@ static const struct iio_info ad5446_info = {
.driver_module = THIS_MODULE,
};
-static int __devinit ad5446_probe(struct spi_device *spi)
+static int __devinit 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(&spi->dev, "vcc");
+ reg = regulator_get(dev, "vcc");
if (!IS_ERR(reg)) {
ret = regulator_enable(reg);
if (ret)
@@ -294,16 +235,15 @@ static int __devinit ad5446_probe(struct spi_device *spi)
goto error_disable_reg;
}
st = iio_priv(indio_dev);
- st->chip_info =
- &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+ st->chip_info = chip_info;
- spi_set_drvdata(spi, indio_dev);
+ dev_set_drvdata(dev, indio_dev);
st->reg = reg;
- st->spi = spi;
+ st->dev = dev;
- /* 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;
+ /* 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;
@@ -316,7 +256,7 @@ static int __devinit ad5446_probe(struct spi_device *spi)
else if (voltage_uv)
st->vref_mv = voltage_uv / 1000;
else
- dev_warn(&spi->dev, "reference voltage unspecified\n");
+ dev_warn(dev, "reference voltage unspecified\n");
ret = iio_device_register(indio_dev);
if (ret)
@@ -336,9 +276,9 @@ error_put_reg:
return ret;
}
-static int ad5446_remove(struct spi_device *spi)
+static int ad5446_remove(struct device *dev)
{
- struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ad5446_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
@@ -351,7 +291,151 @@ static int ad5446_remove(struct spi_device *spi)
return 0;
}
-static const struct spi_device_id ad5446_id[] = {
+#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},
@@ -375,18 +459,160 @@ static const struct spi_device_id ad5446_id[] = {
{"ad5662", ID_AD5662},
{}
};
-MODULE_DEVICE_TABLE(spi, ad5446_id);
+MODULE_DEVICE_TABLE(spi, ad5446_spi_ids);
+
+static int __devinit ad5446_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
-static struct spi_driver ad5446_driver = {
+ return ad5446_probe(&spi->dev, id->name,
+ &ad5446_spi_chip_info[id->driver_data]);
+}
+
+static int __devexit 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_probe,
- .remove = __devexit_p(ad5446_remove),
- .id_table = ad5446_id,
+ .probe = ad5446_spi_probe,
+ .remove = __devexit_p(ad5446_spi_remove),
+ .id_table = ad5446_spi_ids,
};
-module_spi_driver(ad5446_driver);
+
+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 __devinit 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 __devexit 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 = __devexit_p(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");
diff --git a/drivers/iio/dac/ad5446.h b/drivers/iio/dac/ad5446.h
deleted file mode 100644
index 2934269a56d5..000000000000
--- a/drivers/iio/dac/ad5446.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * AD5446 SPI DAC driver
- *
- * Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-#ifndef IIO_DAC_AD5446_H_
-#define IIO_DAC_AD5446_H_
-
-/* DAC Control Bits */
-
-#define AD5446_LOAD (0x0 << 14) /* Load and update */
-#define AD5446_SDO_DIS (0x1 << 14) /* Disable SDO */
-#define AD5446_NOP (0x2 << 14) /* No operation */
-#define AD5446_CLK_RISING (0x3 << 14) /* Clock data on rising edge */
-
-#define AD5620_LOAD (0x0 << 14) /* Load and update Norm Operation*/
-#define AD5620_PWRDWN_1k (0x1 << 14) /* Power-down: 1kOhm to GND */
-#define AD5620_PWRDWN_100k (0x2 << 14) /* Power-down: 100kOhm to GND */
-#define AD5620_PWRDWN_TRISTATE (0x3 << 14) /* Power-down: Three-state */
-
-#define AD5660_LOAD (0x0 << 16) /* Load and update Norm Operation*/
-#define AD5660_PWRDWN_1k (0x1 << 16) /* Power-down: 1kOhm to GND */
-#define AD5660_PWRDWN_100k (0x2 << 16) /* Power-down: 100kOhm to GND */
-#define AD5660_PWRDWN_TRISTATE (0x3 << 16) /* Power-down: Three-state */
-
-#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 spi_device *spi;
- 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);
-};
-
-/**
- * ad5446_supported_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_device_ids {
- 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,
-};
-
-#endif /* IIO_DAC_AD5446_H_ */
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
new file mode 100644
index 000000000000..5db3506034c5
--- /dev/null
+++ b/drivers/iio/dac/ad5755.c
@@ -0,0 +1,650 @@
+/*
+ * 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);
+ struct spi_message m;
+ 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,
+ },
+ };
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+
+ 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(st->spi, &m);
+ 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 = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+ IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
+ IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
+ IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \
+ IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \
+ .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 __devinit 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 __devinit 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 __devinit 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 __devinit 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 __devexit 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 = __devexit_p(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/gyro/Kconfig b/drivers/iio/gyro/Kconfig
new file mode 100644
index 000000000000..21e27e2fc68c
--- /dev/null
+++ b/drivers/iio/gyro/Kconfig
@@ -0,0 +1,16 @@
+#
+# IIO Digital Gyroscope Sensor drivers configuration
+#
+menu "Digital gyroscope sensors"
+
+config HID_SENSOR_GYRO_3D
+ depends on HID_SENSOR_HUB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select HID_SENSOR_IIO_COMMON
+ tristate "HID Gyroscope 3D"
+ help
+ Say yes here to build support for the HID SENSOR
+ Gyroscope 3D.
+
+endmenu
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
new file mode 100644
index 000000000000..8a895d9fcbce
--- /dev/null
+++ b/drivers/iio/gyro/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O gyroscope sensor drivers
+#
+
+obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
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 000000000000..4c56ada51c39
--- /dev/null
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -0,0 +1,418 @@
+/*
+ * 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-attributes.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_iio_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 = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_X,
+ }, {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Y,
+ }, {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .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)
+{
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int datum_sz;
+
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ if (!buffer) {
+ dev_err(&indio_dev->dev, "Buffer == NULL\n");
+ return;
+ }
+ datum_sz = buffer->access->get_bytes_per_datum(buffer);
+ if (len > datum_sz) {
+ dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+ datum_sz);
+ return;
+ }
+ iio_push_to_buffer(buffer, (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 __devinit 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) {
+ 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 __devinit 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/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 4add9bb40eeb..d4ad37455a67 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -422,7 +422,7 @@ ssize_t iio_buffer_store_enable(struct device *dev,
ret = indio_dev->setup_ops->preenable(indio_dev);
if (ret) {
printk(KERN_ERR
- "Buffer not started:"
+ "Buffer not started: "
"buffer preenable failed\n");
goto error_ret;
}
@@ -431,12 +431,12 @@ ssize_t iio_buffer_store_enable(struct device *dev,
ret = buffer->access->request_update(buffer);
if (ret) {
printk(KERN_INFO
- "Buffer not started:"
+ "Buffer not started: "
"buffer parameter update failed\n");
goto error_ret;
}
}
- /* Definitely possible for devices to support both of these.*/
+ /* Definitely possible for devices to support both of these. */
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {
if (!indio_dev->trig) {
printk(KERN_INFO
@@ -456,7 +456,7 @@ ssize_t iio_buffer_store_enable(struct device *dev,
ret = indio_dev->setup_ops->postenable(indio_dev);
if (ret) {
printk(KERN_INFO
- "Buffer not started:"
+ "Buffer not started: "
"postenable failed\n");
indio_dev->currentmode = previous_mode;
if (indio_dev->setup_ops->postdisable)
@@ -657,7 +657,7 @@ 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
+ * @to: index to copy to
* @length: how many bytes to copy
* @l: list head used for management
*/
@@ -682,12 +682,11 @@ static unsigned char *iio_demux(struct iio_buffer *buffer,
return buffer->demux_bounce;
}
-int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data,
- s64 timestamp)
+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, timestamp);
+ return buffer->access->store_to(buffer, dataout);
}
EXPORT_SYMBOL_GPL(iio_push_to_buffer);
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 2ec266ef41a3..6eb24dbc081e 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -29,7 +29,7 @@
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
-/* IDA to assign each registered device a unique id*/
+/* IDA to assign each registered device a unique id */
static DEFINE_IDA(iio_ida);
static dev_t iio_devt;
@@ -99,6 +99,7 @@ static const char * const iio_chan_info_postfix[] = {
[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
@@ -365,6 +366,7 @@ static ssize_t iio_read_channel_info(struct device *dev,
{
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,
@@ -390,6 +392,11 @@ static ssize_t iio_read_channel_info(struct device *dev,
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);
default:
return 0;
}
@@ -729,7 +736,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
attrcount = attrcount_orig;
/*
* New channel registration method - relies on the fact a group does
- * not need to be initialized if it is name is NULL.
+ * not need to be initialized if its name is NULL.
*/
if (indio_dev->channels)
for (i = 0; i < indio_dev->num_channels; i++) {
@@ -980,6 +987,6 @@ EXPORT_SYMBOL(iio_device_unregister);
subsys_initcall(iio_init);
module_exit(iio_exit);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Industrial I/O core");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index b5afc2ff34fd..f2b78d4fe457 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -111,6 +111,7 @@ struct iio_channel *iio_channel_get(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);
@@ -130,18 +131,32 @@ struct iio_channel *iio_channel_get(const char *name, const char *channel_name)
if (c == NULL)
return ERR_PTR(-ENODEV);
- channel = kmalloc(sizeof(*channel), GFP_KERNEL);
- if (channel == NULL)
- return ERR_PTR(-ENOMEM);
+ 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)
+ 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);
}
EXPORT_SYMBOL_GPL(iio_channel_get);
@@ -229,9 +244,21 @@ void iio_channel_release_all(struct iio_channel *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 val2, ret;
+ int ret;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (chan->indio_dev->info == NULL) {
@@ -239,8 +266,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val)
goto err_unlock;
}
- ret = chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
- val, &val2, 0);
+ ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
@@ -248,6 +274,100 @@ err_unlock:
}
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_SCALE);
+ 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;
+ 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;
@@ -258,10 +378,7 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
goto err_unlock;
}
- ret = chan->indio_dev->info->read_raw(chan->indio_dev,
- chan->channel,
- val, val2,
- IIO_CHAN_INFO_SCALE);
+ ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c
index 6bf9d05f4841..5bc5c860e9ca 100644
--- a/drivers/iio/kfifo_buf.c
+++ b/drivers/iio/kfifo_buf.c
@@ -6,6 +6,7 @@
#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;
@@ -22,7 +23,8 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
return -EINVAL;
__iio_update_buffer(&buf->buffer, bytes_per_datum, length);
- return kfifo_alloc(&buf->kf, bytes_per_datum*length, GFP_KERNEL);
+ return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
+ bytes_per_datum, GFP_KERNEL);
}
static int iio_request_update_kfifo(struct iio_buffer *r)
@@ -35,6 +37,7 @@ static int iio_request_update_kfifo(struct iio_buffer *r)
kfifo_free(&buf->kf);
ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
buf->buffer.length);
+ r->stufftoread = false;
error_ret:
return ret;
}
@@ -81,6 +84,9 @@ static int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd)
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);
@@ -89,14 +95,16 @@ static int iio_set_length_kfifo(struct iio_buffer *r, int length)
}
static int iio_store_to_kfifo(struct iio_buffer *r,
- u8 *data,
- s64 timestamp)
+ u8 *data)
{
int ret;
struct iio_kfifo *kf = iio_to_kfifo(r);
- ret = kfifo_in(&kf->kf, data, r->bytes_per_datum);
- if (ret != r->bytes_per_datum)
+ ret = kfifo_in(&kf->kf, data, 1);
+ if (ret != 1)
return -EBUSY;
+ r->stufftoread = true;
+ wake_up_interruptible(&r->pollq);
+
return 0;
}
@@ -106,11 +114,18 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,
int ret, copied;
struct iio_kfifo *kf = iio_to_kfifo(r);
- if (n < r->bytes_per_datum)
+ if (n < r->bytes_per_datum || r->bytes_per_datum == 0)
return -EINVAL;
- n = rounddown(n, r->bytes_per_datum);
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;
}
@@ -136,7 +151,7 @@ struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
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);
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 91d15d2f694f..1763c9bcb98a 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -42,4 +42,14 @@ config VCNL4000
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
+ 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
index 13f8a782d292..21a8f0df1407 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.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
index 9a99f43094f0..164b62b91a4b 100644
--- a/drivers/iio/light/adjd_s311.c
+++ b/drivers/iio/light/adjd_s311.c
@@ -187,7 +187,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
*(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64)))
= time_ns;
- iio_push_to_buffer(buffer, (u8 *)data->buffer, time_ns);
+ iio_push_to_buffer(buffer, (u8 *)data->buffer);
done:
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
new file mode 100644
index 000000000000..96e3691e42c4
--- /dev/null
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -0,0 +1,385 @@
+/*
+ * 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-attributes.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_iio_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 = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .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)
+{
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int datum_sz;
+
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ if (!buffer) {
+ dev_err(&indio_dev->dev, "Buffer == NULL\n");
+ return;
+ }
+ datum_sz = buffer->access->get_bytes_per_datum(buffer);
+ if (len > datum_sz) {
+ dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+ datum_sz);
+ return;
+ }
+ iio_push_to_buffer(buffer, (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 __devinit 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) {
+ 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 __devinit 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/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
new file mode 100644
index 000000000000..c1f0cdd57037
--- /dev/null
+++ b/drivers/iio/magnetometer/Kconfig
@@ -0,0 +1,16 @@
+#
+# Magnetometer sensors
+#
+menu "Magnetometer sensors"
+
+config HID_SENSOR_MAGNETOMETER_3D
+ depends on HID_SENSOR_HUB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select HID_SENSOR_IIO_COMMON
+ tristate "HID Magenetometer 3D"
+ help
+ Say yes here to build support for the HID SENSOR
+ Magnetometer 3D.
+
+endmenu
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
new file mode 100644
index 000000000000..60dc4f2b1963
--- /dev/null
+++ b/drivers/iio/magnetometer/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O Magnetometer sensor drivers
+#
+
+obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
new file mode 100644
index 000000000000..c4f0d274f577
--- /dev/null
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -0,0 +1,419 @@
+/*
+ * 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-attributes.h"
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Magnetometer-3D: 0x200083*/
+#define DRIVER_NAME "HID-SENSOR-200083"
+
+enum magn_3d_channel {
+ CHANNEL_SCAN_INDEX_X,
+ CHANNEL_SCAN_INDEX_Y,
+ CHANNEL_SCAN_INDEX_Z,
+ MAGN_3D_CHANNEL_MAX,
+};
+
+struct magn_3d_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_iio_common common_attributes;
+ struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
+ u32 magn_val[MAGN_3D_CHANNEL_MAX];
+};
+
+static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
+ HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS,
+ HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS,
+ HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec magn_3d_channels[] = {
+ {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_X,
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Y,
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Z,
+ }
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void magn_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 magn_3d_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct magn_3d_state *magn_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 =
+ magn_state->magn[chan->scan_index].report_id;
+ address = magn_3d_addresses[chan->scan_index];
+ if (report_id >= 0)
+ *val = sensor_hub_input_attr_get_raw_value(
+ magn_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_COMPASS_3D, address,
+ report_id);
+ else {
+ *val = 0;
+ return -EINVAL;
+ }
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = magn_state->magn[CHANNEL_SCAN_INDEX_X].units;
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = hid_sensor_convert_exponent(
+ magn_state->magn[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(
+ &magn_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(
+ &magn_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 magn_3d_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct magn_3d_state *magn_state = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_write_samp_freq_value(
+ &magn_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_write_raw_hyst_value(
+ &magn_state->common_attributes, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int magn_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 magn_3d_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &magn_3d_read_raw,
+ .write_raw = &magn_3d_write_raw,
+ .write_raw_get_fmt = &magn_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)
+{
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int datum_sz;
+
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ if (!buffer) {
+ dev_err(&indio_dev->dev, "Buffer == NULL\n");
+ return;
+ }
+ datum_sz = buffer->access->get_bytes_per_datum(buffer);
+ if (len > datum_sz) {
+ dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+ datum_sz);
+ return;
+ }
+ iio_push_to_buffer(buffer, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct magn_3d_state *magn_state = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "magn_3d_proc_event [%d]\n",
+ magn_state->common_attributes.data_ready);
+ if (magn_state->common_attributes.data_ready)
+ hid_sensor_push_data(indio_dev,
+ (u8 *)magn_state->magn_val,
+ sizeof(magn_state->magn_val));
+
+ return 0;
+}
+
+/* Capture samples in local storage */
+static int magn_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 magn_3d_state *magn_state = iio_priv(indio_dev);
+ int offset;
+ int ret = -EINVAL;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS:
+ case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS:
+ case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS:
+ offset = usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS;
+ magn_state->magn_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 magn_3d_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ unsigned usage_id,
+ struct magn_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_ORIENT_MAGN_FLUX_X_AXIS + i,
+ &st->magn[CHANNEL_SCAN_INDEX_X + i]);
+ if (ret < 0)
+ break;
+ magn_3d_adjust_channel_bit_mask(channels,
+ CHANNEL_SCAN_INDEX_X + i,
+ st->magn[CHANNEL_SCAN_INDEX_X + i].size);
+ }
+ dev_dbg(&pdev->dev, "magn_3d %x:%x, %x:%x, %x:%x\n",
+ st->magn[0].index,
+ st->magn[0].report_id,
+ st->magn[1].index, st->magn[1].report_id,
+ st->magn[2].index, st->magn[2].report_id);
+
+ return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int __devinit hid_magn_3d_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ static char *name = "magn_3d";
+ struct iio_dev *indio_dev;
+ struct magn_3d_state *magn_state;
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_chan_spec *channels;
+
+ indio_dev = iio_device_alloc(sizeof(struct magn_3d_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+
+ magn_state = iio_priv(indio_dev);
+ magn_state->common_attributes.hsdev = hsdev;
+ magn_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev,
+ HID_USAGE_SENSOR_COMPASS_3D,
+ &magn_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes\n");
+ goto error_free_dev;
+ }
+
+ channels = kmemdup(magn_3d_channels,
+ sizeof(magn_3d_channels),
+ GFP_KERNEL);
+ if (!channels) {
+ dev_err(&pdev->dev, "failed to duplicate channels\n");
+ goto error_free_dev;
+ }
+
+ ret = magn_3d_parse_report(pdev, hsdev, channels,
+ HID_USAGE_SENSOR_COMPASS_3D, magn_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(magn_3d_channels);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &magn_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;
+ }
+ magn_state->common_attributes.data_ready = false;
+ ret = hid_sensor_setup_trigger(indio_dev, name,
+ &magn_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;
+ }
+
+ magn_state->callbacks.send_event = magn_3d_proc_event;
+ magn_state->callbacks.capture_sample = magn_3d_capture_sample;
+ magn_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,
+ &magn_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 __devinit hid_magn_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_COMPASS_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_magn_3d_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = hid_magn_3d_probe,
+ .remove = hid_magn_3d_remove,
+};
+module_platform_driver(hid_magn_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Magnetometer 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");