summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/configfs-iio21
-rw-r--r--Documentation/devicetree/bindings/i2c/trivial-devices.txt1
-rw-r--r--Documentation/devicetree/bindings/iio/adc/imx7d-adc.txt22
-rw-r--r--Documentation/devicetree/bindings/iio/adc/mcp3422.txt3
-rw-r--r--Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt4
-rw-r--r--Documentation/devicetree/bindings/iio/health/max30100.txt21
-rw-r--r--Documentation/devicetree/bindings/iio/light/us5182d.txt11
-rw-r--r--Documentation/iio/iio_configfs.txt93
-rw-r--r--drivers/iio/Kconfig17
-rw-r--r--drivers/iio/Makefile3
-rw-r--r--drivers/iio/adc/Kconfig25
-rw-r--r--drivers/iio/adc/Makefile2
-rw-r--r--drivers/iio/adc/imx7d_adc.c609
-rw-r--r--drivers/iio/adc/ina2xx-adc.c745
-rw-r--r--drivers/iio/adc/mcp3422.c9
-rw-r--r--drivers/iio/adc/ti-adc128s052.c13
-rw-r--r--drivers/iio/chemical/Kconfig8
-rw-r--r--drivers/iio/chemical/Makefile1
-rw-r--r--drivers/iio/chemical/ams-iaq-core.c200
-rw-r--r--drivers/iio/chemical/vz89x.c66
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c3
-rw-r--r--drivers/iio/dummy/Kconfig3
-rw-r--r--drivers/iio/health/Kconfig21
-rw-r--r--drivers/iio/health/Makefile7
-rw-r--r--drivers/iio/health/max30100.c453
-rw-r--r--drivers/iio/industrialio-configfs.c51
-rw-r--r--drivers/iio/industrialio-sw-trigger.c184
-rw-r--r--drivers/iio/inkern.c6
-rw-r--r--drivers/iio/light/us5182d.c243
-rw-r--r--drivers/iio/proximity/pulsedlight-lidar-lite-v2.c95
-rw-r--r--drivers/iio/trigger/Kconfig10
-rw-r--r--drivers/iio/trigger/Makefile2
-rw-r--r--drivers/iio/trigger/iio-trig-hrtimer.c193
-rw-r--r--drivers/staging/iio/adc/ad7192.c2
-rw-r--r--drivers/staging/iio/adc/ad7280a.c2
-rw-r--r--drivers/staging/iio/adc/ad7816.c2
-rw-r--r--drivers/staging/iio/frequency/ad9832.c2
-rw-r--r--drivers/staging/iio/frequency/ad9834.c2
-rw-r--r--drivers/staging/iio/light/tsl2x7x_core.c2
-rw-r--r--include/linux/iio/configfs.h15
-rw-r--r--include/linux/iio/sw_trigger.h70
41 files changed, 3163 insertions, 79 deletions
diff --git a/Documentation/ABI/testing/configfs-iio b/Documentation/ABI/testing/configfs-iio
new file mode 100644
index 000000000000..2483756fccf5
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-iio
@@ -0,0 +1,21 @@
+What: /config/iio
+Date: October 2015
+KernelVersion: 4.4
+Contact: linux-iio@vger.kernel.org
+Description:
+ This represents Industrial IO configuration entry point
+ directory. It contains sub-groups corresponding to IIO
+ objects.
+
+What: /config/iio/triggers
+Date: October 2015
+KernelVersion: 4.4
+Description:
+ Industrial IO software triggers directory.
+
+What: /config/iio/triggers/hrtimers
+Date: October 2015
+KernelVersion: 4.4
+Description:
+ High resolution timers directory. Creating a directory here
+ will result in creating a hrtimer trigger in the IIO subsystem.
diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
index c50cf13c852e..f6fec952d683 100644
--- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
@@ -20,6 +20,7 @@ adi,adt7476 +/-1C TDM Extended Temp Range I.C
adi,adt7490 +/-1C TDM Extended Temp Range I.C
adi,adxl345 Three-Axis Digital Accelerometer
adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too)
+ams,iaq-core AMS iAQ-Core VOC Sensor
at,24c08 i2c serial eeprom (24cxx)
atmel,24c00 i2c serial eeprom (24cxx)
atmel,24c01 i2c serial eeprom (24cxx)
diff --git a/Documentation/devicetree/bindings/iio/adc/imx7d-adc.txt b/Documentation/devicetree/bindings/iio/adc/imx7d-adc.txt
new file mode 100644
index 000000000000..5c184b940669
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/imx7d-adc.txt
@@ -0,0 +1,22 @@
+Freescale imx7d ADC bindings
+
+The devicetree bindings are for the ADC driver written for
+imx7d SoC.
+
+Required properties:
+- compatible: Should be "fsl,imx7d-adc"
+- reg: Offset and length of the register set for the ADC device
+- interrupts: The interrupt number for the ADC device
+- clocks: The root clock of the ADC controller
+- clock-names: Must contain "adc", matching entry in the clocks property
+- vref-supply: The regulator supply ADC reference voltage
+
+Example:
+adc1: adc@30610000 {
+ compatible = "fsl,imx7d-adc";
+ reg = <0x30610000 0x10000>;
+ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX7D_ADC_ROOT_CLK>;
+ clock-names = "adc";
+ vref-supply = <&reg_vcc_3v3_mcu>;
+};
diff --git a/Documentation/devicetree/bindings/iio/adc/mcp3422.txt b/Documentation/devicetree/bindings/iio/adc/mcp3422.txt
index 333139cc0bfb..dcae4ccfcc52 100644
--- a/Documentation/devicetree/bindings/iio/adc/mcp3422.txt
+++ b/Documentation/devicetree/bindings/iio/adc/mcp3422.txt
@@ -1,7 +1,8 @@
-* Microchip mcp3422/3/4/6/7/8 chip family (ADC)
+* Microchip mcp3421/2/3/4/6/7/8 chip family (ADC)
Required properties:
- compatible: Should be
+ "microchip,mcp3421" or
"microchip,mcp3422" or
"microchip,mcp3423" or
"microchip,mcp3424" or
diff --git a/Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt b/Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt
index 15ca6b47958e..daa2b2c29428 100644
--- a/Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt
+++ b/Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt
@@ -1,7 +1,7 @@
-* Texas Instruments' ADC128S052 and ADC122S021 ADC chip
+* Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip
Required properties:
- - compatible: Should be "ti,adc128s052" or "ti,adc122s021"
+ - compatible: Should be "ti,adc128s052", "ti,adc122s021" or "ti,adc124s021"
- reg: spi chip select number for the device
- vref-supply: The regulator supply for ADC reference voltage
diff --git a/Documentation/devicetree/bindings/iio/health/max30100.txt b/Documentation/devicetree/bindings/iio/health/max30100.txt
new file mode 100644
index 000000000000..f6fbac66ad06
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/health/max30100.txt
@@ -0,0 +1,21 @@
+Maxim MAX30100 heart rate and pulse oximeter sensor
+
+* https://datasheets.maximintegrated.com/en/ds/MAX30100.pdf
+
+Required properties:
+ - compatible: must be "maxim,max30100"
+ - reg: the I2C address of the sensor
+ - interrupt-parent: should be the phandle for the interrupt controller
+ - interrupts: the sole interrupt generated by the device
+
+ Refer to interrupt-controller/interrupts.txt for generic
+ interrupt client node bindings.
+
+Example:
+
+max30100@057 {
+ compatible = "maxim,max30100";
+ reg = <57>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <16 2>;
+};
diff --git a/Documentation/devicetree/bindings/iio/light/us5182d.txt b/Documentation/devicetree/bindings/iio/light/us5182d.txt
index 6f0a530144fd..a61979997f37 100644
--- a/Documentation/devicetree/bindings/iio/light/us5182d.txt
+++ b/Documentation/devicetree/bindings/iio/light/us5182d.txt
@@ -7,13 +7,24 @@ Required properties:
Optional properties:
- upisemi,glass-coef: glass attenuation factor - compensation factor of
resolution 1000 for material transmittance.
+
- upisemi,dark-ths: array of 8 elements containing 16-bit thresholds (adc
counts) corresponding to every scale.
+
- upisemi,upper-dark-gain: 8-bit dark gain compensation factor(4 int and 4
fractional bits - Q4.4) applied when light > threshold
+
- upisemi,lower-dark-gain: 8-bit dark gain compensation factor(4 int and 4
fractional bits - Q4.4) applied when light < threshold
+- upisemi,continuous: This chip has two power modes: one-shot (chip takes one
+ measurement and then shuts itself down) and continuous (
+ chip takes continuous measurements). The one-shot mode is
+ more power-friendly but the continuous mode may be more
+ reliable. If this property is specified the continuous
+ mode will be used instead of the default one-shot one for
+ raw reads.
+
If the optional properties are not specified these factors will default to the
values in the below example.
The glass-coef defaults to no compensation for the covering material.
diff --git a/Documentation/iio/iio_configfs.txt b/Documentation/iio/iio_configfs.txt
new file mode 100644
index 000000000000..f0add35cd52e
--- /dev/null
+++ b/Documentation/iio/iio_configfs.txt
@@ -0,0 +1,93 @@
+Industrial IIO configfs support
+
+1. Overview
+
+Configfs is a filesystem-based manager of kernel objects. IIO uses some
+objects that could be easily configured using configfs (e.g.: devices,
+triggers).
+
+See Documentation/filesystems/configfs/configfs.txt for more information
+about how configfs works.
+
+2. Usage
+
+In order to use configfs support in IIO we need to select it at compile
+time via CONFIG_IIO_CONFIGFS config option.
+
+Then, mount the configfs filesystem (usually under /config directory):
+
+$ mkdir /config
+$ mount -t configfs none /config
+
+At this point, all default IIO groups will be created and can be accessed
+under /config/iio. Next chapters will describe available IIO configuration
+objects.
+
+3. Software triggers
+
+One of the IIO default configfs groups is the "triggers" group. It is
+automagically accessible when the configfs is mounted and can be found
+under /config/iio/triggers.
+
+IIO software triggers implementation offers support for creating multiple
+trigger types. A new trigger type is usually implemented as a separate
+kernel module following the interface in include/linux/iio/sw_trigger.h:
+
+/*
+ * drivers/iio/trigger/iio-trig-sample.c
+ * sample kernel module implementing a new trigger type
+ */
+#include <linux/iio/sw_trigger.h>
+
+
+static struct iio_sw_trigger *iio_trig_sample_probe(const char *name)
+{
+ /*
+ * This allocates and registers an IIO trigger plus other
+ * trigger type specific initialization.
+ */
+}
+
+static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt)
+{
+ /*
+ * This undoes the actions in iio_trig_sample_probe
+ */
+}
+
+static const struct iio_sw_trigger_ops iio_trig_sample_ops = {
+ .probe = iio_trig_sample_probe,
+ .remove = iio_trig_sample_remove,
+};
+
+static struct iio_sw_trigger_type iio_trig_sample = {
+ .name = "trig-sample",
+ .owner = THIS_MODULE,
+ .ops = &iio_trig_sample_ops,
+};
+
+module_iio_sw_trigger_driver(iio_trig_sample);
+
+Each trigger type has its own directory under /config/iio/triggers. Loading
+iio-trig-sample module will create 'trig-sample' trigger type directory
+/config/iio/triggers/trig-sample.
+
+We support the following interrupt sources (trigger types):
+ * hrtimer, uses high resolution timers as interrupt source
+
+3.1 Hrtimer triggers creation and destruction
+
+Loading iio-trig-hrtimer module will register hrtimer trigger types allowing
+users to create hrtimer triggers under /config/iio/triggers/hrtimer.
+
+e.g:
+
+$ mkdir /config/triggers/hrtimer/instance1
+$ rmdir /config/triggers/hrtimer/instance1
+
+Each trigger can have one or more attributes specific to the trigger type.
+
+3.2 "hrtimer" trigger types attributes
+
+"hrtimer" trigger type doesn't have any configurable attribute from /config dir.
+It does introduce the sampling_frequency attribute to trigger directory.
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 6b8c77c97d40..505e921f0b19 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -22,6 +22,14 @@ if IIO_BUFFER
source "drivers/iio/buffer/Kconfig"
endif # IIO_BUFFER
+config IIO_CONFIGFS
+ tristate "Enable IIO configuration via configfs"
+ select CONFIGFS_FS
+ help
+ This allows configuring various IIO bits through configfs
+ (e.g. software triggers). For more info see
+ Documentation/iio/iio_configfs.txt.
+
config IIO_TRIGGER
bool "Enable triggered sampling support"
help
@@ -38,6 +46,14 @@ config IIO_CONSUMERS_PER_TRIGGER
This value controls the maximum number of consumers that a
given trigger may handle. Default is 2.
+config IIO_SW_TRIGGER
+ tristate "Enable software triggers support"
+ select IIO_CONFIGFS
+ help
+ Provides IIO core support for software triggers. A software
+ trigger can be created via configfs or directly by a driver
+ using the API provided.
+
config IIO_TRIGGERED_EVENT
tristate
select IIO_TRIGGER
@@ -53,6 +69,7 @@ source "drivers/iio/dac/Kconfig"
source "drivers/iio/dummy/Kconfig"
source "drivers/iio/frequency/Kconfig"
source "drivers/iio/gyro/Kconfig"
+source "drivers/iio/health/Kconfig"
source "drivers/iio/humidity/Kconfig"
source "drivers/iio/imu/Kconfig"
source "drivers/iio/light/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 6769f2f43e86..20f649073462 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -7,6 +7,8 @@ industrialio-y := industrialio-core.o industrialio-event.o inkern.o
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
+obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
+obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o
obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
obj-y += accel/
@@ -19,6 +21,7 @@ obj-y += dac/
obj-y += dummy/
obj-y += gyro/
obj-y += frequency/
+obj-y += health/
obj-y += humidity/
obj-y += imu/
obj-y += light/
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 9162dfefff30..605ff42c4631 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -194,6 +194,25 @@ config HI8435
This driver can also be built as a module. If so, the module will be
called hi8435.
+config INA2XX_ADC
+ tristate "Texas Instruments INA2xx Power Monitors IIO driver"
+ depends on I2C && !SENSORS_INA2XX
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to build support for TI INA2xx family of Power Monitors.
+ This driver is mutually exclusive with the HWMON version.
+
+config IMX7D_ADC
+ tristate "IMX7D ADC driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ help
+ Say yes here to build support for IMX7D ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called imx7d_adc.
+
config LP8788_ADC
tristate "LP8788 ADC driver"
depends on MFD_LP8788
@@ -332,11 +351,11 @@ config TI_ADC081C
called ti-adc081c.
config TI_ADC128S052
- tristate "Texas Instruments ADC128S052/ADC122S021"
+ tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021"
depends on SPI
help
- If you say yes here you get support for Texas Instruments ADC128S052
- and ADC122S021 chips.
+ If you say yes here you get support for Texas Instruments ADC128S052,
+ ADC122S021 and ADC124S021 chips.
This driver can also be built as a module. If so, the module will be
called ti-adc128s052.
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 91a65bf11c58..6435780e9b71 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -20,6 +20,8 @@ obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_HI8435) += hi8435.o
+obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
+obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX1363) += max1363.o
diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c
new file mode 100644
index 000000000000..e2241ee94783
--- /dev/null
+++ b/drivers/iio/adc/imx7d_adc.c
@@ -0,0 +1,609 @@
+/*
+ * Freescale i.MX7D ADC driver
+ *
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/sysfs.h>
+
+/* ADC register */
+#define IMX7D_REG_ADC_CH_A_CFG1 0x00
+#define IMX7D_REG_ADC_CH_A_CFG2 0x10
+#define IMX7D_REG_ADC_CH_B_CFG1 0x20
+#define IMX7D_REG_ADC_CH_B_CFG2 0x30
+#define IMX7D_REG_ADC_CH_C_CFG1 0x40
+#define IMX7D_REG_ADC_CH_C_CFG2 0x50
+#define IMX7D_REG_ADC_CH_D_CFG1 0x60
+#define IMX7D_REG_ADC_CH_D_CFG2 0x70
+#define IMX7D_REG_ADC_CH_SW_CFG 0x80
+#define IMX7D_REG_ADC_TIMER_UNIT 0x90
+#define IMX7D_REG_ADC_DMA_FIFO 0xa0
+#define IMX7D_REG_ADC_FIFO_STATUS 0xb0
+#define IMX7D_REG_ADC_INT_SIG_EN 0xc0
+#define IMX7D_REG_ADC_INT_EN 0xd0
+#define IMX7D_REG_ADC_INT_STATUS 0xe0
+#define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0
+#define IMX7D_REG_ADC_CHC_D_CNV_RSLT 0x100
+#define IMX7D_REG_ADC_CH_SW_CNV_RSLT 0x110
+#define IMX7D_REG_ADC_DMA_FIFO_DAT 0x120
+#define IMX7D_REG_ADC_ADC_CFG 0x130
+
+#define IMX7D_REG_ADC_CHANNEL_CFG2_BASE 0x10
+#define IMX7D_EACH_CHANNEL_REG_OFFSET 0x20
+
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN (0x1 << 31)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE BIT(30)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN BIT(29)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x) ((x) << 24)
+
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4 (0x0 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8 (0x1 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16 (0x2 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32 (0x3 << 12)
+
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4 (0x0 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8 (0x1 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16 (0x2 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32 (0x3 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64 (0x4 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128 (0x5 << 29)
+
+#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN BIT(31)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN BIT(1)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_EN BIT(0)
+
+#define IMX7D_REG_ADC_INT_CHA_COV_INT_EN BIT(8)
+#define IMX7D_REG_ADC_INT_CHB_COV_INT_EN BIT(9)
+#define IMX7D_REG_ADC_INT_CHC_COV_INT_EN BIT(10)
+#define IMX7D_REG_ADC_INT_CHD_COV_INT_EN BIT(11)
+#define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \
+ (IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \
+ IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \
+ IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \
+ IMX7D_REG_ADC_INT_CHD_COV_INT_EN)
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS 0xf00
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
+
+#define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100)
+
+enum imx7d_adc_clk_pre_div {
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,
+};
+
+enum imx7d_adc_average_num {
+ IMX7D_ADC_AVERAGE_NUM_4,
+ IMX7D_ADC_AVERAGE_NUM_8,
+ IMX7D_ADC_AVERAGE_NUM_16,
+ IMX7D_ADC_AVERAGE_NUM_32,
+};
+
+struct imx7d_adc_feature {
+ enum imx7d_adc_clk_pre_div clk_pre_div;
+ enum imx7d_adc_average_num avg_num;
+
+ u32 core_time_unit; /* impact the sample rate */
+
+ bool average_en;
+};
+
+struct imx7d_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+
+ u32 vref_uv;
+ u32 value;
+ u32 channel;
+ u32 pre_div_num;
+
+ struct regulator *vref;
+ struct imx7d_adc_feature adc_feature;
+
+ struct completion completion;
+};
+
+struct imx7d_adc_analogue_core_clk {
+ u32 pre_div;
+ u32 reg_config;
+};
+
+#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) { \
+ .pre_div = (_pre_div), \
+ .reg_config = (_reg_conf), \
+}
+
+static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),
+};
+
+#define IMX7D_ADC_CHAN(_idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec imx7d_adc_iio_channels[] = {
+ IMX7D_ADC_CHAN(0),
+ IMX7D_ADC_CHAN(1),
+ IMX7D_ADC_CHAN(2),
+ IMX7D_ADC_CHAN(3),
+ IMX7D_ADC_CHAN(4),
+ IMX7D_ADC_CHAN(5),
+ IMX7D_ADC_CHAN(6),
+ IMX7D_ADC_CHAN(7),
+ IMX7D_ADC_CHAN(8),
+ IMX7D_ADC_CHAN(9),
+ IMX7D_ADC_CHAN(10),
+ IMX7D_ADC_CHAN(11),
+ IMX7D_ADC_CHAN(12),
+ IMX7D_ADC_CHAN(13),
+ IMX7D_ADC_CHAN(14),
+ IMX7D_ADC_CHAN(15),
+};
+
+static const u32 imx7d_adc_average_num[] = {
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,
+};
+
+static void imx7d_adc_feature_config(struct imx7d_adc *info)
+{
+ info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
+ info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
+ info->adc_feature.core_time_unit = 1;
+ info->adc_feature.average_en = true;
+}
+
+static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
+{
+ struct imx7d_adc_feature *adc_feature = &info->adc_feature;
+ struct imx7d_adc_analogue_core_clk adc_analogure_clk;
+ u32 i;
+ u32 tmp_cfg1;
+ u32 sample_rate = 0;
+
+ /*
+ * Before sample set, disable channel A,B,C,D. Here we
+ * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.
+ */
+ for (i = 0; i < 4; i++) {
+ tmp_cfg1 =
+ readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
+ tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;
+ writel(tmp_cfg1,
+ info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
+ }
+
+ adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];
+ sample_rate |= adc_analogure_clk.reg_config;
+ info->pre_div_num = adc_analogure_clk.pre_div;
+
+ sample_rate |= adc_feature->core_time_unit;
+ writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);
+}
+
+static void imx7d_adc_hw_init(struct imx7d_adc *info)
+{
+ u32 cfg;
+
+ /* power up and enable adc analogue core */
+ cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);
+ cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+
+ /* enable channel A,B,C,D interrupt */
+ writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
+ info->regs + IMX7D_REG_ADC_INT_SIG_EN);
+ writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
+ info->regs + IMX7D_REG_ADC_INT_EN);
+
+ imx7d_adc_sample_rate_set(info);
+}
+
+static void imx7d_adc_channel_set(struct imx7d_adc *info)
+{
+ u32 cfg1 = 0;
+ u32 cfg2;
+ u32 channel;
+
+ channel = info->channel;
+
+ /* the channel choose single conversion, and enable average mode */
+ cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE);
+ if (info->adc_feature.average_en)
+ cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN;
+
+ /*
+ * physical channel 0 chose logical channel A
+ * physical channel 1 chose logical channel B
+ * physical channel 2 chose logical channel C
+ * physical channel 3 chose logical channel D
+ */
+ cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);
+
+ /*
+ * read register REG_ADC_CH_A\B\C\D_CFG2, according to the
+ * channel chosen
+ */
+ cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
+ IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
+
+ cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];
+
+ /*
+ * write the register REG_ADC_CH_A\B\C\D_CFG2, according to
+ * the channel chosen
+ */
+ writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
+ IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
+ writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel);
+}
+
+static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
+{
+ /* input clock is always 24MHz */
+ u32 input_clk = 24000000;
+ u32 analogue_core_clk;
+ u32 core_time_unit = info->adc_feature.core_time_unit;
+ u32 tmp;
+
+ analogue_core_clk = input_clk / info->pre_div_num;
+ tmp = (core_time_unit + 1) * 6;
+
+ return analogue_core_clk / tmp;
+}
+
+static int imx7d_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ u32 channel;
+ long ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ reinit_completion(&info->completion);
+
+ channel = chan->channel & 0x03;
+ info->channel = channel;
+ imx7d_adc_channel_set(info);
+
+ ret = wait_for_completion_interruptible_timeout
+ (&info->completion, IMX7D_ADC_TIMEOUT);
+ if (ret == 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return -ETIMEDOUT;
+ }
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+
+ *val = info->value;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ info->vref_uv = regulator_get_voltage(info->vref);
+ *val = info->vref_uv / 1000;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = imx7d_adc_get_sample_rate(info);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int imx7d_adc_read_data(struct imx7d_adc *info)
+{
+ u32 channel;
+ u32 value;
+
+ channel = info->channel & 0x03;
+
+ /*
+ * channel A and B conversion result share one register,
+ * bit[27~16] is the channel B conversion result,
+ * bit[11~0] is the channel A conversion result.
+ * channel C and D is the same.
+ */
+ if (channel < 2)
+ value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);
+ else
+ value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);
+ if (channel & 0x1) /* channel B or D */
+ value = (value >> 16) & 0xFFF;
+ else /* channel A or C */
+ value &= 0xFFF;
+
+ return value;
+}
+
+static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
+{
+ struct imx7d_adc *info = (struct imx7d_adc *)dev_id;
+ int status;
+
+ status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {
+ info->value = imx7d_adc_read_data(info);
+ complete(&info->completion);
+
+ /*
+ * The register IMX7D_REG_ADC_INT_STATUS can't clear
+ * itself after read operation, need software to write
+ * 0 to the related bit. Here we clear the channel A/B/C/D
+ * conversion finished flag.
+ */
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ }
+
+ /*
+ * If the channel A/B/C/D conversion timeout, report it and clear these
+ * timeout flags.
+ */
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
+ pr_err("%s: ADC got conversion time out interrupt: 0x%08x\n",
+ dev_name(info->dev), status);
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int imx7d_adc_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG)
+ return -EINVAL;
+
+ *readval = readl(info->regs + reg);
+
+ return 0;
+}
+
+static const struct iio_info imx7d_adc_iio_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &imx7d_adc_read_raw,
+ .debugfs_reg_access = &imx7d_adc_reg_access,
+};
+
+static const struct of_device_id imx7d_adc_match[] = {
+ { .compatible = "fsl,imx7d-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx7d_adc_match);
+
+static void imx7d_adc_power_down(struct imx7d_adc *info)
+{
+ u32 adc_cfg;
+
+ adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;
+ adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+}
+
+static int imx7d_adc_probe(struct platform_device *pdev)
+{
+ struct imx7d_adc *info;
+ struct iio_dev *indio_dev;
+ struct resource *mem;
+ int irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ info->dev = &pdev->dev;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(info->regs)) {
+ ret = PTR_ERR(info->regs);
+ dev_err(&pdev->dev,
+ "Failed to remap adc memory, err = %d\n", ret);
+ return ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "No irq resource?\n");
+ return irq;
+ }
+
+ info->clk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(info->clk)) {
+ ret = PTR_ERR(info->clk);
+ dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret);
+ return ret;
+ }
+
+ info->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(info->vref)) {
+ ret = PTR_ERR(info->vref);
+ dev_err(&pdev->dev,
+ "Failed getting reference voltage, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(info->vref);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Can't enable adc reference top voltage, err = %d\n",
+ ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&info->completion);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &imx7d_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = imx7d_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the clock.\n");
+ goto error_adc_clk_enable;
+ }
+
+ ret = devm_request_irq(info->dev, irq,
+ imx7d_adc_isr, 0,
+ dev_name(&pdev->dev), info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed requesting irq, irq = %d\n", irq);
+ goto error_iio_device_register;
+ }
+
+ imx7d_adc_feature_config(info);
+ imx7d_adc_hw_init(info);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ imx7d_adc_power_down(info);
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ goto error_iio_device_register;
+ }
+
+ return 0;
+
+error_iio_device_register:
+ clk_disable_unprepare(info->clk);
+error_adc_clk_enable:
+ regulator_disable(info->vref);
+
+ return ret;
+}
+
+static int imx7d_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ imx7d_adc_power_down(info);
+
+ clk_disable_unprepare(info->clk);
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static int __maybe_unused imx7d_adc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ imx7d_adc_power_down(info);
+
+ clk_disable_unprepare(info->clk);
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static int __maybe_unused imx7d_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(info->vref);
+ if (ret) {
+ dev_err(info->dev,
+ "Can't enable adc reference top voltage, err = %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(info->dev,
+ "Could not prepare or enable clock.\n");
+ regulator_disable(info->vref);
+ return ret;
+ }
+
+ imx7d_adc_hw_init(info);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, imx7d_adc_resume);
+
+static struct platform_driver imx7d_adc_driver = {
+ .probe = imx7d_adc_probe,
+ .remove = imx7d_adc_remove,
+ .driver = {
+ .name = "imx7d_adc",
+ .of_match_table = imx7d_adc_match,
+ .pm = &imx7d_adc_pm_ops,
+ },
+};
+
+module_platform_driver(imx7d_adc_driver);
+
+MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
+MODULE_DESCRIPTION("Freeacale IMX7D ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
new file mode 100644
index 000000000000..4c18c4cc8939
--- /dev/null
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -0,0 +1,745 @@
+/*
+ * INA2XX Current and Power Monitors
+ *
+ * Copyright 2015 Baylibre SAS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Based on linux/drivers/iio/adc/ad7291.c
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Based on linux/drivers/hwmon/ina2xx.c
+ * Copyright 2012 Lothar Felten <l-felten@ti.com>
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * IIO driver for INA219-220-226-230-231
+ *
+ * Configurable 7-bit I2C slave address from 0x40 to 0x4F
+ */
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/platform_data/ina2xx.h>
+
+#include <linux/util_macros.h>
+
+/* INA2XX registers definition */
+#define INA2XX_CONFIG 0x00
+#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
+#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */
+#define INA2XX_POWER 0x03 /* readonly */
+#define INA2XX_CURRENT 0x04 /* readonly */
+#define INA2XX_CALIBRATION 0x05
+
+#define INA226_ALERT_MASK 0x06
+#define INA266_CVRF BIT(3)
+
+#define INA2XX_MAX_REGISTERS 8
+
+/* settings - depend on use case */
+#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */
+#define INA226_CONFIG_DEFAULT 0x4327
+#define INA226_DEFAULT_AVG 4
+#define INA226_DEFAULT_IT 1110
+
+#define INA2XX_RSHUNT_DEFAULT 10000
+
+/*
+ * bit mask for reading the averaging setting in the configuration register
+ * FIXME: use regmap_fields.
+ */
+#define INA2XX_MODE_MASK GENMASK(3, 0)
+
+#define INA226_AVG_MASK GENMASK(11, 9)
+#define INA226_SHIFT_AVG(val) ((val) << 9)
+
+/* Integration time for VBus */
+#define INA226_ITB_MASK GENMASK(8, 6)
+#define INA226_SHIFT_ITB(val) ((val) << 6)
+
+/* Integration time for VShunt */
+#define INA226_ITS_MASK GENMASK(5, 3)
+#define INA226_SHIFT_ITS(val) ((val) << 3)
+
+/* Cosmetic macro giving the sampling period for a full P=UxI cycle */
+#define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \
+ * c->avg)
+
+static bool ina2xx_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return (reg == INA2XX_CONFIG) || (reg > INA2XX_CURRENT);
+}
+
+static bool ina2xx_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return (reg != INA2XX_CONFIG);
+}
+
+static inline bool is_signed_reg(unsigned int reg)
+{
+ return (reg == INA2XX_SHUNT_VOLTAGE) || (reg == INA2XX_CURRENT);
+}
+
+static const struct regmap_config ina2xx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = INA2XX_MAX_REGISTERS,
+ .writeable_reg = ina2xx_is_writeable_reg,
+ .volatile_reg = ina2xx_is_volatile_reg,
+};
+
+enum ina2xx_ids { ina219, ina226 };
+
+struct ina2xx_config {
+ u16 config_default;
+ int calibration_factor;
+ int shunt_div;
+ int bus_voltage_shift;
+ int bus_voltage_lsb; /* uV */
+ int power_lsb; /* uW */
+};
+
+struct ina2xx_chip_info {
+ struct regmap *regmap;
+ struct task_struct *task;
+ const struct ina2xx_config *config;
+ struct mutex state_lock;
+ unsigned int shunt_resistor;
+ int avg;
+ s64 prev_ns; /* track buffer capture time, check for underruns*/
+ int int_time_vbus; /* Bus voltage integration time uS */
+ int int_time_vshunt; /* Shunt voltage integration time uS */
+ bool allow_async_readout;
+};
+
+static const struct ina2xx_config ina2xx_config[] = {
+ [ina219] = {
+ .config_default = INA219_CONFIG_DEFAULT,
+ .calibration_factor = 40960000,
+ .shunt_div = 100,
+ .bus_voltage_shift = 3,
+ .bus_voltage_lsb = 4000,
+ .power_lsb = 20000,
+ },
+ [ina226] = {
+ .config_default = INA226_CONFIG_DEFAULT,
+ .calibration_factor = 5120000,
+ .shunt_div = 400,
+ .bus_voltage_shift = 0,
+ .bus_voltage_lsb = 1250,
+ .power_lsb = 25000,
+ },
+};
+
+static int ina2xx_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned int regval;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(chip->regmap, chan->address, &regval);
+ if (ret < 0)
+ return ret;
+
+ if (is_signed_reg(chan->address))
+ *val = (s16) regval;
+ else
+ *val = regval;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = chip->avg;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ if (chan->address == INA2XX_SHUNT_VOLTAGE)
+ *val2 = chip->int_time_vshunt;
+ else
+ *val2 = chip->int_time_vbus;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /*
+ * Sample freq is read only, it is a consequence of
+ * 1/AVG*(CT_bus+CT_shunt).
+ */
+ *val = DIV_ROUND_CLOSEST(1000000, SAMPLING_PERIOD(chip));
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->address) {
+ case INA2XX_SHUNT_VOLTAGE:
+ /* processed (mV) = raw*1000/shunt_div */
+ *val2 = chip->config->shunt_div;
+ *val = 1000;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_BUS_VOLTAGE:
+ /* processed (mV) = raw*lsb (uV) / (1000 << shift) */
+ *val = chip->config->bus_voltage_lsb;
+ *val2 = 1000 << chip->config->bus_voltage_shift;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_POWER:
+ /* processed (mW) = raw*lsb (uW) / 1000 */
+ *val = chip->config->power_lsb;
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_CURRENT:
+ /* processed (mA) = raw (mA) */
+ *val = 1;
+ return IIO_VAL_INT;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Available averaging rates for ina226. The indices correspond with
+ * the bit values expected by the chip (according to the ina226 datasheet,
+ * table 3 AVG bit settings, found at
+ * http://www.ti.com/lit/ds/symlink/ina226.pdf.
+ */
+static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 };
+
+static int ina226_set_average(struct ina2xx_chip_info *chip, unsigned int val,
+ unsigned int *config)
+{
+ int bits;
+
+ if (val > 1024 || val < 1)
+ return -EINVAL;
+
+ bits = find_closest(val, ina226_avg_tab,
+ ARRAY_SIZE(ina226_avg_tab));
+
+ chip->avg = ina226_avg_tab[bits];
+
+ *config &= ~INA226_AVG_MASK;
+ *config |= INA226_SHIFT_AVG(bits) & INA226_AVG_MASK;
+
+ return 0;
+}
+
+/* Conversion times in uS */
+static const int ina226_conv_time_tab[] = { 140, 204, 332, 588, 1100,
+ 2116, 4156, 8244 };
+
+static int ina226_set_int_time_vbus(struct ina2xx_chip_info *chip,
+ unsigned int val_us, unsigned int *config)
+{
+ int bits;
+
+ if (val_us > 8244 || val_us < 140)
+ return -EINVAL;
+
+ bits = find_closest(val_us, ina226_conv_time_tab,
+ ARRAY_SIZE(ina226_conv_time_tab));
+
+ chip->int_time_vbus = ina226_conv_time_tab[bits];
+
+ *config &= ~INA226_ITB_MASK;
+ *config |= INA226_SHIFT_ITB(bits) & INA226_ITB_MASK;
+
+ return 0;
+}
+
+static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip,
+ unsigned int val_us, unsigned int *config)
+{
+ int bits;
+
+ if (val_us > 8244 || val_us < 140)
+ return -EINVAL;
+
+ bits = find_closest(val_us, ina226_conv_time_tab,
+ ARRAY_SIZE(ina226_conv_time_tab));
+
+ chip->int_time_vshunt = ina226_conv_time_tab[bits];
+
+ *config &= ~INA226_ITS_MASK;
+ *config |= INA226_SHIFT_ITS(bits) & INA226_ITS_MASK;
+
+ return 0;
+}
+
+static int ina2xx_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ int ret;
+ unsigned int config, tmp;
+
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ mutex_lock(&chip->state_lock);
+
+ ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config);
+ if (ret < 0)
+ goto _err;
+
+ tmp = config;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = ina226_set_average(chip, val, &tmp);
+ break;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ if (chan->address == INA2XX_SHUNT_VOLTAGE)
+ ret = ina226_set_int_time_vshunt(chip, val2, &tmp);
+ else
+ ret = ina226_set_int_time_vbus(chip, val2, &tmp);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (!ret && (tmp != config))
+ ret = regmap_write(chip->regmap, INA2XX_CONFIG, tmp);
+_err:
+ mutex_unlock(&chip->state_lock);
+
+ return ret;
+}
+
+
+static ssize_t ina2xx_allow_async_readout_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "%d\n", chip->allow_async_readout);
+}
+
+static ssize_t ina2xx_allow_async_readout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+ bool val;
+ int ret;
+
+ ret = strtobool((const char *) buf, &val);
+ if (ret)
+ return ret;
+
+ chip->allow_async_readout = val;
+
+ return len;
+}
+
+static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val)
+{
+ if (val <= 0 || val > chip->config->calibration_factor)
+ return -EINVAL;
+
+ chip->shunt_resistor = val;
+ return 0;
+}
+
+static ssize_t ina2xx_shunt_resistor_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "%d\n", chip->shunt_resistor);
+}
+
+static ssize_t ina2xx_shunt_resistor_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul((const char *) buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = set_shunt_resistor(chip, val);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+#define INA2XX_CHAN(_type, _index, _address) { \
+ .type = (_type), \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
+ | BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ } \
+}
+
+/*
+ * Sampling Freq is a consequence of the integration times of
+ * the Voltage channels.
+ */
+#define INA2XX_CHAN_VOLTAGE(_index, _address) { \
+ .type = IIO_VOLTAGE, \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_INT_TIME), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ } \
+}
+
+static const struct iio_chan_spec ina2xx_channels[] = {
+ INA2XX_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE),
+ INA2XX_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE),
+ INA2XX_CHAN(IIO_CURRENT, 2, INA2XX_CURRENT),
+ INA2XX_CHAN(IIO_POWER, 3, INA2XX_POWER),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static int ina2xx_work_buffer(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned short data[8];
+ int bit, ret, i = 0;
+ unsigned long buffer_us, elapsed_us;
+ s64 time_a, time_b;
+ unsigned int alert;
+
+ time_a = iio_get_time_ns();
+
+ /*
+ * Because the timer thread and the chip conversion clock
+ * are asynchronous, the period difference will eventually
+ * result in reading V[k-1] again, or skip V[k] at time Tk.
+ * In order to resync the timer with the conversion process
+ * we check the ConVersionReadyFlag.
+ * On hardware that supports using the ALERT pin to toggle a
+ * GPIO a triggered buffer could be used instead.
+ * For now, we pay for that extra read of the ALERT register
+ */
+ if (!chip->allow_async_readout)
+ do {
+ ret = regmap_read(chip->regmap, INA226_ALERT_MASK,
+ &alert);
+ if (ret < 0)
+ return ret;
+
+ alert &= INA266_CVRF;
+ trace_printk("Conversion ready: %d\n", !!alert);
+
+ } while (!alert);
+
+ /*
+ * Single register reads: bulk_read will not work with ina226
+ * as there is no auto-increment of the address register for
+ * data length longer than 16bits.
+ */
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ unsigned int val;
+
+ ret = regmap_read(chip->regmap,
+ INA2XX_SHUNT_VOLTAGE + bit, &val);
+ if (ret < 0)
+ return ret;
+
+ data[i++] = val;
+ }
+
+ time_b = iio_get_time_ns();
+
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ (unsigned int *)data, time_a);
+
+ buffer_us = (unsigned long)(time_b - time_a) / 1000;
+ elapsed_us = (unsigned long)(time_a - chip->prev_ns) / 1000;
+
+ trace_printk("uS: elapsed: %lu, buf: %lu\n", elapsed_us, buffer_us);
+
+ chip->prev_ns = time_a;
+
+ return buffer_us;
+};
+
+static int ina2xx_capture_thread(void *data)
+{
+ struct iio_dev *indio_dev = (struct iio_dev *)data;
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned int sampling_us = SAMPLING_PERIOD(chip);
+ int buffer_us;
+
+ /*
+ * Poll a bit faster than the chip internal Fs, in case
+ * we wish to sync with the conversion ready flag.
+ */
+ if (!chip->allow_async_readout)
+ sampling_us -= 200;
+
+ do {
+ buffer_us = ina2xx_work_buffer(indio_dev);
+ if (buffer_us < 0)
+ return buffer_us;
+
+ if (sampling_us > buffer_us)
+ udelay(sampling_us - buffer_us);
+
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned int sampling_us = SAMPLING_PERIOD(chip);
+
+ trace_printk("Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n",
+ (unsigned int)(*indio_dev->active_scan_mask),
+ 1000000/sampling_us, chip->avg);
+
+ trace_printk("Expected work period: %u us\n", sampling_us);
+ trace_printk("Async readout mode: %d\n", chip->allow_async_readout);
+
+ chip->prev_ns = iio_get_time_ns();
+
+ chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev,
+ "%s:%d-%uus", indio_dev->name, indio_dev->id,
+ sampling_us);
+
+ return PTR_ERR_OR_ZERO(chip->task);
+}
+
+static int ina2xx_buffer_disable(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+
+ if (chip->task) {
+ kthread_stop(chip->task);
+ chip->task = NULL;
+ }
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ina2xx_setup_ops = {
+ .postenable = &ina2xx_buffer_enable,
+ .predisable = &ina2xx_buffer_disable,
+};
+
+static int ina2xx_debug_reg(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval, unsigned *readval)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+
+ if (!readval)
+ return regmap_write(chip->regmap, reg, writeval);
+
+ return regmap_read(chip->regmap, reg, readval);
+}
+
+/* Possible integration times for vshunt and vbus */
+static IIO_CONST_ATTR_INT_TIME_AVAIL \
+ ("0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244");
+
+static IIO_DEVICE_ATTR(in_allow_async_readout, S_IRUGO | S_IWUSR,
+ ina2xx_allow_async_readout_show,
+ ina2xx_allow_async_readout_store, 0);
+
+static IIO_DEVICE_ATTR(in_shunt_resistor, S_IRUGO | S_IWUSR,
+ ina2xx_shunt_resistor_show,
+ ina2xx_shunt_resistor_store, 0);
+
+static struct attribute *ina2xx_attributes[] = {
+ &iio_dev_attr_in_allow_async_readout.dev_attr.attr,
+ &iio_const_attr_integration_time_available.dev_attr.attr,
+ &iio_dev_attr_in_shunt_resistor.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ina2xx_attribute_group = {
+ .attrs = ina2xx_attributes,
+};
+
+static const struct iio_info ina2xx_info = {
+ .debugfs_reg_access = &ina2xx_debug_reg,
+ .read_raw = &ina2xx_read_raw,
+ .write_raw = &ina2xx_write_raw,
+ .attrs = &ina2xx_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+/* Initialize the configuration and calibration registers. */
+static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config)
+{
+ u16 regval;
+ int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config);
+
+ if (ret < 0)
+ return ret;
+ /*
+ * Set current LSB to 1mA, shunt is in uOhms
+ * (equation 13 in datasheet). We hardcode a Current_LSB
+ * of 1.0 x10-6. The only remaining parameter is RShunt.
+ * There is no need to expose the CALIBRATION register
+ * to the user for now.
+ */
+ regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor,
+ chip->shunt_resistor);
+
+ return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
+}
+
+static int ina2xx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ina2xx_chip_info *chip;
+ struct iio_dev *indio_dev;
+ struct iio_buffer *buffer;
+ int ret;
+ unsigned int val;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+
+ chip->config = &ina2xx_config[id->driver_data];
+
+ if (of_property_read_u32(client->dev.of_node,
+ "shunt-resistor", &val) < 0) {
+ struct ina2xx_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+
+ if (pdata)
+ val = pdata->shunt_uohms;
+ else
+ val = INA2XX_RSHUNT_DEFAULT;
+ }
+
+ ret = set_shunt_resistor(chip, val);
+ if (ret)
+ return ret;
+
+ mutex_init(&chip->state_lock);
+
+ /* This is only used for device removal purposes. */
+ i2c_set_clientdata(client, indio_dev);
+
+ indio_dev->name = id->name;
+ indio_dev->channels = ina2xx_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ina2xx_channels);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &ina2xx_info;
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+
+ chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ dev_err(&client->dev, "failed to allocate register map\n");
+ return PTR_ERR(chip->regmap);
+ }
+
+ /* Patch the current config register with default. */
+ val = chip->config->config_default;
+
+ if (id->driver_data == ina226) {
+ ina226_set_average(chip, INA226_DEFAULT_AVG, &val);
+ ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val);
+ ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val);
+ }
+
+ ret = ina2xx_init(chip, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "error configuring the device: %d\n",
+ ret);
+ return -ENODEV;
+ }
+
+ buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
+ if (!buffer)
+ return -ENOMEM;
+
+ indio_dev->setup_ops = &ina2xx_setup_ops;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ return iio_device_register(indio_dev);
+}
+
+
+static int ina2xx_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ /* Powerdown */
+ return regmap_update_bits(chip->regmap, INA2XX_CONFIG,
+ INA2XX_MODE_MASK, 0);
+}
+
+
+static const struct i2c_device_id ina2xx_id[] = {
+ {"ina219", ina219},
+ {"ina220", ina219},
+ {"ina226", ina226},
+ {"ina230", ina226},
+ {"ina231", ina226},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ina2xx_id);
+
+static struct i2c_driver ina2xx_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+ .probe = ina2xx_probe,
+ .remove = ina2xx_remove,
+ .id_table = ina2xx_id,
+};
+
+module_i2c_driver(ina2xx_driver);
+
+MODULE_AUTHOR("Marc Titinger <marc.titinger@baylibre.com>");
+MODULE_DESCRIPTION("Texas Instruments INA2XX ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c
index 3555122008b4..6eca7aea8a37 100644
--- a/drivers/iio/adc/mcp3422.c
+++ b/drivers/iio/adc/mcp3422.c
@@ -305,6 +305,10 @@ static const struct attribute_group mcp3422_attribute_group = {
.attrs = mcp3422_attributes,
};
+static const struct iio_chan_spec mcp3421_channels[] = {
+ MCP3422_CHAN(0),
+};
+
static const struct iio_chan_spec mcp3422_channels[] = {
MCP3422_CHAN(0),
MCP3422_CHAN(1),
@@ -352,6 +356,10 @@ static int mcp3422_probe(struct i2c_client *client,
indio_dev->info = &mcp3422_info;
switch (adc->id) {
+ case 1:
+ indio_dev->channels = mcp3421_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels);
+ break;
case 2:
case 3:
case 6:
@@ -383,6 +391,7 @@ static int mcp3422_probe(struct i2c_client *client,
}
static const struct i2c_device_id mcp3422_id[] = {
+ { "mcp3421", 1 },
{ "mcp3422", 2 },
{ "mcp3423", 3 },
{ "mcp3424", 4 },
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index ff6f7f63c8d9..bc58867d6e8d 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -1,10 +1,11 @@
/*
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
*
- * Driver for Texas Instruments' ADC128S052 and ADC122S021 ADC chip.
+ * Driver for Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip.
* Datasheets can be found here:
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
* http://www.ti.com/lit/ds/symlink/adc122s021.pdf
+ * http://www.ti.com/lit/ds/symlink/adc124s021.pdf
*
* 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
@@ -114,9 +115,17 @@ static const struct iio_chan_spec adc122s021_channels[] = {
ADC128_VOLTAGE_CHANNEL(1),
};
+static const struct iio_chan_spec adc124s021_channels[] = {
+ ADC128_VOLTAGE_CHANNEL(0),
+ ADC128_VOLTAGE_CHANNEL(1),
+ ADC128_VOLTAGE_CHANNEL(2),
+ ADC128_VOLTAGE_CHANNEL(3),
+};
+
static const struct adc128_configuration adc128_config[] = {
{ adc128s052_channels, ARRAY_SIZE(adc128s052_channels) },
{ adc122s021_channels, ARRAY_SIZE(adc122s021_channels) },
+ { adc124s021_channels, ARRAY_SIZE(adc124s021_channels) },
};
static const struct iio_info adc128_info = {
@@ -177,6 +186,7 @@ static int adc128_remove(struct spi_device *spi)
static const struct of_device_id adc128_of_match[] = {
{ .compatible = "ti,adc128s052", },
{ .compatible = "ti,adc122s021", },
+ { .compatible = "ti,adc124s021", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, adc128_of_match);
@@ -184,6 +194,7 @@ MODULE_DEVICE_TABLE(of, adc128_of_match);
static const struct spi_device_id adc128_id[] = {
{ "adc128s052", 0}, /* index into adc128_config */
{ "adc122s021", 1},
+ { "adc124s021", 2},
{ }
};
MODULE_DEVICE_TABLE(spi, adc128_id);
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index 3061b7299f0f..f16de61be46d 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -4,6 +4,14 @@
menu "Chemical Sensors"
+config IAQCORE
+ tristate "AMS iAQ-Core VOC sensors"
+ depends on I2C
+ help
+ Say Y here to build I2C interface support for the AMS
+ iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
+ sensors
+
config VZ89X
tristate "SGX Sensortech MiCS VZ89X VOC sensor"
depends on I2C
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
index 7292f2ded587..167861fadfab 100644
--- a/drivers/iio/chemical/Makefile
+++ b/drivers/iio/chemical/Makefile
@@ -3,4 +3,5 @@
#
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_VZ89X) += vz89x.o
diff --git a/drivers/iio/chemical/ams-iaq-core.c b/drivers/iio/chemical/ams-iaq-core.c
new file mode 100644
index 000000000000..41a8e6f2e31d
--- /dev/null
+++ b/drivers/iio/chemical/ams-iaq-core.c
@@ -0,0 +1,200 @@
+/*
+ * ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors
+ *
+ * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#define AMS_IAQCORE_DATA_SIZE 9
+
+#define AMS_IAQCORE_VOC_CO2_IDX 0
+#define AMS_IAQCORE_VOC_RESISTANCE_IDX 1
+#define AMS_IAQCORE_VOC_TVOC_IDX 2
+
+struct ams_iaqcore_reading {
+ __be16 co2_ppm;
+ u8 status;
+ __be32 resistance;
+ __be16 voc_ppb;
+} __attribute__((__packed__));
+
+struct ams_iaqcore_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ unsigned long last_update;
+
+ struct ams_iaqcore_reading buffer;
+};
+
+static const struct iio_chan_spec ams_iaqcore_channels[] = {
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_CO2,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = AMS_IAQCORE_VOC_CO2_IDX,
+ },
+ {
+ .type = IIO_RESISTANCE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = AMS_IAQCORE_VOC_RESISTANCE_IDX,
+ },
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_VOC,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = AMS_IAQCORE_VOC_TVOC_IDX,
+ },
+};
+
+static int ams_iaqcore_read_measurement(struct ams_iaqcore_data *data)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = client->flags | I2C_M_RD,
+ .len = AMS_IAQCORE_DATA_SIZE,
+ .buf = (char *) &data->buffer,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return (ret == AMS_IAQCORE_DATA_SIZE) ? 0 : ret;
+}
+
+static int ams_iaqcore_get_measurement(struct ams_iaqcore_data *data)
+{
+ int ret;
+
+ /* sensor can only be polled once a second max per datasheet */
+ if (!time_after(jiffies, data->last_update + HZ))
+ return 0;
+
+ ret = ams_iaqcore_read_measurement(data);
+ if (ret < 0)
+ return ret;
+
+ data->last_update = jiffies;
+
+ return 0;
+}
+
+static int ams_iaqcore_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct ams_iaqcore_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (mask != IIO_CHAN_INFO_PROCESSED)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = ams_iaqcore_get_measurement(data);
+
+ if (ret)
+ goto err_out;
+
+ switch (chan->address) {
+ case AMS_IAQCORE_VOC_CO2_IDX:
+ *val = 0;
+ *val2 = be16_to_cpu(data->buffer.co2_ppm);
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case AMS_IAQCORE_VOC_RESISTANCE_IDX:
+ *val = be32_to_cpu(data->buffer.resistance);
+ ret = IIO_VAL_INT;
+ break;
+ case AMS_IAQCORE_VOC_TVOC_IDX:
+ *val = 0;
+ *val2 = be16_to_cpu(data->buffer.voc_ppb);
+ ret = IIO_VAL_INT_PLUS_NANO;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+err_out:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static const struct iio_info ams_iaqcore_info = {
+ .read_raw = ams_iaqcore_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ams_iaqcore_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct ams_iaqcore_data *data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ /* so initial reading will complete */
+ data->last_update = jiffies - HZ;
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &ams_iaqcore_info,
+ indio_dev->name = dev_name(&client->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ indio_dev->channels = ams_iaqcore_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ams_iaqcore_channels);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id ams_iaqcore_id[] = {
+ { "ams-iaq-core", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ams_iaqcore_id);
+
+static const struct of_device_id ams_iaqcore_dt_ids[] = {
+ { .compatible = "ams,iaq-core" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ams_iaqcore_dt_ids);
+
+static struct i2c_driver ams_iaqcore_driver = {
+ .driver = {
+ .name = "ams-iaq-core",
+ .of_match_table = of_match_ptr(ams_iaqcore_dt_ids),
+ },
+ .probe = ams_iaqcore_probe,
+ .id_table = ams_iaqcore_id,
+};
+module_i2c_driver(ams_iaqcore_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c
index 11e59a5a5112..b8b804923230 100644
--- a/drivers/iio/chemical/vz89x.c
+++ b/drivers/iio/chemical/vz89x.c
@@ -34,8 +34,9 @@
struct vz89x_data {
struct i2c_client *client;
struct mutex lock;
- unsigned long last_update;
+ int (*xfer)(struct vz89x_data *data, u8 cmd);
+ unsigned long last_update;
u8 buffer[VZ89X_REG_MEASUREMENT_SIZE];
};
@@ -100,27 +101,60 @@ static int vz89x_measurement_is_valid(struct vz89x_data *data)
return !!(data->buffer[VZ89X_REG_MEASUREMENT_SIZE - 1] > 0);
}
-static int vz89x_get_measurement(struct vz89x_data *data)
+static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd)
{
+ struct i2c_client *client = data->client;
+ struct i2c_msg msg[2];
int ret;
- int i;
+ u8 buf[3] = { cmd, 0, 0};
- /* sensor can only be polled once a second max per datasheet */
- if (!time_after(jiffies, data->last_update + HZ))
- return 0;
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags;
+ msg[0].len = 3;
+ msg[0].buf = (char *) &buf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = client->flags | I2C_M_RD;
+ msg[1].len = VZ89X_REG_MEASUREMENT_SIZE;
+ msg[1].buf = (char *) &data->buffer;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
- ret = i2c_smbus_write_word_data(data->client,
- VZ89X_REG_MEASUREMENT, 0);
+ return (ret == 2) ? 0 : ret;
+}
+
+static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+ int i;
+
+ ret = i2c_smbus_write_word_data(client, cmd, 0);
if (ret < 0)
return ret;
for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
- ret = i2c_smbus_read_byte(data->client);
+ ret = i2c_smbus_read_byte(client);
if (ret < 0)
return ret;
data->buffer[i] = ret;
}
+ return 0;
+}
+
+static int vz89x_get_measurement(struct vz89x_data *data)
+{
+ int ret;
+
+ /* sensor can only be polled once a second max per datasheet */
+ if (!time_after(jiffies, data->last_update + HZ))
+ return 0;
+
+ ret = data->xfer(data, VZ89X_REG_MEASUREMENT);
+ if (ret < 0)
+ return ret;
+
ret = vz89x_measurement_is_valid(data);
if (ret)
return -EAGAIN;
@@ -204,15 +238,19 @@ static int vz89x_probe(struct i2c_client *client,
struct iio_dev *indio_dev;
struct vz89x_data *data;
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_BYTE))
- return -ENODEV;
-
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
-
data = iio_priv(indio_dev);
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ data->xfer = vz89x_i2c_xfer;
+ else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
+ data->xfer = vz89x_smbus_xfer;
+ else
+ return -ENOTSUPP;
+
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->last_update = jiffies - HZ;
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 25258e2c1a82..8447c31e27f2 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -18,9 +18,6 @@
#include <asm/unaligned.h>
#include <linux/iio/common/st_sensors.h>
-
-#define ST_SENSORS_WAI_ADDRESS 0x0f
-
static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
{
return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;
diff --git a/drivers/iio/dummy/Kconfig b/drivers/iio/dummy/Kconfig
index e8676aa97d62..71805ced1aae 100644
--- a/drivers/iio/dummy/Kconfig
+++ b/drivers/iio/dummy/Kconfig
@@ -5,7 +5,8 @@ menu "IIO dummy driver"
depends on IIO
config IIO_DUMMY_EVGEN
- tristate
+ select IRQ_WORK
+ tristate
config IIO_SIMPLE_DUMMY
tristate "An example driver with no hardware requirements"
diff --git a/drivers/iio/health/Kconfig b/drivers/iio/health/Kconfig
new file mode 100644
index 000000000000..a647679da805
--- /dev/null
+++ b/drivers/iio/health/Kconfig
@@ -0,0 +1,21 @@
+#
+# Health sensors
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Health sensors"
+
+config MAX30100
+ tristate "MAX30100 heart rate and pulse oximeter sensor"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say Y here to build I2C interface support for the Maxim
+ MAX30100 heart rate, and pulse oximeter sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max30100.
+
+endmenu
diff --git a/drivers/iio/health/Makefile b/drivers/iio/health/Makefile
new file mode 100644
index 000000000000..7c475d7faad8
--- /dev/null
+++ b/drivers/iio/health/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for IIO Health sensors
+#
+
+# When adding new entries keep the list in alphabetical order
+
+obj-$(CONFIG_MAX30100) += max30100.o
diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
new file mode 100644
index 000000000000..9d1c81f91dd7
--- /dev/null
+++ b/drivers/iio/health/max30100.c
@@ -0,0 +1,453 @@
+/*
+ * max30100.c - Support for MAX30100 heart rate and pulse oximeter sensor
+ *
+ * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * TODO: allow LED current and pulse length controls via device tree properties
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define MAX30100_REGMAP_NAME "max30100_regmap"
+#define MAX30100_DRV_NAME "max30100"
+
+#define MAX30100_REG_INT_STATUS 0x00
+#define MAX30100_REG_INT_STATUS_PWR_RDY BIT(0)
+#define MAX30100_REG_INT_STATUS_SPO2_RDY BIT(4)
+#define MAX30100_REG_INT_STATUS_HR_RDY BIT(5)
+#define MAX30100_REG_INT_STATUS_FIFO_RDY BIT(7)
+
+#define MAX30100_REG_INT_ENABLE 0x01
+#define MAX30100_REG_INT_ENABLE_SPO2_EN BIT(0)
+#define MAX30100_REG_INT_ENABLE_HR_EN BIT(1)
+#define MAX30100_REG_INT_ENABLE_FIFO_EN BIT(3)
+#define MAX30100_REG_INT_ENABLE_MASK 0xf0
+#define MAX30100_REG_INT_ENABLE_MASK_SHIFT 4
+
+#define MAX30100_REG_FIFO_WR_PTR 0x02
+#define MAX30100_REG_FIFO_OVR_CTR 0x03
+#define MAX30100_REG_FIFO_RD_PTR 0x04
+#define MAX30100_REG_FIFO_DATA 0x05
+#define MAX30100_REG_FIFO_DATA_ENTRY_COUNT 16
+#define MAX30100_REG_FIFO_DATA_ENTRY_LEN 4
+
+#define MAX30100_REG_MODE_CONFIG 0x06
+#define MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN BIT(0)
+#define MAX30100_REG_MODE_CONFIG_MODE_HR_EN BIT(1)
+#define MAX30100_REG_MODE_CONFIG_MODE_MASK 0x03
+#define MAX30100_REG_MODE_CONFIG_TEMP_EN BIT(3)
+#define MAX30100_REG_MODE_CONFIG_PWR BIT(7)
+
+#define MAX30100_REG_SPO2_CONFIG 0x07
+#define MAX30100_REG_SPO2_CONFIG_100HZ BIT(2)
+#define MAX30100_REG_SPO2_CONFIG_HI_RES_EN BIT(6)
+#define MAX30100_REG_SPO2_CONFIG_1600US 0x3
+
+#define MAX30100_REG_LED_CONFIG 0x09
+#define MAX30100_REG_LED_CONFIG_RED_LED_SHIFT 4
+
+#define MAX30100_REG_LED_CONFIG_24MA 0x07
+#define MAX30100_REG_LED_CONFIG_50MA 0x0f
+
+#define MAX30100_REG_TEMP_INTEGER 0x16
+#define MAX30100_REG_TEMP_FRACTION 0x17
+
+struct max30100_data {
+ struct i2c_client *client;
+ struct iio_dev *indio_dev;
+ struct mutex lock;
+ struct regmap *regmap;
+
+ __be16 buffer[2]; /* 2 16-bit channels */
+};
+
+static bool max30100_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX30100_REG_INT_STATUS:
+ case MAX30100_REG_MODE_CONFIG:
+ case MAX30100_REG_FIFO_WR_PTR:
+ case MAX30100_REG_FIFO_OVR_CTR:
+ case MAX30100_REG_FIFO_RD_PTR:
+ case MAX30100_REG_FIFO_DATA:
+ case MAX30100_REG_TEMP_INTEGER:
+ case MAX30100_REG_TEMP_FRACTION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max30100_regmap_config = {
+ .name = MAX30100_REGMAP_NAME,
+
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MAX30100_REG_TEMP_FRACTION,
+ .cache_type = REGCACHE_FLAT,
+
+ .volatile_reg = max30100_is_volatile_reg,
+};
+
+static const unsigned long max30100_scan_masks[] = {0x3, 0};
+
+static const struct iio_chan_spec max30100_channels[] = {
+ {
+ .type = IIO_INTENSITY,
+ .channel2 = IIO_MOD_LIGHT_IR,
+ .modified = 1,
+
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ {
+ .type = IIO_INTENSITY,
+ .channel2 = IIO_MOD_LIGHT_RED,
+ .modified = 1,
+
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = -1,
+ },
+};
+
+static int max30100_set_powermode(struct max30100_data *data, bool state)
+{
+ return regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
+ MAX30100_REG_MODE_CONFIG_PWR,
+ state ? 0 : MAX30100_REG_MODE_CONFIG_PWR);
+}
+
+static int max30100_clear_fifo(struct max30100_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, MAX30100_REG_FIFO_WR_PTR, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, MAX30100_REG_FIFO_OVR_CTR, 0);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, MAX30100_REG_FIFO_RD_PTR, 0);
+}
+
+static int max30100_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct max30100_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = max30100_set_powermode(data, true);
+ if (ret)
+ return ret;
+
+ return max30100_clear_fifo(data);
+}
+
+static int max30100_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct max30100_data *data = iio_priv(indio_dev);
+
+ return max30100_set_powermode(data, false);
+}
+
+static const struct iio_buffer_setup_ops max30100_buffer_setup_ops = {
+ .postenable = max30100_buffer_postenable,
+ .predisable = max30100_buffer_predisable,
+};
+
+static inline int max30100_fifo_count(struct max30100_data *data)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(data->regmap, MAX30100_REG_INT_STATUS, &val);
+ if (ret)
+ return ret;
+
+ /* FIFO is almost full */
+ if (val & MAX30100_REG_INT_STATUS_FIFO_RDY)
+ return MAX30100_REG_FIFO_DATA_ENTRY_COUNT - 1;
+
+ return 0;
+}
+
+static int max30100_read_measurement(struct max30100_data *data)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ MAX30100_REG_FIFO_DATA,
+ MAX30100_REG_FIFO_DATA_ENTRY_LEN,
+ (u8 *) &data->buffer);
+
+ return (ret == MAX30100_REG_FIFO_DATA_ENTRY_LEN) ? 0 : ret;
+}
+
+static irqreturn_t max30100_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct max30100_data *data = iio_priv(indio_dev);
+ int ret, cnt = 0;
+
+ mutex_lock(&data->lock);
+
+ while (cnt-- || (cnt = max30100_fifo_count(data) > 0)) {
+ ret = max30100_read_measurement(data);
+ if (ret)
+ break;
+
+ iio_push_to_buffers(data->indio_dev, data->buffer);
+ }
+
+ mutex_unlock(&data->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int max30100_chip_init(struct max30100_data *data)
+{
+ int ret;
+
+ /* RED IR LED = 24mA, IR LED = 50mA */
+ ret = regmap_write(data->regmap, MAX30100_REG_LED_CONFIG,
+ (MAX30100_REG_LED_CONFIG_24MA <<
+ MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
+ MAX30100_REG_LED_CONFIG_50MA);
+ if (ret)
+ return ret;
+
+ /* enable hi-res SPO2 readings at 100Hz */
+ ret = regmap_write(data->regmap, MAX30100_REG_SPO2_CONFIG,
+ MAX30100_REG_SPO2_CONFIG_HI_RES_EN |
+ MAX30100_REG_SPO2_CONFIG_100HZ);
+ if (ret)
+ return ret;
+
+ /* enable SPO2 mode */
+ ret = regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
+ MAX30100_REG_MODE_CONFIG_MODE_MASK,
+ MAX30100_REG_MODE_CONFIG_MODE_HR_EN |
+ MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN);
+ if (ret)
+ return ret;
+
+ /* enable FIFO interrupt */
+ return regmap_update_bits(data->regmap, MAX30100_REG_INT_ENABLE,
+ MAX30100_REG_INT_ENABLE_MASK,
+ MAX30100_REG_INT_ENABLE_FIFO_EN
+ << MAX30100_REG_INT_ENABLE_MASK_SHIFT);
+}
+
+static int max30100_read_temp(struct max30100_data *data, int *val)
+{
+ int ret;
+ unsigned int reg;
+
+ ret = regmap_read(data->regmap, MAX30100_REG_TEMP_INTEGER, &reg);
+ if (ret < 0)
+ return ret;
+ *val = reg << 4;
+
+ ret = regmap_read(data->regmap, MAX30100_REG_TEMP_FRACTION, &reg);
+ if (ret < 0)
+ return ret;
+
+ *val |= reg & 0xf;
+ *val = sign_extend32(*val, 11);
+
+ return 0;
+}
+
+static int max30100_get_temp(struct max30100_data *data, int *val)
+{
+ int ret;
+
+ /* start acquisition */
+ ret = regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
+ MAX30100_REG_MODE_CONFIG_TEMP_EN,
+ MAX30100_REG_MODE_CONFIG_TEMP_EN);
+ if (ret)
+ return ret;
+
+ usleep_range(35000, 50000);
+
+ return max30100_read_temp(data, val);
+}
+
+static int max30100_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max30100_data *data = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * Temperature reading can only be acquired while engine
+ * is running
+ */
+ mutex_lock(&indio_dev->mlock);
+
+ if (!iio_buffer_enabled(indio_dev))
+ ret = -EAGAIN;
+ else {
+ ret = max30100_get_temp(data, val);
+ if (!ret)
+ ret = IIO_VAL_INT;
+
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1; /* 0.0625 */
+ *val2 = 16;
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct iio_info max30100_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = max30100_read_raw,
+};
+
+static int max30100_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max30100_data *data;
+ struct iio_buffer *buffer;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ buffer = devm_iio_kfifo_allocate(&client->dev);
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ indio_dev->name = MAX30100_DRV_NAME;
+ indio_dev->channels = max30100_channels;
+ indio_dev->info = &max30100_info;
+ indio_dev->num_channels = ARRAY_SIZE(max30100_channels);
+ indio_dev->available_scan_masks = max30100_scan_masks;
+ indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
+ indio_dev->setup_ops = &max30100_buffer_setup_ops;
+
+ data = iio_priv(indio_dev);
+ data->indio_dev = indio_dev;
+ data->client = client;
+
+ mutex_init(&data->lock);
+ i2c_set_clientdata(client, indio_dev);
+
+ data->regmap = devm_regmap_init_i2c(client, &max30100_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&client->dev, "regmap initialization failed.\n");
+ return PTR_ERR(data->regmap);
+ }
+ max30100_set_powermode(data, false);
+
+ ret = max30100_chip_init(data);
+ if (ret)
+ return ret;
+
+ if (client->irq <= 0) {
+ dev_err(&client->dev, "no valid irq defined\n");
+ return -EINVAL;
+ }
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, max30100_interrupt_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max30100_irq", indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
+ return ret;
+ }
+
+ return iio_device_register(indio_dev);
+}
+
+static int max30100_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct max30100_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ max30100_set_powermode(data, false);
+
+ return 0;
+}
+
+static const struct i2c_device_id max30100_id[] = {
+ { "max30100", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, max30100_id);
+
+static const struct of_device_id max30100_dt_ids[] = {
+ { .compatible = "maxim,max30100" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max30100_dt_ids);
+
+static struct i2c_driver max30100_driver = {
+ .driver = {
+ .name = MAX30100_DRV_NAME,
+ .of_match_table = of_match_ptr(max30100_dt_ids),
+ },
+ .probe = max30100_probe,
+ .remove = max30100_remove,
+ .id_table = max30100_id,
+};
+module_i2c_driver(max30100_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("MAX30100 heart rate and pulse oximeter sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/industrialio-configfs.c b/drivers/iio/industrialio-configfs.c
new file mode 100644
index 000000000000..45ce2bc47180
--- /dev/null
+++ b/drivers/iio/industrialio-configfs.c
@@ -0,0 +1,51 @@
+/*
+ * Industrial I/O configfs bits
+ *
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * 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/configfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/configfs.h>
+
+static struct config_item_type iio_root_group_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+struct configfs_subsystem iio_configfs_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "iio",
+ .ci_type = &iio_root_group_type,
+ },
+ },
+ .su_mutex = __MUTEX_INITIALIZER(iio_configfs_subsys.su_mutex),
+};
+EXPORT_SYMBOL(iio_configfs_subsys);
+
+static int __init iio_configfs_init(void)
+{
+ config_group_init(&iio_configfs_subsys.su_group);
+
+ return configfs_register_subsystem(&iio_configfs_subsys);
+}
+module_init(iio_configfs_init);
+
+static void __exit iio_configfs_exit(void)
+{
+ configfs_unregister_subsystem(&iio_configfs_subsys);
+}
+module_exit(iio_configfs_exit);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("Industrial I/O configfs support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/industrialio-sw-trigger.c b/drivers/iio/industrialio-sw-trigger.c
new file mode 100644
index 000000000000..311f9fe5aa34
--- /dev/null
+++ b/drivers/iio/industrialio-sw-trigger.c
@@ -0,0 +1,184 @@
+/*
+ * The Industrial I/O core, software trigger functions
+ *
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include <linux/iio/sw_trigger.h>
+#include <linux/iio/configfs.h>
+#include <linux/configfs.h>
+
+static struct config_group *iio_triggers_group;
+static struct config_item_type iio_trigger_type_group_type;
+
+static struct config_item_type iio_triggers_group_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static LIST_HEAD(iio_trigger_types_list);
+static DEFINE_MUTEX(iio_trigger_types_lock);
+
+static
+struct iio_sw_trigger_type *__iio_find_sw_trigger_type(const char *name,
+ unsigned len)
+{
+ struct iio_sw_trigger_type *t = NULL, *iter;
+
+ list_for_each_entry(iter, &iio_trigger_types_list, list)
+ if (!strcmp(iter->name, name)) {
+ t = iter;
+ break;
+ }
+
+ return t;
+}
+
+int iio_register_sw_trigger_type(struct iio_sw_trigger_type *t)
+{
+ struct iio_sw_trigger_type *iter;
+ int ret = 0;
+
+ mutex_lock(&iio_trigger_types_lock);
+ iter = __iio_find_sw_trigger_type(t->name, strlen(t->name));
+ if (iter)
+ ret = -EBUSY;
+ else
+ list_add_tail(&t->list, &iio_trigger_types_list);
+ mutex_unlock(&iio_trigger_types_lock);
+
+ if (ret)
+ return ret;
+
+ t->group = configfs_register_default_group(iio_triggers_group, t->name,
+ &iio_trigger_type_group_type);
+ if (IS_ERR(t->group))
+ ret = PTR_ERR(t->group);
+
+ return ret;
+}
+EXPORT_SYMBOL(iio_register_sw_trigger_type);
+
+void iio_unregister_sw_trigger_type(struct iio_sw_trigger_type *t)
+{
+ struct iio_sw_trigger_type *iter;
+
+ mutex_lock(&iio_trigger_types_lock);
+ iter = __iio_find_sw_trigger_type(t->name, strlen(t->name));
+ if (iter)
+ list_del(&t->list);
+ mutex_unlock(&iio_trigger_types_lock);
+
+ configfs_unregister_default_group(t->group);
+}
+EXPORT_SYMBOL(iio_unregister_sw_trigger_type);
+
+static
+struct iio_sw_trigger_type *iio_get_sw_trigger_type(const char *name)
+{
+ struct iio_sw_trigger_type *t;
+
+ mutex_lock(&iio_trigger_types_lock);
+ t = __iio_find_sw_trigger_type(name, strlen(name));
+ if (t && !try_module_get(t->owner))
+ t = NULL;
+ mutex_unlock(&iio_trigger_types_lock);
+
+ return t;
+}
+
+struct iio_sw_trigger *iio_sw_trigger_create(const char *type, const char *name)
+{
+ struct iio_sw_trigger *t;
+ struct iio_sw_trigger_type *tt;
+
+ tt = iio_get_sw_trigger_type(type);
+ if (!tt) {
+ pr_err("Invalid trigger type: %s\n", type);
+ return ERR_PTR(-EINVAL);
+ }
+ t = tt->ops->probe(name);
+ if (IS_ERR(t))
+ goto out_module_put;
+
+ t->trigger_type = tt;
+
+ return t;
+out_module_put:
+ module_put(tt->owner);
+ return t;
+}
+EXPORT_SYMBOL(iio_sw_trigger_create);
+
+void iio_sw_trigger_destroy(struct iio_sw_trigger *t)
+{
+ struct iio_sw_trigger_type *tt = t->trigger_type;
+
+ tt->ops->remove(t);
+ module_put(tt->owner);
+}
+EXPORT_SYMBOL(iio_sw_trigger_destroy);
+
+static struct config_group *trigger_make_group(struct config_group *group,
+ const char *name)
+{
+ struct iio_sw_trigger *t;
+
+ t = iio_sw_trigger_create(group->cg_item.ci_name, name);
+ if (IS_ERR(t))
+ return ERR_CAST(t);
+
+ config_item_set_name(&t->group.cg_item, "%s", name);
+
+ return &t->group;
+}
+
+static void trigger_drop_group(struct config_group *group,
+ struct config_item *item)
+{
+ struct iio_sw_trigger *t = to_iio_sw_trigger(item);
+
+ iio_sw_trigger_destroy(t);
+ config_item_put(item);
+}
+
+static struct configfs_group_operations trigger_ops = {
+ .make_group = &trigger_make_group,
+ .drop_item = &trigger_drop_group,
+};
+
+static struct config_item_type iio_trigger_type_group_type = {
+ .ct_group_ops = &trigger_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static int __init iio_sw_trigger_init(void)
+{
+ iio_triggers_group =
+ configfs_register_default_group(&iio_configfs_subsys.su_group,
+ "triggers",
+ &iio_triggers_group_type);
+ if (IS_ERR(iio_triggers_group))
+ return PTR_ERR(iio_triggers_group);
+ return 0;
+}
+module_init(iio_sw_trigger_init);
+
+static void __exit iio_sw_trigger_exit(void)
+{
+ configfs_unregister_default_group(iio_triggers_group);
+}
+module_exit(iio_sw_trigger_exit);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("Industrial I/O software triggers support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index c8bad3cf891d..80fbbfd76faf 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -61,12 +61,10 @@ EXPORT_SYMBOL_GPL(iio_map_array_register);
int iio_map_array_unregister(struct iio_dev *indio_dev)
{
int ret = -ENODEV;
- struct iio_map_internal *mapi;
- struct list_head *pos, *tmp;
+ struct iio_map_internal *mapi, *next;
mutex_lock(&iio_map_list_lock);
- list_for_each_safe(pos, tmp, &iio_map_list) {
- mapi = list_entry(pos, struct iio_map_internal, l);
+ list_for_each_entry_safe(mapi, next, &iio_map_list, l) {
if (indio_dev == mapi->indio_dev) {
list_del(&mapi->l);
kfree(mapi);
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c
index 49dab3cb3e23..256c4bc12d21 100644
--- a/drivers/iio/light/us5182d.c
+++ b/drivers/iio/light/us5182d.c
@@ -23,6 +23,8 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#define US5182D_REG_CFG0 0x00
#define US5182D_CFG0_ONESHOT_EN BIT(6)
@@ -81,6 +83,7 @@
#define US5182D_READ_BYTE 1
#define US5182D_READ_WORD 2
#define US5182D_OPSTORE_SLEEP_TIME 20 /* ms */
+#define US5182D_SLEEP_MS 3000 /* ms */
/* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */
static const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600,
@@ -99,6 +102,11 @@ enum mode {
US5182D_PX_ONLY
};
+enum pmode {
+ US5182D_CONTINUOUS,
+ US5182D_ONESHOT
+};
+
struct us5182d_data {
struct i2c_client *client;
struct mutex lock;
@@ -112,6 +120,12 @@ struct us5182d_data {
u16 *us5182d_dark_ths;
u8 opmode;
+ u8 power_mode;
+
+ bool als_enabled;
+ bool px_enabled;
+
+ bool default_continuous;
};
static IIO_CONST_ATTR(in_illuminance_scale_available,
@@ -130,13 +144,11 @@ static const struct {
u8 reg;
u8 val;
} us5182d_regvals[] = {
- {US5182D_REG_CFG0, (US5182D_CFG0_SHUTDOWN_EN |
- US5182D_CFG0_WORD_ENABLE)},
+ {US5182D_REG_CFG0, US5182D_CFG0_WORD_ENABLE},
{US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16},
{US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 |
US5182D_CFG2_PXGAIN_DEFAULT)},
{US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100},
- {US5182D_REG_MODE_STORE, US5182D_STORE_MODE},
{US5182D_REG_CFG4, 0x00},
};
@@ -169,7 +181,7 @@ static int us5182d_get_als(struct us5182d_data *data)
return result;
}
-static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
+static int us5182d_oneshot_en(struct us5182d_data *data)
{
int ret;
@@ -183,6 +195,20 @@ static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
*/
ret = ret | US5182D_CFG0_ONESHOT_EN;
+ return i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret);
+}
+
+static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
+{
+ int ret;
+
+ if (mode == data->opmode)
+ return 0;
+
+ ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
+ if (ret < 0)
+ return ret;
+
/* update mode */
ret = ret & ~US5182D_OPMODE_MASK;
ret = ret | (mode << US5182D_OPMODE_SHIFT);
@@ -196,9 +222,6 @@ static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
if (ret < 0)
return ret;
- if (mode == data->opmode)
- return 0;
-
ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_MODE_STORE,
US5182D_STORE_MODE);
if (ret < 0)
@@ -210,6 +233,96 @@ static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
return 0;
}
+static int us5182d_als_enable(struct us5182d_data *data)
+{
+ int ret;
+ u8 mode;
+
+ if (data->power_mode == US5182D_ONESHOT)
+ return us5182d_set_opmode(data, US5182D_ALS_ONLY);
+
+ if (data->als_enabled)
+ return 0;
+
+ mode = data->px_enabled ? US5182D_ALS_PX : US5182D_ALS_ONLY;
+
+ ret = us5182d_set_opmode(data, mode);
+ if (ret < 0)
+ return ret;
+
+ data->als_enabled = true;
+
+ return 0;
+}
+
+static int us5182d_px_enable(struct us5182d_data *data)
+{
+ int ret;
+ u8 mode;
+
+ if (data->power_mode == US5182D_ONESHOT)
+ return us5182d_set_opmode(data, US5182D_PX_ONLY);
+
+ if (data->px_enabled)
+ return 0;
+
+ mode = data->als_enabled ? US5182D_ALS_PX : US5182D_PX_ONLY;
+
+ ret = us5182d_set_opmode(data, mode);
+ if (ret < 0)
+ return ret;
+
+ data->px_enabled = true;
+
+ return 0;
+}
+
+static int us5182d_shutdown_en(struct us5182d_data *data, u8 state)
+{
+ int ret;
+
+ if (data->power_mode == US5182D_ONESHOT)
+ return 0;
+
+ ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
+ if (ret < 0)
+ return ret;
+
+ ret = ret & ~US5182D_CFG0_SHUTDOWN_EN;
+ ret = ret | state;
+
+ ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret);
+ if (ret < 0)
+ return ret;
+
+ if (state & US5182D_CFG0_SHUTDOWN_EN) {
+ data->als_enabled = false;
+ data->px_enabled = false;
+ }
+
+ return ret;
+}
+
+
+static int us5182d_set_power_state(struct us5182d_data *data, bool on)
+{
+ int ret;
+
+ if (data->power_mode == US5182D_ONESHOT)
+ return 0;
+
+ if (on) {
+ ret = pm_runtime_get_sync(&data->client->dev);
+ if (ret < 0)
+ pm_runtime_put_noidle(&data->client->dev);
+ } else {
+ pm_runtime_mark_last_busy(&data->client->dev);
+ ret = pm_runtime_put_autosuspend(&data->client->dev);
+ }
+
+ return ret;
+}
+
static int us5182d_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@@ -222,29 +335,49 @@ static int us5182d_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_LIGHT:
mutex_lock(&data->lock);
- ret = us5182d_set_opmode(data, US5182D_OPMODE_ALS);
+ if (data->power_mode == US5182D_ONESHOT) {
+ ret = us5182d_oneshot_en(data);
+ if (ret < 0)
+ goto out_err;
+ }
+ ret = us5182d_set_power_state(data, true);
if (ret < 0)
goto out_err;
-
+ ret = us5182d_als_enable(data);
+ if (ret < 0)
+ goto out_poweroff;
ret = us5182d_get_als(data);
if (ret < 0)
+ goto out_poweroff;
+ *val = ret;
+ ret = us5182d_set_power_state(data, false);
+ if (ret < 0)
goto out_err;
mutex_unlock(&data->lock);
- *val = ret;
return IIO_VAL_INT;
case IIO_PROXIMITY:
mutex_lock(&data->lock);
- ret = us5182d_set_opmode(data, US5182D_OPMODE_PX);
+ if (data->power_mode == US5182D_ONESHOT) {
+ ret = us5182d_oneshot_en(data);
+ if (ret < 0)
+ goto out_err;
+ }
+ ret = us5182d_set_power_state(data, true);
if (ret < 0)
goto out_err;
-
+ ret = us5182d_px_enable(data);
+ if (ret < 0)
+ goto out_poweroff;
ret = i2c_smbus_read_word_data(data->client,
US5182D_REG_PDL);
if (ret < 0)
+ goto out_poweroff;
+ *val = ret;
+ ret = us5182d_set_power_state(data, false);
+ if (ret < 0)
goto out_err;
mutex_unlock(&data->lock);
- *val = ret;
- return IIO_VAL_INT;
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
@@ -263,6 +396,9 @@ static int us5182d_read_raw(struct iio_dev *indio_dev,
}
return -EINVAL;
+
+out_poweroff:
+ us5182d_set_power_state(data, false);
out_err:
mutex_unlock(&data->lock);
return ret;
@@ -368,6 +504,7 @@ static int us5182d_init(struct iio_dev *indio_dev)
return ret;
data->opmode = 0;
+ data->power_mode = US5182D_CONTINUOUS;
for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) {
ret = i2c_smbus_write_byte_data(data->client,
us5182d_regvals[i].reg,
@@ -376,7 +513,17 @@ static int us5182d_init(struct iio_dev *indio_dev)
return ret;
}
- return 0;
+ data->als_enabled = true;
+ data->px_enabled = true;
+
+ if (!data->default_continuous) {
+ ret = us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
+ if (ret < 0)
+ return ret;
+ data->power_mode = US5182D_ONESHOT;
+ }
+
+ return ret;
}
static void us5182d_get_platform_data(struct iio_dev *indio_dev)
@@ -399,6 +546,8 @@ static void us5182d_get_platform_data(struct iio_dev *indio_dev)
"upisemi,lower-dark-gain",
&data->lower_dark_gain))
data->lower_dark_gain = US5182D_REG_AUTO_LDARK_GAIN_DEFAULT;
+ data->default_continuous = device_property_read_bool(&data->client->dev,
+ "upisemi,continuous");
}
static int us5182d_dark_gain_config(struct iio_dev *indio_dev)
@@ -464,18 +613,73 @@ static int us5182d_probe(struct i2c_client *client,
ret = us5182d_dark_gain_config(indio_dev);
if (ret < 0)
- return ret;
+ goto out_err;
+
+ if (data->default_continuous) {
+ pm_runtime_set_active(&client->dev);
+ if (ret < 0)
+ goto out_err;
+ }
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev,
+ US5182D_SLEEP_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
+ return ret;
- return iio_device_register(indio_dev);
}
static int us5182d_remove(struct i2c_client *client)
{
+ struct us5182d_data *data = iio_priv(i2c_get_clientdata(client));
+
iio_device_unregister(i2c_get_clientdata(client));
- return i2c_smbus_write_byte_data(client, US5182D_REG_CFG0,
- US5182D_CFG0_SHUTDOWN_EN);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ return us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
}
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM)
+static int us5182d_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct us5182d_data *data = iio_priv(indio_dev);
+
+ if (data->power_mode == US5182D_CONTINUOUS)
+ return us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
+
+ return 0;
+}
+
+static int us5182d_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct us5182d_data *data = iio_priv(indio_dev);
+
+ if (data->power_mode == US5182D_CONTINUOUS)
+ return us5182d_shutdown_en(data,
+ ~US5182D_CFG0_SHUTDOWN_EN & 0xff);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops us5182d_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(us5182d_suspend, us5182d_resume)
+ SET_RUNTIME_PM_OPS(us5182d_suspend, us5182d_resume, NULL)
+};
+
static const struct acpi_device_id us5182d_acpi_match[] = {
{ "USD5182", 0},
{}
@@ -493,6 +697,7 @@ MODULE_DEVICE_TABLE(i2c, us5182d_id);
static struct i2c_driver us5182d_driver = {
.driver = {
.name = US5182D_DRV_NAME,
+ .pm = &us5182d_pm_ops,
.acpi_match_table = ACPI_PTR(us5182d_acpi_match),
},
.probe = us5182d_probe,
diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
index 29ac262f591b..93e29fb67fa0 100644
--- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
+++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
@@ -36,8 +36,10 @@
#define LIDAR_REG_STATUS_INVALID BIT(3)
#define LIDAR_REG_STATUS_READY BIT(0)
-#define LIDAR_REG_DATA_HBYTE 0x0f
-#define LIDAR_REG_DATA_LBYTE 0x10
+#define LIDAR_REG_DATA_HBYTE 0x0f
+#define LIDAR_REG_DATA_LBYTE 0x10
+#define LIDAR_REG_DATA_WORD_READ BIT(7)
+
#define LIDAR_REG_PWR_CONTROL 0x65
#define LIDAR_DRV_NAME "lidar"
@@ -46,6 +48,9 @@ struct lidar_data {
struct iio_dev *indio_dev;
struct i2c_client *client;
+ int (*xfer)(struct lidar_data *data, u8 reg, u8 *val, int len);
+ int i2c_enabled;
+
u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */
};
@@ -64,7 +69,28 @@ static const struct iio_chan_spec lidar_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(1),
};
-static int lidar_read_byte(struct lidar_data *data, int reg)
+static int lidar_i2c_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
+{
+ struct i2c_client *client = data->client;
+ struct i2c_msg msg[2];
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags | I2C_M_STOP;
+ msg[0].len = 1;
+ msg[0].buf = (char *) &reg;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = client->flags | I2C_M_RD;
+ msg[1].len = len;
+ msg[1].buf = (char *) val;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+
+ return (ret == 2) ? 0 : ret;
+}
+
+static int lidar_smbus_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
{
struct i2c_client *client = data->client;
int ret;
@@ -74,17 +100,35 @@ static int lidar_read_byte(struct lidar_data *data, int reg)
* so in turn i2c_smbus_read_byte_data cannot be used
*/
- ret = i2c_smbus_write_byte(client, reg);
- if (ret < 0) {
- dev_err(&client->dev, "cannot write addr value");
- return ret;
+ while (len--) {
+ ret = i2c_smbus_write_byte(client, reg++);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot write addr value");
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot read data value");
+ return ret;
+ }
+
+ *(val++) = ret;
}
- ret = i2c_smbus_read_byte(client);
+ return 0;
+}
+
+static int lidar_read_byte(struct lidar_data *data, u8 reg)
+{
+ int ret;
+ u8 val;
+
+ ret = data->xfer(data, reg, &val, 1);
if (ret < 0)
- dev_err(&client->dev, "cannot read data value");
+ return ret;
- return ret;
+ return val;
}
static inline int lidar_write_control(struct lidar_data *data, int val)
@@ -100,22 +144,14 @@ static inline int lidar_write_power(struct lidar_data *data, int val)
static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
{
- int ret;
- int val;
-
- ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE);
- if (ret < 0)
- return ret;
- val = ret << 8;
+ int ret = data->xfer(data, LIDAR_REG_DATA_HBYTE |
+ (data->i2c_enabled ? LIDAR_REG_DATA_WORD_READ : 0),
+ (u8 *) reg, 2);
- ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE);
- if (ret < 0)
- return ret;
+ if (!ret)
+ *reg = be16_to_cpu(*reg);
- val |= ret;
- *reg = val;
-
- return 0;
+ return ret;
}
static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
@@ -233,6 +269,16 @@ static int lidar_probe(struct i2c_client *client,
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
+ data = iio_priv(indio_dev);
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ data->xfer = lidar_i2c_xfer;
+ data->i2c_enabled = 1;
+ } else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
+ data->xfer = lidar_smbus_xfer;
+ else
+ return -ENOTSUPP;
indio_dev->info = &lidar_info;
indio_dev->name = LIDAR_DRV_NAME;
@@ -240,7 +286,6 @@ static int lidar_probe(struct i2c_client *client,
indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
- data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
index 79996123a71b..519e6772f6f5 100644
--- a/drivers/iio/trigger/Kconfig
+++ b/drivers/iio/trigger/Kconfig
@@ -5,6 +5,16 @@
menu "Triggers - standalone"
+config IIO_HRTIMER_TRIGGER
+ tristate "High resolution timer trigger"
+ depends on IIO_SW_TRIGGER
+ help
+ Provides a frequency based IIO trigger using high resolution
+ timers as interrupt source.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iio-trig-hrtimer.
+
config IIO_INTERRUPT_TRIGGER
tristate "Generic interrupt trigger"
help
diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
index 0694daecaf22..fe06eb564367 100644
--- a/drivers/iio/trigger/Makefile
+++ b/drivers/iio/trigger/Makefile
@@ -3,5 +3,7 @@
#
# When adding new entries keep the list in alphabetical order
+
+obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c
new file mode 100644
index 000000000000..5e6d451febeb
--- /dev/null
+++ b/drivers/iio/trigger/iio-trig-hrtimer.c
@@ -0,0 +1,193 @@
+/**
+ * The industrial I/O periodic hrtimer trigger driver
+ *
+ * Copyright (C) Intuitive Aerial AB
+ * Written by Marten Svanfeldt, marten@intuitiveaerial.com
+ * Copyright (C) 2012, Analog Device Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (C) 2015, Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/sw_trigger.h>
+
+/* default sampling frequency - 100Hz */
+#define HRTIMER_DEFAULT_SAMPLING_FREQUENCY 100
+
+struct iio_hrtimer_info {
+ struct iio_sw_trigger swt;
+ struct hrtimer timer;
+ unsigned long sampling_frequency;
+ ktime_t period;
+};
+
+static struct config_item_type iio_hrtimer_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static
+ssize_t iio_hrtimer_show_sampling_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig);
+
+ return snprintf(buf, PAGE_SIZE, "%lu\n", info->sampling_frequency);
+}
+
+static
+ssize_t iio_hrtimer_store_sampling_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (!val || val > NSEC_PER_SEC)
+ return -EINVAL;
+
+ info->sampling_frequency = val;
+ info->period = ktime_set(0, NSEC_PER_SEC / val);
+
+ return len;
+}
+
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
+ iio_hrtimer_show_sampling_frequency,
+ iio_hrtimer_store_sampling_frequency);
+
+static struct attribute *iio_hrtimer_attrs[] = {
+ &dev_attr_sampling_frequency.attr,
+ NULL
+};
+
+static const struct attribute_group iio_hrtimer_attr_group = {
+ .attrs = iio_hrtimer_attrs,
+};
+
+static const struct attribute_group *iio_hrtimer_attr_groups[] = {
+ &iio_hrtimer_attr_group,
+ NULL
+};
+
+static enum hrtimer_restart iio_hrtimer_trig_handler(struct hrtimer *timer)
+{
+ struct iio_hrtimer_info *info;
+
+ info = container_of(timer, struct iio_hrtimer_info, timer);
+
+ hrtimer_forward_now(timer, info->period);
+ iio_trigger_poll(info->swt.trigger);
+
+ return HRTIMER_RESTART;
+}
+
+static int iio_trig_hrtimer_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_hrtimer_info *trig_info;
+
+ trig_info = iio_trigger_get_drvdata(trig);
+
+ if (state)
+ hrtimer_start(&trig_info->timer, trig_info->period,
+ HRTIMER_MODE_REL);
+ else
+ hrtimer_cancel(&trig_info->timer);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops iio_hrtimer_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = iio_trig_hrtimer_set_state,
+};
+
+static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name)
+{
+ struct iio_hrtimer_info *trig_info;
+ int ret;
+
+ trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
+ if (!trig_info)
+ return ERR_PTR(-ENOMEM);
+
+ trig_info->swt.trigger = iio_trigger_alloc("%s", name);
+ if (!trig_info->swt.trigger) {
+ ret = -ENOMEM;
+ goto err_free_trig_info;
+ }
+
+ iio_trigger_set_drvdata(trig_info->swt.trigger, trig_info);
+ trig_info->swt.trigger->ops = &iio_hrtimer_trigger_ops;
+ trig_info->swt.trigger->dev.groups = iio_hrtimer_attr_groups;
+
+ hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ trig_info->timer.function = iio_hrtimer_trig_handler;
+
+ trig_info->sampling_frequency = HRTIMER_DEFAULT_SAMPLING_FREQUENCY;
+ trig_info->period = ktime_set(0, NSEC_PER_SEC /
+ trig_info->sampling_frequency);
+
+ ret = iio_trigger_register(trig_info->swt.trigger);
+ if (ret)
+ goto err_free_trigger;
+
+ iio_swt_group_init_type_name(&trig_info->swt, name, &iio_hrtimer_type);
+ return &trig_info->swt;
+err_free_trigger:
+ iio_trigger_free(trig_info->swt.trigger);
+err_free_trig_info:
+ kfree(trig_info);
+
+ return ERR_PTR(ret);
+}
+
+static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt)
+{
+ struct iio_hrtimer_info *trig_info;
+
+ trig_info = iio_trigger_get_drvdata(swt->trigger);
+
+ iio_trigger_unregister(swt->trigger);
+
+ /* cancel the timer after unreg to make sure no one rearms it */
+ hrtimer_cancel(&trig_info->timer);
+ iio_trigger_free(swt->trigger);
+ kfree(trig_info);
+
+ return 0;
+}
+
+static const struct iio_sw_trigger_ops iio_trig_hrtimer_ops = {
+ .probe = iio_trig_hrtimer_probe,
+ .remove = iio_trig_hrtimer_remove,
+};
+
+static struct iio_sw_trigger_type iio_trig_hrtimer = {
+ .name = "hrtimer",
+ .owner = THIS_MODULE,
+ .ops = &iio_trig_hrtimer_ops,
+};
+
+module_iio_sw_trigger_driver(iio_trig_hrtimer);
+
+MODULE_AUTHOR("Marten Svanfeldt <marten@intuitiveaerial.com>");
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("Periodic hrtimer trigger for the IIO subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index bb40f3728742..92211039ffa9 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -609,7 +609,7 @@ static const struct iio_chan_spec ad7192_channels[] = {
static int ad7192_probe(struct spi_device *spi)
{
- const struct ad7192_platform_data *pdata = spi->dev.platform_data;
+ const struct ad7192_platform_data *pdata = dev_get_platdata(&spi->dev);
struct ad7192_state *st;
struct iio_dev *indio_dev;
int ret, voltage_uv = 0;
diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c
index 35acb1a4669b..f45ebedb7a05 100644
--- a/drivers/staging/iio/adc/ad7280a.c
+++ b/drivers/staging/iio/adc/ad7280a.c
@@ -833,7 +833,7 @@ static const struct ad7280_platform_data ad7793_default_pdata = {
static int ad7280_probe(struct spi_device *spi)
{
- const struct ad7280_platform_data *pdata = spi->dev.platform_data;
+ const struct ad7280_platform_data *pdata = dev_get_platdata(&spi->dev);
struct ad7280_state *st;
int ret;
const unsigned short tACQ_ns[4] = {465, 1010, 1460, 1890};
diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c
index c8e156646528..22260512cf01 100644
--- a/drivers/staging/iio/adc/ad7816.c
+++ b/drivers/staging/iio/adc/ad7816.c
@@ -345,7 +345,7 @@ static int ad7816_probe(struct spi_device *spi_dev)
{
struct ad7816_chip_info *chip;
struct iio_dev *indio_dev;
- unsigned short *pins = spi_dev->dev.platform_data;
+ unsigned short *pins = dev_get_platdata(&spi_dev->dev);
int ret = 0;
int i;
diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c
index 2b65faa6296a..18b27a1984b2 100644
--- a/drivers/staging/iio/frequency/ad9832.c
+++ b/drivers/staging/iio/frequency/ad9832.c
@@ -201,7 +201,7 @@ static const struct iio_info ad9832_info = {
static int ad9832_probe(struct spi_device *spi)
{
- struct ad9832_platform_data *pdata = spi->dev.platform_data;
+ struct ad9832_platform_data *pdata = dev_get_platdata(&spi->dev);
struct iio_dev *indio_dev;
struct ad9832_state *st;
struct regulator *reg;
diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c
index 6464f2cbe94b..6366216e4f37 100644
--- a/drivers/staging/iio/frequency/ad9834.c
+++ b/drivers/staging/iio/frequency/ad9834.c
@@ -318,7 +318,7 @@ static const struct iio_info ad9833_info = {
static int ad9834_probe(struct spi_device *spi)
{
- struct ad9834_platform_data *pdata = spi->dev.platform_data;
+ struct ad9834_platform_data *pdata = dev_get_platdata(&spi->dev);
struct ad9834_state *st;
struct iio_dev *indio_dev;
struct regulator *reg;
diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c
index 9dfd04855a1b..5b1c1650a0e4 100644
--- a/drivers/staging/iio/light/tsl2x7x_core.c
+++ b/drivers/staging/iio/light/tsl2x7x_core.c
@@ -1898,7 +1898,7 @@ static int tsl2x7x_probe(struct i2c_client *clientp,
mutex_init(&chip->prox_mutex);
chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
- chip->pdata = clientp->dev.platform_data;
+ chip->pdata = dev_get_platdata(&clientp->dev);
chip->id = id->driver_data;
chip->chip_info =
&tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]];
diff --git a/include/linux/iio/configfs.h b/include/linux/iio/configfs.h
new file mode 100644
index 000000000000..93befd67c15c
--- /dev/null
+++ b/include/linux/iio/configfs.h
@@ -0,0 +1,15 @@
+/*
+ * Industrial I/O configfs support
+ *
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef __IIO_CONFIGFS
+#define __IIO_CONFIGFS
+
+extern struct configfs_subsystem iio_configfs_subsys;
+
+#endif /* __IIO_CONFIGFS */
diff --git a/include/linux/iio/sw_trigger.h b/include/linux/iio/sw_trigger.h
new file mode 100644
index 000000000000..5198f8ed08a4
--- /dev/null
+++ b/include/linux/iio/sw_trigger.h
@@ -0,0 +1,70 @@
+/*
+ * Industrial I/O software trigger interface
+ *
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __IIO_SW_TRIGGER
+#define __IIO_SW_TRIGGER
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/configfs.h>
+
+#define module_iio_sw_trigger_driver(__iio_sw_trigger_type) \
+ module_driver(__iio_sw_trigger_type, iio_register_sw_trigger_type, \
+ iio_unregister_sw_trigger_type)
+
+struct iio_sw_trigger_ops;
+
+struct iio_sw_trigger_type {
+ const char *name;
+ struct module *owner;
+ const struct iio_sw_trigger_ops *ops;
+ struct list_head list;
+ struct config_group *group;
+};
+
+struct iio_sw_trigger {
+ struct iio_trigger *trigger;
+ struct iio_sw_trigger_type *trigger_type;
+ struct config_group group;
+};
+
+struct iio_sw_trigger_ops {
+ struct iio_sw_trigger* (*probe)(const char *);
+ int (*remove)(struct iio_sw_trigger *);
+};
+
+static inline
+struct iio_sw_trigger *to_iio_sw_trigger(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct iio_sw_trigger,
+ group);
+}
+
+int iio_register_sw_trigger_type(struct iio_sw_trigger_type *tt);
+void iio_unregister_sw_trigger_type(struct iio_sw_trigger_type *tt);
+
+struct iio_sw_trigger *iio_sw_trigger_create(const char *, const char *);
+void iio_sw_trigger_destroy(struct iio_sw_trigger *);
+
+int iio_sw_trigger_type_configfs_register(struct iio_sw_trigger_type *tt);
+void iio_sw_trigger_type_configfs_unregister(struct iio_sw_trigger_type *tt);
+
+static inline
+void iio_swt_group_init_type_name(struct iio_sw_trigger *t,
+ const char *name,
+ struct config_item_type *type)
+{
+#ifdef CONFIG_CONFIGFS_FS
+ config_group_init_type_name(&t->group, name, type);
+#endif
+}
+
+#endif /* __IIO_SW_TRIGGER */