aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/Kconfig4
-rw-r--r--drivers/iio/Makefile2
-rw-r--r--drivers/iio/accel/Kconfig1
-rw-r--r--drivers/iio/accel/st_accel_core.c39
-rw-r--r--drivers/iio/adc/Kconfig10
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/at91_adc.c2
-rw-r--r--drivers/iio/adc/exynos_adc.c12
-rw-r--r--drivers/iio/adc/max1363.c2
-rw-r--r--drivers/iio/adc/mcp320x.c257
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c132
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_buffer.c61
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c34
-rw-r--r--drivers/iio/dac/Kconfig10
-rw-r--r--drivers/iio/dac/Makefile1
-rw-r--r--drivers/iio/dac/ad7303.c315
-rw-r--r--drivers/iio/frequency/adf4350.c186
-rw-r--r--drivers/iio/gyro/Kconfig8
-rw-r--r--drivers/iio/gyro/Makefile1
-rw-r--r--drivers/iio/gyro/adis16130.c207
-rw-r--r--drivers/iio/gyro/st_gyro_core.c24
-rw-r--r--drivers/iio/industrialio-buffer.c10
-rw-r--r--drivers/iio/light/hid-sensor-als.c2
-rw-r--r--drivers/iio/magnetometer/Kconfig1
-rw-r--r--drivers/iio/magnetometer/ak8975.c103
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c39
-rw-r--r--drivers/iio/pressure/Kconfig35
-rw-r--r--drivers/iio/pressure/Makefile10
-rw-r--r--drivers/iio/pressure/st_pressure.h39
-rw-r--r--drivers/iio/pressure/st_pressure_buffer.c105
-rw-r--r--drivers/iio/pressure/st_pressure_core.c272
-rw-r--r--drivers/iio/pressure/st_pressure_i2c.c77
-rw-r--r--drivers/iio/pressure/st_pressure_spi.c76
-rw-r--r--drivers/iio/trigger/Kconfig26
-rw-r--r--drivers/iio/trigger/Makefile6
-rw-r--r--drivers/iio/trigger/iio-trig-interrupt.c121
-rw-r--r--drivers/iio/trigger/iio-trig-sysfs.c227
37 files changed, 2330 insertions, 128 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index b2f963be3993..9af763a90d93 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -70,5 +70,9 @@ source "drivers/iio/gyro/Kconfig"
source "drivers/iio/imu/Kconfig"
source "drivers/iio/light/Kconfig"
source "drivers/iio/magnetometer/Kconfig"
+if IIO_TRIGGER
+ source "drivers/iio/trigger/Kconfig"
+endif #IIO_TRIGGER
+source "drivers/iio/pressure/Kconfig"
endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index a0e8cdd67e4d..7a3866c2d2a1 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -21,3 +21,5 @@ obj-y += frequency/
obj-y += imu/
obj-y += light/
obj-y += magnetometer/
+obj-y += trigger/
+obj-y += pressure/
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index bb594963f91e..719d83fe51dd 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -28,7 +28,6 @@ config IIO_ST_ACCEL_3AXIS
select IIO_ST_ACCEL_I2C_3AXIS if (I2C)
select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER)
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
- select IIO_ST_ACCEL_BUFFER if (IIO_TRIGGERED_BUFFER)
help
Say yes here to build support for STMicroelectronics accelerometers:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index e0f5a3ceba5e..4aec121261d7 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -26,6 +26,8 @@
#include <linux/iio/common/st_sensors.h>
#include "st_accel.h"
+#define ST_ACCEL_NUMBER_DATA_CHANNELS 3
+
/* DEFAULT VALUE FOR SENSORS */
#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28
#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a
@@ -125,22 +127,34 @@
#define ST_ACCEL_3_MULTIREAD_BIT false
static const struct iio_chan_spec st_accel_12bit_channels[] = {
- ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
- ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
- ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
- ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 12, 16,
+ ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 12, 16,
+ ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 12, 16,
+ ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct iio_chan_spec st_accel_16bit_channels[] = {
- ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
- ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
- ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
- ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
+ ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
+ ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
+ ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
@@ -442,6 +456,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
if (err < 0)
goto st_accel_common_probe_error;
+ adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
adata->multiread_bit = adata->sensor->multi_read_bit;
indio_dev->channels = adata->sensor->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index ab0767e6727e..93129ec4b649 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -133,6 +133,16 @@ config MAX1363
max11646, max11647) Provides direct access via sysfs and buffered
data via the iio dev interface.
+config MCP320X
+ tristate "Microchip Technology MCP3204/08"
+ depends on SPI
+ help
+ Say yes here to build support for Microchip Technology's MCP3204 or
+ MCP3208 analog to digital converter.
+
+ This driver can also be built as a module. If so, the module will be
+ called mcp320x.
+
config TI_ADC081C
tristate "Texas Instruments ADC081C021/027"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 0a825bed43f6..8f475d31fe4d 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1363) += max1363.o
+obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index e5b88d5d3b59..b6db6a0e09cd 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -774,11 +774,13 @@ static int at91_adc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
static const struct of_device_id at91_adc_dt_ids[] = {
{ .compatible = "atmel,at91sam9260-adc" },
{},
};
MODULE_DEVICE_TABLE(of, at91_adc_dt_ids);
+#endif
static struct platform_driver at91_adc_driver = {
.probe = at91_adc_probe,
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index b3d03d335948..9809fc9a35d2 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -270,16 +270,16 @@ static int exynos_adc_probe(struct platform_device *pdev)
info = iio_priv(indio_dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- info->regs = devm_request_and_ioremap(&pdev->dev, mem);
- if (!info->regs) {
- ret = -ENOMEM;
+ info->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(info->regs)) {
+ ret = PTR_ERR(info->regs);
goto err_iio;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- info->enable_reg = devm_request_and_ioremap(&pdev->dev, mem);
- if (!info->enable_reg) {
- ret = -ENOMEM;
+ info->enable_reg = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(info->enable_reg)) {
+ ret = PTR_ERR(info->enable_reg);
goto err_iio;
}
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
index 9e6da72ad823..f148d00b83f7 100644
--- a/drivers/iio/adc/max1363.c
+++ b/drivers/iio/adc/max1363.c
@@ -660,7 +660,7 @@ static ssize_t max1363_monitor_store_freq(struct device *dev,
unsigned long val;
bool found = false;
- ret = strict_strtoul(buf, 10, &val);
+ ret = kstrtoul(buf, 10, &val);
if (ret)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++)
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
new file mode 100644
index 000000000000..ebc015922a79
--- /dev/null
+++ b/drivers/iio/adc/mcp320x.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
+ *
+ * Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips.
+ * Datasheet can be found here:
+ * http://ww1.microchip.com/downloads/en/devicedoc/21298c.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
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/regulator/consumer.h>
+
+#define MCP_SINGLE_ENDED (1 << 3)
+#define MCP_START_BIT (1 << 4)
+
+enum {
+ mcp3204,
+ mcp3208,
+};
+
+struct mcp320x {
+ struct spi_device *spi;
+ struct spi_message msg;
+ struct spi_transfer transfer[2];
+
+ u8 tx_buf;
+ u8 rx_buf[2];
+
+ struct regulator *reg;
+ struct mutex lock;
+};
+
+static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg)
+{
+ int ret;
+
+ adc->tx_buf = msg;
+ ret = spi_sync(adc->spi, &adc->msg);
+ if (ret < 0)
+ return ret;
+
+ return ((adc->rx_buf[0] & 0x3f) << 6) |
+ (adc->rx_buf[1] >> 2);
+}
+
+static int mcp320x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct mcp320x *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&adc->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (channel->differential)
+ ret = mcp320x_adc_conversion(adc,
+ MCP_START_BIT | channel->address);
+ else
+ ret = mcp320x_adc_conversion(adc,
+ MCP_START_BIT | MCP_SINGLE_ENDED |
+ channel->address);
+ if (ret < 0)
+ goto out;
+
+ *val = ret;
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ /* Digital output code = (4096 * Vin) / Vref */
+ ret = regulator_get_voltage(adc->reg);
+ if (ret < 0)
+ goto out;
+
+ *val = ret / 1000;
+ *val2 = 12;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+
+ default:
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+#define MCP320X_VOLTAGE_CHANNEL(num) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (num), \
+ .address = (num), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ }
+
+#define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (num * 2), \
+ .channel2 = (num * 2 + 1), \
+ .address = (num * 2), \
+ .differential = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ }
+
+static const struct iio_chan_spec mcp3204_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL(0),
+ MCP320X_VOLTAGE_CHANNEL(1),
+ MCP320X_VOLTAGE_CHANNEL(2),
+ MCP320X_VOLTAGE_CHANNEL(3),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(1),
+};
+
+static const struct iio_chan_spec mcp3208_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL(0),
+ MCP320X_VOLTAGE_CHANNEL(1),
+ MCP320X_VOLTAGE_CHANNEL(2),
+ MCP320X_VOLTAGE_CHANNEL(3),
+ MCP320X_VOLTAGE_CHANNEL(4),
+ MCP320X_VOLTAGE_CHANNEL(5),
+ MCP320X_VOLTAGE_CHANNEL(6),
+ MCP320X_VOLTAGE_CHANNEL(7),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(1),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(2),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(3),
+};
+
+static const struct iio_info mcp320x_info = {
+ .read_raw = mcp320x_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+struct mcp3208_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+static const struct mcp3208_chip_info mcp3208_chip_infos[] = {
+ [mcp3204] = {
+ .channels = mcp3204_channels,
+ .num_channels = ARRAY_SIZE(mcp3204_channels)
+ },
+ [mcp3208] = {
+ .channels = mcp3208_channels,
+ .num_channels = ARRAY_SIZE(mcp3208_channels)
+ },
+};
+
+static int mcp320x_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct mcp320x *adc;
+ const struct mcp3208_chip_info *chip_info;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mcp320x_info;
+
+ chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data];
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = chip_info->num_channels;
+
+ adc->transfer[0].tx_buf = &adc->tx_buf;
+ adc->transfer[0].len = sizeof(adc->tx_buf);
+ adc->transfer[1].rx_buf = adc->rx_buf;
+ adc->transfer[1].len = sizeof(adc->rx_buf);
+
+ spi_message_init_with_transfers(&adc->msg, adc->transfer,
+ ARRAY_SIZE(adc->transfer));
+
+ adc->reg = regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg)) {
+ ret = PTR_ERR(adc->reg);
+ goto iio_free;
+ }
+
+ ret = regulator_enable(adc->reg);
+ if (ret < 0)
+ goto reg_free;
+
+ mutex_init(&adc->lock);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto reg_disable;
+
+ return 0;
+
+reg_disable:
+ regulator_disable(adc->reg);
+reg_free:
+ regulator_put(adc->reg);
+iio_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int mcp320x_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct mcp320x *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(adc->reg);
+ regulator_put(adc->reg);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id mcp320x_id[] = {
+ { "mcp3204", mcp3204 },
+ { "mcp3208", mcp3208 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp320x_id);
+
+static struct spi_driver mcp320x_driver = {
+ .driver = {
+ .name = "mcp320x",
+ .owner = THIS_MODULE,
+ },
+ .probe = mcp320x_probe,
+ .remove = mcp320x_remove,
+ .id_table = mcp320x_id,
+};
+module_spi_driver(mcp320x_driver);
+
+MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
+MODULE_DESCRIPTION("Microchip Technology MCP3204/08");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index 5f9a7e7d3135..4427e8e46a7f 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -22,13 +22,18 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/iio/iio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
#include <linux/mfd/ti_am335x_tscadc.h>
-#include <linux/platform_data/ti_am335x_adc.h>
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
int channels;
+ u8 channel_line[8];
+ u8 channel_step[8];
};
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
@@ -42,10 +47,20 @@ static void tiadc_writel(struct tiadc_device *adc, unsigned int reg,
writel(val, adc->mfd_tscadc->tscadc_base + reg);
}
+static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
+{
+ u32 step_en;
+
+ step_en = ((1 << adc_dev->channels) - 1);
+ step_en <<= TOTAL_STEPS - adc_dev->channels + 1;
+ return step_en;
+}
+
static void tiadc_step_config(struct tiadc_device *adc_dev)
{
unsigned int stepconfig;
- int i, channels = 0, steps;
+ int i, steps;
+ u32 step_en;
/*
* There are 16 configurable steps and 8 analog input
@@ -58,43 +73,63 @@ static void tiadc_step_config(struct tiadc_device *adc_dev)
*/
steps = TOTAL_STEPS - adc_dev->channels;
- channels = TOTAL_CHANNELS - adc_dev->channels;
-
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
- for (i = (steps + 1); i <= TOTAL_STEPS; i++) {
- tiadc_writel(adc_dev, REG_STEPCONFIG(i),
- stepconfig | STEPCONFIG_INP(channels));
- tiadc_writel(adc_dev, REG_STEPDELAY(i),
+ for (i = 0; i < adc_dev->channels; i++) {
+ int chan;
+
+ chan = adc_dev->channel_line[i];
+ tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
+ stepconfig | STEPCONFIG_INP(chan));
+ tiadc_writel(adc_dev, REG_STEPDELAY(steps),
STEPCONFIG_OPENDLY);
- channels++;
+ adc_dev->channel_step[i] = steps;
+ steps++;
}
- tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
+ step_en = get_adc_step_mask(adc_dev);
+ am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
}
+static const char * const chan_name_ain[] = {
+ "AIN0",
+ "AIN1",
+ "AIN2",
+ "AIN3",
+ "AIN4",
+ "AIN5",
+ "AIN6",
+ "AIN7",
+};
+
static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
struct iio_chan_spec *chan_array;
+ struct iio_chan_spec *chan;
int i;
indio_dev->num_channels = channels;
- chan_array = kcalloc(indio_dev->num_channels,
+ chan_array = kcalloc(channels,
sizeof(struct iio_chan_spec), GFP_KERNEL);
-
if (chan_array == NULL)
return -ENOMEM;
- for (i = 0; i < (indio_dev->num_channels); i++) {
- struct iio_chan_spec *chan = chan_array + i;
+ chan = chan_array;
+ for (i = 0; i < channels; i++, chan++) {
+
chan->type = IIO_VOLTAGE;
chan->indexed = 1;
- chan->channel = i;
+ chan->channel = adc_dev->channel_line[i];
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->datasheet_name = chan_name_ain[chan->channel];
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 12;
+ chan->scan_type.storagebits = 32;
}
indio_dev->channels = chan_array;
- return indio_dev->num_channels;
+ return 0;
}
static void tiadc_channels_remove(struct iio_dev *indio_dev)
@@ -108,7 +143,9 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
int i;
- unsigned int fifo1count, readx1;
+ unsigned int fifo1count, read;
+ u32 step = UINT_MAX;
+ bool found = false;
/*
* When the sub-system is first enabled,
@@ -121,14 +158,26 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
* Hence we need to flush out this data.
*/
+ for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
+ if (chan->channel == adc_dev->channel_line[i]) {
+ step = adc_dev->channel_step[i];
+ break;
+ }
+ }
+ if (WARN_ON_ONCE(step == UINT_MAX))
+ return -EINVAL;
+
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
for (i = 0; i < fifo1count; i++) {
- readx1 = tiadc_readl(adc_dev, REG_FIFO1);
- if (i == chan->channel)
- *val = readx1 & 0xfff;
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+ if (read >> 16 == step) {
+ *val = read & 0xfff;
+ found = true;
+ }
}
- tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
-
+ am335x_tsc_se_update(adc_dev->mfd_tscadc);
+ if (found == false)
+ return -EBUSY;
return IIO_VAL_INT;
}
@@ -140,13 +189,15 @@ static int tiadc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct tiadc_device *adc_dev;
- struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
- struct mfd_tscadc_board *pdata;
+ struct device_node *node = pdev->dev.of_node;
+ struct property *prop;
+ const __be32 *cur;
int err;
+ u32 val;
+ int channels = 0;
- pdata = tscadc_dev->dev->platform_data;
- if (!pdata || !pdata->adc_init) {
- dev_err(&pdev->dev, "Could not find platform data\n");
+ if (!node) {
+ dev_err(&pdev->dev, "Could not find valid DT data.\n");
return -EINVAL;
}
@@ -158,8 +209,13 @@ static int tiadc_probe(struct platform_device *pdev)
}
adc_dev = iio_priv(indio_dev);
- adc_dev->mfd_tscadc = tscadc_dev;
- adc_dev->channels = pdata->adc_init->adc_channels;
+ adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
+
+ of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
+ adc_dev->channel_line[channels] = val;
+ channels++;
+ }
+ adc_dev->channels = channels;
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
@@ -191,10 +247,15 @@ err_ret:
static int tiadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ u32 step_en;
iio_device_unregister(indio_dev);
tiadc_channels_remove(indio_dev);
+ step_en = get_adc_step_mask(adc_dev);
+ am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en);
+
iio_device_free(indio_dev);
return 0;
@@ -205,9 +266,10 @@ static int tiadc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
- struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
+ struct ti_tscadc_dev *tscadc_dev;
unsigned int idle;
+ tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
if (!device_may_wakeup(tscadc_dev->dev)) {
idle = tiadc_readl(adc_dev, REG_CTRL);
idle &= ~(CNTRLREG_TSCSSENB);
@@ -243,16 +305,22 @@ static const struct dev_pm_ops tiadc_pm_ops = {
#define TIADC_PM_OPS NULL
#endif
+static const struct of_device_id ti_adc_dt_ids[] = {
+ { .compatible = "ti,am3359-adc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
+
static struct platform_driver tiadc_driver = {
.driver = {
- .name = "tiadc",
+ .name = "TI-am335x-adc",
.owner = THIS_MODULE,
.pm = TIADC_PM_OPS,
+ .of_match_table = of_match_ptr(ti_adc_dt_ids),
},
.probe = tiadc_probe,
.remove = tiadc_remove,
};
-
module_platform_driver(tiadc_driver);
MODULE_DESCRIPTION("TI ADC controller driver");
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
index 09b236d6ee89..71a2c5f63b9c 100644
--- a/drivers/iio/common/st_sensors/st_sensors_buffer.c
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -24,11 +24,20 @@
int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
{
+ u8 *addr;
int i, n = 0, len;
- u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
struct st_sensor_data *sdata = iio_priv(indio_dev);
+ unsigned int num_data_channels = sdata->num_data_channels;
+ unsigned int byte_for_channel =
+ indio_dev->channels[0].scan_type.storagebits >> 3;
- for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
+ addr = kmalloc(num_data_channels, GFP_KERNEL);
+ if (!addr) {
+ len = -ENOMEM;
+ goto st_sensors_get_buffer_element_error;
+ }
+
+ for (i = 0; i < num_data_channels; i++) {
if (test_bit(i, indio_dev->active_scan_mask)) {
addr[n] = indio_dev->channels[i].address;
n++;
@@ -37,52 +46,58 @@ int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
switch (n) {
case 1:
len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
- addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
- sdata->multiread_bit);
+ addr[0], byte_for_channel, buf, sdata->multiread_bit);
break;
case 2:
- if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
+ if ((addr[1] - addr[0]) == byte_for_channel) {
len = sdata->tf->read_multiple_byte(&sdata->tb,
- sdata->dev, addr[0],
- ST_SENSORS_BYTE_FOR_CHANNEL*n,
- buf, sdata->multiread_bit);
+ sdata->dev, addr[0], byte_for_channel * n,
+ buf, sdata->multiread_bit);
} else {
- u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
- ST_SENSORS_NUMBER_DATA_CHANNELS];
+ u8 *rx_array;
+ rx_array = kmalloc(byte_for_channel * num_data_channels,
+ GFP_KERNEL);
+ if (!rx_array) {
+ len = -ENOMEM;
+ goto st_sensors_free_memory;
+ }
+
len = sdata->tf->read_multiple_byte(&sdata->tb,
sdata->dev, addr[0],
- ST_SENSORS_BYTE_FOR_CHANNEL*
- ST_SENSORS_NUMBER_DATA_CHANNELS,
+ byte_for_channel * num_data_channels,
rx_array, sdata->multiread_bit);
- if (len < 0)
- goto read_data_channels_error;
+ if (len < 0) {
+ kfree(rx_array);
+ goto st_sensors_free_memory;
+ }
- for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
- i++) {
+ for (i = 0; i < n * num_data_channels; i++) {
if (i < n)
buf[i] = rx_array[i];
else
buf[i] = rx_array[n + i];
}
- len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
+ kfree(rx_array);
+ len = byte_for_channel * n;
}
break;
case 3:
len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
- addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
- ST_SENSORS_NUMBER_DATA_CHANNELS,
+ addr[0], byte_for_channel * num_data_channels,
buf, sdata->multiread_bit);
break;
default:
len = -EINVAL;
- goto read_data_channels_error;
+ goto st_sensors_free_memory;
}
- if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
+ if (len != byte_for_channel * n) {
len = -EIO;
- goto read_data_channels_error;
+ goto st_sensors_free_memory;
}
-read_data_channels_error:
+st_sensors_free_memory:
+ kfree(addr);
+st_sensors_get_buffer_element_error:
return len;
}
EXPORT_SYMBOL(st_sensors_get_buffer_element);
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index ed9bc8ae9330..865b1781df66 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -20,6 +20,11 @@
#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);
+}
+
static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
u8 reg_addr, u8 mask, u8 data)
{
@@ -112,7 +117,8 @@ st_sensors_match_odr_error:
return ret;
}
-static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs)
+static int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+ unsigned int fs)
{
int err, i = 0;
struct st_sensor_data *sdata = iio_priv(indio_dev);
@@ -273,21 +279,33 @@ st_sensors_match_scale_error:
EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
- u8 ch_addr, int *data)
+ struct iio_chan_spec const *ch, int *data)
{
int err;
- u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
+ u8 *outdata;
struct st_sensor_data *sdata = iio_priv(indio_dev);
+ unsigned int byte_for_channel = ch->scan_type.storagebits >> 3;
+
+ outdata = kmalloc(byte_for_channel, GFP_KERNEL);
+ if (!outdata) {
+ err = -EINVAL;
+ goto st_sensors_read_axis_data_error;
+ }
err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
- ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
+ ch->address, byte_for_channel,
outdata, sdata->multiread_bit);
if (err < 0)
- goto read_error;
+ goto st_sensors_free_memory;
- *data = (s16)get_unaligned_le16(outdata);
+ if (byte_for_channel == 2)
+ *data = (s16)get_unaligned_le16(outdata);
+ else if (byte_for_channel == 3)
+ *data = (s32)st_sensors_get_unaligned_le24(outdata);
-read_error:
+st_sensors_free_memory:
+ kfree(outdata);
+st_sensors_read_axis_data_error:
return err;
}
@@ -307,7 +325,7 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev,
goto read_error;
msleep((sdata->sensor->bootime * 1000) / sdata->odr);
- err = st_sensors_read_axis_data(indio_dev, ch->address, val);
+ err = st_sensors_read_axis_data(indio_dev, ch, val);
if (err < 0)
goto read_error;
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index b61160bd935e..c9c33ce32d3a 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -130,6 +130,16 @@ config AD5686
To compile this driver as a module, choose M here: the
module will be called ad5686.
+config AD7303
+ tristate "Analog Devices Analog Devices AD7303 DAC driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD7303 Digital to Analog
+ Converters (DAC).
+
+ To compile this driver as module choose M here: the module will be called
+ ad7303.
+
config MAX517
tristate "Maxim MAX517/518/519 DAC driver"
depends on I2C
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 5b528ebb3343..c8d7ab6bff01 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -14,5 +14,6 @@ obj-$(CONFIG_AD5755) += ad5755.o
obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
+obj-$(CONFIG_AD7303) += ad7303.o
obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MCP4725) += mcp4725.o
diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c
new file mode 100644
index 000000000000..85aeef60dc5f
--- /dev/null
+++ b/drivers/iio/dac/ad7303.c
@@ -0,0 +1,315 @@
+/*
+ * AD7303 Digital to analog converters driver
+ *
+ * Copyright 2013 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/platform_data/ad7303.h>
+
+#define AD7303_CFG_EXTERNAL_VREF BIT(15)
+#define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch))
+#define AD7303_CFG_ADDR_OFFSET 10
+
+#define AD7303_CMD_UPDATE_DAC (0x3 << 8)
+
+/**
+ * struct ad7303_state - driver instance specific data
+ * @spi: the device for this driver instance
+ * @config: cached config register value
+ * @dac_cache: current DAC raw value (chip does not support readback)
+ * @data: spi transfer buffer
+ */
+
+struct ad7303_state {
+ struct spi_device *spi;
+ uint16_t config;
+ uint8_t dac_cache[2];
+
+ struct regulator *vdd_reg;
+ struct regulator *vref_reg;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 data ____cacheline_aligned;
+};
+
+static int ad7303_write(struct ad7303_state *st, unsigned int chan,
+ uint8_t val)
+{
+ st->data = cpu_to_be16(AD7303_CMD_UPDATE_DAC |
+ (chan << AD7303_CFG_ADDR_OFFSET) |
+ st->config | val);
+
+ return spi_write(st->spi, &st->data, sizeof(st->data));
+}
+
+static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad7303_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", (bool)(st->config &
+ AD7303_CFG_POWER_DOWN(chan->channel)));
+}
+
+static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ struct ad7303_state *st = iio_priv(indio_dev);
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (pwr_down)
+ st->config |= AD7303_CFG_POWER_DOWN(chan->channel);
+ else
+ st->config &= ~AD7303_CFG_POWER_DOWN(chan->channel);
+
+ /* There is no noop cmd which allows us to only update the powerdown
+ * mode, so just write one of the DAC channels again */
+ ad7303_write(st, chan->channel, st->dac_cache[chan->channel]);
+
+ mutex_unlock(&indio_dev->mlock);
+ return ret ? ret : len;
+}
+
+static int ad7303_get_vref(struct ad7303_state *st,
+ struct iio_chan_spec const *chan)
+{
+ int ret;
+
+ if (st->config & AD7303_CFG_EXTERNAL_VREF)
+ return regulator_get_voltage(st->vref_reg);
+
+ ret = regulator_get_voltage(st->vdd_reg);
+ if (ret < 0)
+ return ret;
+ return ret / 2;
+}
+
+static int ad7303_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad7303_state *st = iio_priv(indio_dev);
+ int vref_uv;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ *val = st->dac_cache[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ vref_uv = ad7303_get_vref(st, chan);
+ if (vref_uv < 0)
+ return vref_uv;
+
+ *val = 2 * vref_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int ad7303_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct ad7303_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad7303_write(st, chan->address, val);
+ if (ret == 0)
+ st->dac_cache[chan->channel] = val;
+ mutex_unlock(&indio_dev->mlock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad7303_info = {
+ .read_raw = ad7303_read_raw,
+ .write_raw = ad7303_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad7303_read_dac_powerdown,
+ .write = ad7303_write_dac_powerdown,
+ },
+ { },
+};
+
+#define AD7303_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (chan), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = '8', \
+ .storagebits = '8', \
+ .shift = '0', \
+ }, \
+ .ext_info = ad7303_ext_info, \
+}
+
+static const struct iio_chan_spec ad7303_channels[] = {
+ AD7303_CHANNEL(0),
+ AD7303_CHANNEL(1),
+};
+
+static int ad7303_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct ad7303_state *st;
+ bool ext_ref;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ st->vdd_reg = regulator_get(&spi->dev, "Vdd");
+ if (IS_ERR(st->vdd_reg)) {
+ ret = PTR_ERR(st->vdd_reg);
+ goto err_free;
+ }
+
+ ret = regulator_enable(st->vdd_reg);
+ if (ret)
+ goto err_put_vdd_reg;
+
+ if (spi->dev.of_node) {
+ ext_ref = of_property_read_bool(spi->dev.of_node,
+ "REF-supply");
+ } else {
+ struct ad7303_platform_data *pdata = spi->dev.platform_data;
+ if (pdata && pdata->use_external_ref)
+ ext_ref = true;
+ else
+ ext_ref = false;
+ }
+
+ if (ext_ref) {
+ st->vref_reg = regulator_get(&spi->dev, "REF");
+ if (IS_ERR(st->vref_reg))
+ goto err_disable_vdd_reg;
+
+ ret = regulator_enable(st->vref_reg);
+ if (ret)
+ goto err_put_vref_reg;
+
+ st->config |= AD7303_CFG_EXTERNAL_VREF;
+ }
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = id->name;
+ indio_dev->info = &ad7303_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7303_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7303_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_disable_vref_reg;
+
+ return 0;
+
+err_disable_vref_reg:
+ if (st->vref_reg)
+ regulator_disable(st->vref_reg);
+err_put_vref_reg:
+ if (st->vref_reg)
+ regulator_put(st->vref_reg);
+err_disable_vdd_reg:
+ regulator_disable(st->vdd_reg);
+err_put_vdd_reg:
+ regulator_put(st->vdd_reg);
+err_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int ad7303_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7303_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (st->vref_reg) {
+ regulator_disable(st->vref_reg);
+ regulator_put(st->vref_reg);
+ }
+ regulator_disable(st->vdd_reg);
+ regulator_put(st->vdd_reg);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7303_spi_ids[] = {
+ { "ad7303", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
+
+static struct spi_driver ad7303_driver = {
+ .driver = {
+ .name = "ad7303",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7303_probe,
+ .remove = ad7303_remove,
+ .id_table = ad7303_spi_ids,
+};
+module_spi_driver(ad7303_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index e76d4ace53ff..a4157cdb314d 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -1,7 +1,7 @@
/*
* ADF4350/ADF4351 SPI Wideband Synthesizer driver
*
- * Copyright 2012 Analog Devices Inc.
+ * Copyright 2012-2013 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
@@ -17,6 +17,9 @@
#include <linux/gcd.h>
#include <linux/gpio.h>
#include <asm/div64.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -33,6 +36,7 @@ struct adf4350_state {
struct spi_device *spi;
struct regulator *reg;
struct adf4350_platform_data *pdata;
+ struct clk *clk;
unsigned long clkin;
unsigned long chspc; /* Channel Spacing */
unsigned long fpfd; /* Phase Frequency Detector */
@@ -43,7 +47,7 @@ struct adf4350_state {
unsigned r4_rf_div_sel;
unsigned long regs[6];
unsigned long regs_hw[6];
-
+ unsigned long long freq_req;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
@@ -52,7 +56,6 @@ struct adf4350_state {
};
static struct adf4350_platform_data default_pdata = {
- .clkin = 122880000,
.channel_spacing = 10000,
.r2_user_settings = ADF4350_REG2_PD_POLARITY_POS |
ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500),
@@ -235,6 +238,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
ADF4350_REG4_MUTE_TILL_LOCK_EN));
st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL;
+ st->freq_req = freq;
return adf4350_sync_config(st);
}
@@ -246,6 +250,7 @@ static ssize_t adf4350_write(struct iio_dev *indio_dev,
{
struct adf4350_state *st = iio_priv(indio_dev);
unsigned long long readin;
+ unsigned long tmp;
int ret;
ret = kstrtoull(buf, 10, &readin);
@@ -258,10 +263,23 @@ static ssize_t adf4350_write(struct iio_dev *indio_dev,
ret = adf4350_set_freq(st, readin);
break;
case ADF4350_FREQ_REFIN:
- if (readin > ADF4350_MAX_FREQ_REFIN)
+ if (readin > ADF4350_MAX_FREQ_REFIN) {
ret = -EINVAL;
- else
- st->clkin = readin;
+ break;
+ }
+
+ if (st->clk) {
+ tmp = clk_round_rate(st->clk, readin);
+ if (tmp != readin) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = clk_set_rate(st->clk, tmp);
+ if (ret < 0)
+ break;
+ }
+ st->clkin = readin;
+ ret = adf4350_set_freq(st, st->freq_req);
break;
case ADF4350_FREQ_RESOLUTION:
if (readin == 0)
@@ -308,6 +326,9 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev,
}
break;
case ADF4350_FREQ_REFIN:
+ if (st->clk)
+ st->clkin = clk_get_rate(st->clk);
+
val = st->clkin;
break;
case ADF4350_FREQ_RESOLUTION:
@@ -318,6 +339,7 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev,
break;
default:
ret = -EINVAL;
+ val = 0;
}
mutex_unlock(&indio_dev->mlock);
@@ -355,19 +377,153 @@ static const struct iio_info adf4350_info = {
.driver_module = THIS_MODULE,
};
+#ifdef CONFIG_OF
+static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct adf4350_platform_data *pdata;
+ unsigned int tmp;
+ int ret;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "could not allocate memory for platform data\n");
+ return NULL;
+ }
+
+ strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1);
+
+ tmp = 10000;
+ of_property_read_u32(np, "adi,channel-spacing", &tmp);
+ pdata->channel_spacing = tmp;
+
+ tmp = 0;
+ of_property_read_u32(np, "adi,power-up-frequency", &tmp);
+ pdata->power_up_frequency = tmp;
+
+ tmp = 0;
+ of_property_read_u32(np, "adi,reference-div-factor", &tmp);
+ pdata->ref_div_factor = tmp;
+
+ ret = of_get_gpio(np, 0);
+ if (ret < 0)
+ pdata->gpio_lock_detect = -1;
+ else
+ pdata->gpio_lock_detect = ret;
+
+ pdata->ref_doubler_en = of_property_read_bool(np,
+ "adi,reference-doubler-enable");
+ pdata->ref_div2_en = of_property_read_bool(np,
+ "adi,reference-div2-enable");
+
+ /* r2_user_settings */
+ pdata->r2_user_settings = of_property_read_bool(np,
+ "adi,phase-detector-polarity-positive-enable") ?
+ ADF4350_REG2_PD_POLARITY_POS : 0;
+ pdata->r2_user_settings |= of_property_read_bool(np,
+ "adi,lock-detect-precision-6ns-enable") ?
+ ADF4350_REG2_LDP_6ns : 0;
+ pdata->r2_user_settings |= of_property_read_bool(np,
+ "adi,lock-detect-function-integer-n-enable") ?
+ ADF4350_REG2_LDF_INT_N : 0;
+
+ tmp = 2500;
+ of_property_read_u32(np, "adi,charge-pump-current", &tmp);
+ pdata->r2_user_settings |= ADF4350_REG2_CHARGE_PUMP_CURR_uA(tmp);
+
+ tmp = 0;
+ of_property_read_u32(np, "adi,muxout-select", &tmp);
+ pdata->r2_user_settings |= ADF4350_REG2_MUXOUT(tmp);
+
+ pdata->r2_user_settings |= of_property_read_bool(np,
+ "adi,low-spur-mode-enable") ?
+ ADF4350_REG2_NOISE_MODE(0x3) : 0;
+
+ /* r3_user_settings */
+
+ pdata->r3_user_settings = of_property_read_bool(np,
+ "adi,cycle-slip-reduction-enable") ?
+ ADF4350_REG3_12BIT_CSR_EN : 0;
+ pdata->r3_user_settings |= of_property_read_bool(np,
+ "adi,charge-cancellation-enable") ?
+ ADF4351_REG3_CHARGE_CANCELLATION_EN : 0;
+
+ pdata->r3_user_settings |= of_property_read_bool(np,
+ "adi,anti-backlash-3ns-enable") ?
+ ADF4351_REG3_ANTI_BACKLASH_3ns_EN : 0;
+ pdata->r3_user_settings |= of_property_read_bool(np,
+ "adi,band-select-clock-mode-high-enable") ?
+ ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH : 0;
+
+ tmp = 0;
+ of_property_read_u32(np, "adi,12bit-clk-divider", &tmp);
+ pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV(tmp);
+
+ tmp = 0;
+ of_property_read_u32(np, "adi,clk-divider-mode", &tmp);
+ pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV_MODE(tmp);
+
+ /* r4_user_settings */
+
+ pdata->r4_user_settings = of_property_read_bool(np,
+ "adi,aux-output-enable") ?
+ ADF4350_REG4_AUX_OUTPUT_EN : 0;
+ pdata->r4_user_settings |= of_property_read_bool(np,
+ "adi,aux-output-fundamental-enable") ?
+ ADF4350_REG4_AUX_OUTPUT_FUND : 0;
+ pdata->r4_user_settings |= of_property_read_bool(np,
+ "adi,mute-till-lock-enable") ?
+ ADF4350_REG4_MUTE_TILL_LOCK_EN : 0;
+
+ tmp = 0;
+ of_property_read_u32(np, "adi,output-power", &tmp);
+ pdata->r4_user_settings |= ADF4350_REG4_OUTPUT_PWR(tmp);
+
+ tmp = 0;
+ of_property_read_u32(np, "adi,aux-output-power", &tmp);
+ pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_PWR(tmp);
+
+ return pdata;
+}
+#else
+static
+struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
static int adf4350_probe(struct spi_device *spi)
{
- struct adf4350_platform_data *pdata = spi->dev.platform_data;
+ struct adf4350_platform_data *pdata;
struct iio_dev *indio_dev;
struct adf4350_state *st;
+ struct clk *clk = NULL;
int ret;
+ if (spi->dev.of_node) {
+ pdata = adf4350_parse_dt(&spi->dev);
+ if (pdata == NULL)
+ return -EINVAL;
+ } else {
+ pdata = spi->dev.platform_data;
+ }
+
if (!pdata) {
dev_warn(&spi->dev, "no platform data? using default\n");
-
pdata = &default_pdata;
}
+ if (!pdata->clkin) {
+ clk = clk_get(&spi->dev, "clkin");
+ if (IS_ERR(clk))
+ return -EPROBE_DEFER;
+
+ ret = clk_prepare_enable(clk);
+ if (ret < 0)
+ return ret;
+ }
+
indio_dev = iio_device_alloc(sizeof(*st));
if (indio_dev == NULL)
return -ENOMEM;
@@ -395,7 +551,12 @@ static int adf4350_probe(struct spi_device *spi)
indio_dev->num_channels = 1;
st->chspc = pdata->channel_spacing;
- st->clkin = pdata->clkin;
+ if (clk) {
+ st->clk = clk;
+ st->clkin = clk_get_rate(clk);
+ } else {
+ st->clkin = pdata->clkin;
+ }
st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ?
ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ;
@@ -435,6 +596,8 @@ error_put_reg:
if (!IS_ERR(st->reg))
regulator_put(st->reg);
+ if (clk)
+ clk_disable_unprepare(clk);
iio_device_free(indio_dev);
return ret;
@@ -451,6 +614,9 @@ static int adf4350_remove(struct spi_device *spi)
iio_device_unregister(indio_dev);
+ if (st->clk)
+ clk_disable_unprepare(st->clk);
+
if (!IS_ERR(reg)) {
regulator_disable(reg);
regulator_put(reg);
@@ -481,6 +647,6 @@ static struct spi_driver adf4350_driver = {
};
module_spi_driver(adf4350_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index 6be4628faffe..8498e9dcda68 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -10,6 +10,13 @@ config ADIS16080
Say yes here to build support for Analog Devices ADIS16080, ADIS16100 Yaw
Rate Gyroscope with SPI.
+config ADIS16130
+ tristate "Analog Devices ADIS16130 High Precision Angular Rate Sensor driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices ADIS16130 High Precision
+ Angular Rate Sensor driver.
+
config ADIS16136
tristate "Analog devices ADIS16136 and similar gyroscopes driver"
depends on SPI_MASTER
@@ -47,7 +54,6 @@ config IIO_ST_GYRO_3AXIS
select IIO_ST_GYRO_I2C_3AXIS if (I2C)
select IIO_ST_GYRO_SPI_3AXIS if (SPI_MASTER)
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
- select IIO_ST_GYRO_BUFFER if (IIO_TRIGGERED_BUFFER)
help
Say yes here to build support for STMicroelectronics gyroscopes:
L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330DLC, L3G4IS, LSM330.
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index 225d289082e6..e9dc034aa18b 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_ADIS16080) += adis16080.o
+obj-$(CONFIG_ADIS16130) += adis16130.o
obj-$(CONFIG_ADIS16136) += adis16136.o
obj-$(CONFIG_ADXRS450) += adxrs450.o
diff --git a/drivers/iio/gyro/adis16130.c b/drivers/iio/gyro/adis16130.c
new file mode 100644
index 000000000000..129acdf801a4
--- /dev/null
+++ b/drivers/iio/gyro/adis16130.c
@@ -0,0 +1,207 @@
+/*
+ * ADIS16130 Digital Output, High Precision Angular Rate Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+
+#define ADIS16130_CON 0x0
+#define ADIS16130_CON_RD (1 << 6)
+#define ADIS16130_IOP 0x1
+
+/* 1 = data-ready signal low when unread data on all channels; */
+#define ADIS16130_IOP_ALL_RDY (1 << 3)
+#define ADIS16130_IOP_SYNC (1 << 0) /* 1 = synchronization enabled */
+#define ADIS16130_RATEDATA 0x8 /* Gyroscope output, rate of rotation */
+#define ADIS16130_TEMPDATA 0xA /* Temperature output */
+#define ADIS16130_RATECS 0x28 /* Gyroscope channel setup */
+#define ADIS16130_RATECS_EN (1 << 3) /* 1 = channel enable; */
+#define ADIS16130_TEMPCS 0x2A /* Temperature channel setup */
+#define ADIS16130_TEMPCS_EN (1 << 3)
+#define ADIS16130_RATECONV 0x30
+#define ADIS16130_TEMPCONV 0x32
+#define ADIS16130_MODE 0x38
+#define ADIS16130_MODE_24BIT (1 << 1) /* 1 = 24-bit resolution; */
+
+/**
+ * struct adis16130_state - device instance specific data
+ * @us: actual spi_device to write data
+ * @buf_lock: mutex to protect tx and rx
+ * @buf: unified tx/rx buffer
+ **/
+struct adis16130_state {
+ struct spi_device *us;
+ struct mutex buf_lock;
+ u8 buf[4] ____cacheline_aligned;
+};
+
+static int adis16130_spi_read(struct iio_dev *indio_dev, u8 reg_addr, u32 *val)
+{
+ int ret;
+ struct adis16130_state *st = iio_priv(indio_dev);
+ struct spi_message msg;
+ struct spi_transfer xfer = {
+ .tx_buf = st->buf,
+ .rx_buf = st->buf,
+ .len = 4,
+ };
+
+ mutex_lock(&st->buf_lock);
+
+ st->buf[0] = ADIS16130_CON_RD | reg_addr;
+ st->buf[1] = st->buf[2] = st->buf[3] = 0;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+ ret = spi_sync(st->us, &msg);
+
+ if (ret == 0)
+ *val = (st->buf[1] << 16) | (st->buf[2] << 8) | st->buf[3];
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int adis16130_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ int ret;
+ u32 temp;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ ret = adis16130_spi_read(indio_dev, chan->address, &temp);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret)
+ return ret;
+ *val = temp;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ /* 0 degree = 838860, 250 degree = 14260608 */
+ *val = 250;
+ *val2 = 336440817; /* RAD_TO_DEGREE(14260608 - 8388608) */
+ return IIO_VAL_FRACTIONAL;
+ case IIO_TEMP:
+ /* 0C = 8036283, 105C = 9516048 */
+ *val = 105000;
+ *val2 = 9516048 - 8036283;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val = -8388608;
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = -8036283;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16130_channels[] = {
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = ADIS16130_RATEDATA,
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = ADIS16130_TEMPDATA,
+ }
+};
+
+static const struct iio_info adis16130_info = {
+ .read_raw = &adis16130_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int adis16130_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis16130_state *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+ st->us = spi;
+ mutex_init(&st->buf_lock);
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->channels = adis16130_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16130_channels);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16130_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_dev;
+
+ return 0;
+
+error_free_dev:
+ iio_device_free(indio_dev);
+
+error_ret:
+ return ret;
+}
+
+static int adis16130_remove(struct spi_device *spi)
+{
+ iio_device_unregister(spi_get_drvdata(spi));
+ iio_device_free(spi_get_drvdata(spi));
+
+ return 0;
+}
+
+static struct spi_driver adis16130_driver = {
+ .driver = {
+ .name = "adis16130",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16130_probe,
+ .remove = adis16130_remove,
+};
+module_spi_driver(adis16130_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16130 High Precision Angular Rate");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16130");
diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c
index fa9b24219987..f9ed3488c314 100644
--- a/drivers/iio/gyro/st_gyro_core.c
+++ b/drivers/iio/gyro/st_gyro_core.c
@@ -27,6 +27,8 @@
#include <linux/iio/common/st_sensors.h>
#include "st_gyro.h"
+#define ST_GYRO_NUMBER_DATA_CHANNELS 3
+
/* DEFAULT VALUE FOR SENSORS */
#define ST_GYRO_DEFAULT_OUT_X_L_ADDR 0x28
#define ST_GYRO_DEFAULT_OUT_Y_L_ADDR 0x2a
@@ -86,15 +88,18 @@
#define ST_GYRO_2_MULTIREAD_BIT true
static const struct iio_chan_spec st_gyro_16bit_channels[] = {
- ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_X,
- IIO_MOD_X, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS,
- ST_GYRO_DEFAULT_OUT_X_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Y,
- IIO_MOD_Y, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS,
- ST_GYRO_DEFAULT_OUT_Y_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Z,
- IIO_MOD_Z, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS,
- ST_GYRO_DEFAULT_OUT_Z_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
+ ST_GYRO_DEFAULT_OUT_X_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
+ ST_GYRO_DEFAULT_OUT_Y_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
+ ST_GYRO_DEFAULT_OUT_Z_L_ADDR),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
@@ -310,6 +315,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
if (err < 0)
goto st_gyro_common_probe_error;
+ gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;
gdata->multiread_bit = gdata->sensor->multi_read_bit;
indio_dev->channels = gdata->sensor->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index aaadd32f9f0d..e73033f3839a 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -542,8 +542,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
ret = indio_dev->setup_ops->preenable(indio_dev);
if (ret) {
printk(KERN_ERR
- "Buffer not started:"
- "buffer preenable failed\n");
+ "Buffer not started: buffer preenable failed (%d)\n", ret);
goto error_remove_inserted;
}
}
@@ -556,8 +555,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
ret = buffer->access->request_update(buffer);
if (ret) {
printk(KERN_INFO
- "Buffer not started:"
- "buffer parameter update failed\n");
+ "Buffer not started: buffer parameter update failed (%d)\n", ret);
goto error_run_postdisable;
}
}
@@ -566,7 +564,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
->update_scan_mode(indio_dev,
indio_dev->active_scan_mask);
if (ret < 0) {
- printk(KERN_INFO "update scan mode failed\n");
+ printk(KERN_INFO "Buffer not started: update scan mode failed (%d)\n", ret);
goto error_run_postdisable;
}
}
@@ -590,7 +588,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
ret = indio_dev->setup_ops->postenable(indio_dev);
if (ret) {
printk(KERN_INFO
- "Buffer not started: postenable failed\n");
+ "Buffer not started: postenable failed (%d)\n", ret);
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->setup_ops->postdisable)
indio_dev->setup_ops->postdisable(indio_dev);
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index 80d68ff02d29..cdc2cad0f01b 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -31,7 +31,7 @@
#include "../common/hid-sensors/hid-sensor-trigger.h"
/*Format: HID-SENSOR-usage_id_in_hex*/
-/*Usage ID from spec for Accelerometer-3D: 0x200041*/
+/*Usage ID from spec for Ambiant-Light: 0x200041*/
#define DRIVER_NAME "HID-SENSOR-200041"
#define CHANNEL_SCAN_INDEX_ILLUM 0
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index bd1cfb666695..c332b0ae4a3b 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -32,7 +32,6 @@ config IIO_ST_MAGN_3AXIS
select IIO_ST_MAGN_I2C_3AXIS if (I2C)
select IIO_ST_MAGN_SPI_3AXIS if (SPI_MASTER)
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
- select IIO_ST_MAGN_BUFFER if (IIO_TRIGGERED_BUFFER)
help
Say yes here to build support for STMicroelectronics magnetometers:
LSM303DLHC, LSM303DLM, LIS3MDL.
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index af6c320a534e..7105f22d6cd7 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -24,11 +24,13 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/delay.h>
-
+#include <linux/bitops.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -82,6 +84,7 @@
*/
#define AK8975_MAX_CONVERSION_TIMEOUT 500
#define AK8975_CONVERSION_DONE_POLL_TIME 10
+#define AK8975_DATA_READY_TIMEOUT ((100*HZ)/1000)
/*
* Per-instance context data for the device.
@@ -94,6 +97,9 @@ struct ak8975_data {
long raw_to_gauss[3];
u8 reg_cache[AK8975_MAX_REGS];
int eoc_gpio;
+ int eoc_irq;
+ wait_queue_head_t data_ready_queue;
+ unsigned long flags;
};
static const int ak8975_index_to_reg[] = {
@@ -123,6 +129,51 @@ static int ak8975_write_data(struct i2c_client *client,
}
/*
+ * Handle data ready irq
+ */
+static irqreturn_t ak8975_irq_handler(int irq, void *data)
+{
+ struct ak8975_data *ak8975 = data;
+
+ set_bit(0, &ak8975->flags);
+ wake_up(&ak8975->data_ready_queue);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Install data ready interrupt handler
+ */
+static int ak8975_setup_irq(struct ak8975_data *data)
+{
+ struct i2c_client *client = data->client;
+ int rc;
+ int irq;
+
+ if (client->irq)
+ irq = client->irq;
+ else
+ irq = gpio_to_irq(data->eoc_gpio);
+
+ rc = request_irq(irq, ak8975_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(&client->dev), data);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "irq %d request failed, (gpio %d): %d\n",
+ irq, data->eoc_gpio, rc);
+ return rc;
+ }
+
+ init_waitqueue_head(&data->data_ready_queue);
+ clear_bit(0, &data->flags);
+ data->eoc_irq = irq;
+
+ return rc;
+}
+
+
+/*
* Perform some start-of-day setup, including reading the asa calibration
* values and caching them.
*/
@@ -170,6 +221,16 @@ static int ak8975_setup(struct i2c_client *client)
AK8975_REG_CNTL_MODE_POWER_DOWN,
AK8975_REG_CNTL_MODE_MASK,
AK8975_REG_CNTL_MODE_SHIFT);
+
+ if (data->eoc_gpio > 0 || client->irq) {
+ ret = ak8975_setup_irq(data);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Error setting data ready interrupt\n");
+ return ret;
+ }
+ }
+
if (ret < 0) {
dev_err(&client->dev, "Error in setting power-down mode\n");
return ret;
@@ -266,9 +327,23 @@ static int wait_conversion_complete_polled(struct ak8975_data *data)
dev_err(&client->dev, "Conversion timeout happened\n");
return -EINVAL;
}
+
return read_status;
}
+/* Returns 0 if the end of conversion interrupt occured or -ETIME otherwise */
+static int wait_conversion_complete_interrupt(struct ak8975_data *data)
+{
+ int ret;
+
+ ret = wait_event_timeout(data->data_ready_queue,
+ test_bit(0, &data->flags),
+ AK8975_DATA_READY_TIMEOUT);
+ clear_bit(0, &data->flags);
+
+ return ret > 0 ? 0 : -ETIME;
+}
+
/*
* Emits the raw flux value for the x, y, or z axis.
*/
@@ -294,13 +369,16 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
}
/* Wait for the conversion to complete. */
- if (gpio_is_valid(data->eoc_gpio))
+ if (data->eoc_irq)
+ ret = wait_conversion_complete_interrupt(data);
+ else if (gpio_is_valid(data->eoc_gpio))
ret = wait_conversion_complete_gpio(data);
else
ret = wait_conversion_complete_polled(data);
if (ret < 0)
goto exit;
+ /* This will be executed only for non-interrupt based waiting case */
if (ret & AK8975_REG_ST1_DRDY_MASK) {
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST2);
if (ret < 0) {
@@ -384,10 +462,15 @@ static int ak8975_probe(struct i2c_client *client,
int err;
/* Grab and set up the supplied GPIO. */
- if (client->dev.platform_data == NULL)
- eoc_gpio = -1;
- else
+ if (client->dev.platform_data)
eoc_gpio = *(int *)(client->dev.platform_data);
+ else if (client->dev.of_node)
+ eoc_gpio = of_get_gpio(client->dev.of_node, 0);
+ else
+ eoc_gpio = -1;
+
+ if (eoc_gpio == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
/* We may not have a GPIO based IRQ to scan, that is fine, we will
poll if so */
@@ -409,6 +492,11 @@ static int ak8975_probe(struct i2c_client *client,
}
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
+
+ data->client = client;
+ data->eoc_gpio = eoc_gpio;
+ data->eoc_irq = 0;
+
/* Perform some basic start-of-day setup of the device. */
err = ak8975_setup(client);
if (err < 0) {
@@ -433,6 +521,8 @@ static int ak8975_probe(struct i2c_client *client,
exit_free_iio:
iio_device_free(indio_dev);
+ if (data->eoc_irq)
+ free_irq(data->eoc_irq, data);
exit_gpio:
if (gpio_is_valid(eoc_gpio))
gpio_free(eoc_gpio);
@@ -447,6 +537,9 @@ static int ak8975_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
+ if (data->eoc_irq)
+ free_irq(data->eoc_irq, data);
+
if (gpio_is_valid(data->eoc_gpio))
gpio_free(data->eoc_gpio);
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index 16f0d6df239f..ebfe8f11a0c2 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -26,6 +26,8 @@
#include <linux/iio/common/st_sensors.h>
#include "st_magn.h"
+#define ST_MAGN_NUMBER_DATA_CHANNELS 3
+
/* DEFAULT VALUE FOR SENSORS */
#define ST_MAGN_DEFAULT_OUT_X_L_ADDR 0X04
#define ST_MAGN_DEFAULT_OUT_Y_L_ADDR 0X08
@@ -113,22 +115,34 @@
#define ST_MAGN_2_OUT_Z_L_ADDR 0x2c
static const struct iio_chan_spec st_magn_16bit_channels[] = {
- ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
- ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_X_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
- ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Y_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
- ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Z_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
+ ST_MAGN_DEFAULT_OUT_X_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
+ ST_MAGN_DEFAULT_OUT_Y_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
+ ST_MAGN_DEFAULT_OUT_Z_L_ADDR),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
- ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
- ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_X_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
- ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Y_L_ADDR),
- ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
- ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Z_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
+ ST_MAGN_2_OUT_X_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
+ ST_MAGN_2_OUT_Y_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
+ ST_MAGN_2_OUT_Z_L_ADDR),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
@@ -344,6 +358,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
if (err < 0)
goto st_magn_common_probe_error;
+ mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;
mdata->multiread_bit = mdata->sensor->multi_read_bit;
indio_dev->channels = mdata->sensor->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
new file mode 100644
index 000000000000..9427f01e1499
--- /dev/null
+++ b/drivers/iio/pressure/Kconfig
@@ -0,0 +1,35 @@
+#
+# Pressure drivers
+#
+menu "Pressure Sensors"
+
+config IIO_ST_PRESS
+ tristate "STMicroelectronics pressures Driver"
+ depends on (I2C || SPI_MASTER) && SYSFS
+ select IIO_ST_SENSORS_CORE
+ select IIO_ST_PRESS_I2C if (I2C)
+ select IIO_ST_PRESS_SPI if (SPI_MASTER)
+ select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
+ help
+ Say yes here to build support for STMicroelectronics pressures:
+ LPS331AP.
+
+ This driver can also be built as a module. If so, will be created
+ these modules:
+ - st_pressure (core functions for the driver [it is mandatory]);
+ - st_pressure_i2c (necessary for the I2C devices [optional*]);
+ - st_pressure_spi (necessary for the SPI devices [optional*]);
+
+ (*) one of these is necessary to do something.
+
+config IIO_ST_PRESS_I2C
+ tristate
+ depends on IIO_ST_PRESS
+ depends on IIO_ST_SENSORS_I2C
+
+config IIO_ST_PRESS_SPI
+ tristate
+ depends on IIO_ST_PRESS
+ depends on IIO_ST_SENSORS_SPI
+
+endmenu
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
new file mode 100644
index 000000000000..d4bb33e5c846
--- /dev/null
+++ b/drivers/iio/pressure/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for industrial I/O pressure drivers
+#
+
+obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o
+st_pressure-y := st_pressure_core.o
+st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
+
+obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o
+obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o
diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h
new file mode 100644
index 000000000000..414e45ac9b9b
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure.h
@@ -0,0 +1,39 @@
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_PRESS_H
+#define ST_PRESS_H
+
+#include <linux/types.h>
+#include <linux/iio/common/st_sensors.h>
+
+#define LPS331AP_PRESS_DEV_NAME "lps331ap"
+
+int st_press_common_probe(struct iio_dev *indio_dev);
+void st_press_common_remove(struct iio_dev *indio_dev);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_press_allocate_ring(struct iio_dev *indio_dev);
+void st_press_deallocate_ring(struct iio_dev *indio_dev);
+int st_press_trig_set_state(struct iio_trigger *trig, bool state);
+#define ST_PRESS_TRIGGER_SET_STATE (&st_press_trig_set_state)
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_press_allocate_ring(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline void st_press_deallocate_ring(struct iio_dev *indio_dev)
+{
+}
+#define ST_PRESS_TRIGGER_SET_STATE NULL
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_PRESS_H */
diff --git a/drivers/iio/pressure/st_pressure_buffer.c b/drivers/iio/pressure/st_pressure_buffer.c
new file mode 100644
index 000000000000..f877ef8af520
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure_buffer.c
@@ -0,0 +1,105 @@
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include "st_pressure.h"
+
+int st_press_trig_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+
+ return st_sensors_set_dataready_irq(indio_dev, state);
+}
+
+static int st_press_buffer_preenable(struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = st_sensors_set_enable(indio_dev, true);
+ if (err < 0)
+ goto st_press_set_enable_error;
+
+ err = iio_sw_buffer_preenable(indio_dev);
+
+st_press_set_enable_error:
+ return err;
+}
+
+static int st_press_buffer_postenable(struct iio_dev *indio_dev)
+{
+ int err;
+ struct st_sensor_data *pdata = iio_priv(indio_dev);
+
+ pdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (pdata->buffer_data == NULL) {
+ err = -ENOMEM;
+ goto allocate_memory_error;
+ }
+
+ err = iio_triggered_buffer_postenable(indio_dev);
+ if (err < 0)
+ goto st_press_buffer_postenable_error;
+
+ return err;
+
+st_press_buffer_postenable_error:
+ kfree(pdata->buffer_data);
+allocate_memory_error:
+ return err;
+}
+
+static int st_press_buffer_predisable(struct iio_dev *indio_dev)
+{
+ int err;
+ struct st_sensor_data *pdata = iio_priv(indio_dev);
+
+ err = iio_triggered_buffer_predisable(indio_dev);
+ if (err < 0)
+ goto st_press_buffer_predisable_error;
+
+ err = st_sensors_set_enable(indio_dev, false);
+
+st_press_buffer_predisable_error:
+ kfree(pdata->buffer_data);
+ return err;
+}
+
+static const struct iio_buffer_setup_ops st_press_buffer_setup_ops = {
+ .preenable = &st_press_buffer_preenable,
+ .postenable = &st_press_buffer_postenable,
+ .predisable = &st_press_buffer_predisable,
+};
+
+int st_press_allocate_ring(struct iio_dev *indio_dev)
+{
+ return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ &st_sensors_trigger_handler, &st_press_buffer_setup_ops);
+}
+
+void st_press_deallocate_ring(struct iio_dev *indio_dev)
+{
+ iio_triggered_buffer_cleanup(indio_dev);
+}
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics pressures buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
new file mode 100644
index 000000000000..9c343b40665e
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -0,0 +1,272 @@
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <asm/unaligned.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include "st_pressure.h"
+
+#define ST_PRESS_MBAR_TO_KPASCAL(x) (x * 10)
+#define ST_PRESS_NUMBER_DATA_CHANNELS 1
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_PRESS_DEFAULT_OUT_XL_ADDR 0x28
+#define ST_TEMP_DEFAULT_OUT_L_ADDR 0x2b
+
+/* FULLSCALE */
+#define ST_PRESS_FS_AVL_1260MB 1260
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_PRESS_1_WAI_EXP 0xbb
+#define ST_PRESS_1_ODR_ADDR 0x20
+#define ST_PRESS_1_ODR_MASK 0x70
+#define ST_PRESS_1_ODR_AVL_1HZ_VAL 0x01
+#define ST_PRESS_1_ODR_AVL_7HZ_VAL 0x05
+#define ST_PRESS_1_ODR_AVL_13HZ_VAL 0x06
+#define ST_PRESS_1_ODR_AVL_25HZ_VAL 0x07
+#define ST_PRESS_1_PW_ADDR 0x20
+#define ST_PRESS_1_PW_MASK 0x80
+#define ST_PRESS_1_FS_ADDR 0x23
+#define ST_PRESS_1_FS_MASK 0x30
+#define ST_PRESS_1_FS_AVL_1260_VAL 0x00
+#define ST_PRESS_1_FS_AVL_1260_GAIN ST_PRESS_MBAR_TO_KPASCAL(244141)
+#define ST_PRESS_1_FS_AVL_TEMP_GAIN 2083000
+#define ST_PRESS_1_BDU_ADDR 0x20
+#define ST_PRESS_1_BDU_MASK 0x04
+#define ST_PRESS_1_DRDY_IRQ_ADDR 0x22
+#define ST_PRESS_1_DRDY_IRQ_MASK 0x04
+#define ST_PRESS_1_MULTIREAD_BIT true
+#define ST_PRESS_1_TEMP_OFFSET 42500
+
+static const struct iio_chan_spec st_press_channels[] = {
+ ST_SENSORS_LSM_CHANNELS(IIO_PRESSURE,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_X, 0, IIO_NO_MOD, 'u', IIO_LE, 24, 24,
+ ST_PRESS_DEFAULT_OUT_XL_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_TEMP,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ -1, 0, IIO_NO_MOD, 's', IIO_LE, 16, 16,
+ ST_TEMP_DEFAULT_OUT_L_ADDR),
+ IIO_CHAN_SOFT_TIMESTAMP(1)
+};
+
+static const struct st_sensors st_press_sensors[] = {
+ {
+ .wai = ST_PRESS_1_WAI_EXP,
+ .sensors_supported = {
+ [0] = LPS331AP_PRESS_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_press_channels,
+ .odr = {
+ .addr = ST_PRESS_1_ODR_ADDR,
+ .mask = ST_PRESS_1_ODR_MASK,
+ .odr_avl = {
+ { 1, ST_PRESS_1_ODR_AVL_1HZ_VAL, },
+ { 7, ST_PRESS_1_ODR_AVL_7HZ_VAL, },
+ { 13, ST_PRESS_1_ODR_AVL_13HZ_VAL, },
+ { 25, ST_PRESS_1_ODR_AVL_25HZ_VAL, },
+ },
+ },
+ .pw = {
+ .addr = ST_PRESS_1_PW_ADDR,
+ .mask = ST_PRESS_1_PW_MASK,
+ .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .fs = {
+ .addr = ST_PRESS_1_FS_ADDR,
+ .mask = ST_PRESS_1_FS_MASK,
+ .fs_avl = {
+ [0] = {
+ .num = ST_PRESS_FS_AVL_1260MB,
+ .value = ST_PRESS_1_FS_AVL_1260_VAL,
+ .gain = ST_PRESS_1_FS_AVL_1260_GAIN,
+ .gain2 = ST_PRESS_1_FS_AVL_TEMP_GAIN,
+ },
+ },
+ },
+ .bdu = {
+ .addr = ST_PRESS_1_BDU_ADDR,
+ .mask = ST_PRESS_1_BDU_MASK,
+ },
+ .drdy_irq = {
+ .addr = ST_PRESS_1_DRDY_IRQ_ADDR,
+ .mask = ST_PRESS_1_DRDY_IRQ_MASK,
+ },
+ .multi_read_bit = ST_PRESS_1_MULTIREAD_BIT,
+ .bootime = 2,
+ },
+};
+
+static int st_press_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *ch, int *val,
+ int *val2, long mask)
+{
+ int err;
+ struct st_sensor_data *pdata = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ err = st_sensors_read_info_raw(indio_dev, ch, val);
+ if (err < 0)
+ goto read_error;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+
+ switch (ch->type) {
+ case IIO_PRESSURE:
+ *val2 = pdata->current_fullscale->gain;
+ break;
+ case IIO_TEMP:
+ *val2 = pdata->current_fullscale->gain2;
+ break;
+ default:
+ err = -EINVAL;
+ goto read_error;
+ }
+
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ switch (ch->type) {
+ case IIO_TEMP:
+ *val = 425;
+ *val2 = 10;
+ break;
+ default:
+ err = -EINVAL;
+ goto read_error;
+ }
+
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+
+read_error:
+ return err;
+}
+
+static ST_SENSOR_DEV_ATTR_SAMP_FREQ();
+static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
+
+static struct attribute *st_press_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group st_press_attribute_group = {
+ .attrs = st_press_attributes,
+};
+
+static const struct iio_info press_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &st_press_attribute_group,
+ .read_raw = &st_press_read_raw,
+};
+
+#ifdef CONFIG_IIO_TRIGGER
+static const struct iio_trigger_ops st_press_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = ST_PRESS_TRIGGER_SET_STATE,
+};
+#define ST_PRESS_TRIGGER_OPS (&st_press_trigger_ops)
+#else
+#define ST_PRESS_TRIGGER_OPS NULL
+#endif
+
+int st_press_common_probe(struct iio_dev *indio_dev)
+{
+ int err;
+ struct st_sensor_data *pdata = iio_priv(indio_dev);
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &press_info;
+
+ err = st_sensors_check_device_support(indio_dev,
+ ARRAY_SIZE(st_press_sensors), st_press_sensors);
+ if (err < 0)
+ goto st_press_common_probe_error;
+
+ pdata->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS;
+ pdata->multiread_bit = pdata->sensor->multi_read_bit;
+ indio_dev->channels = pdata->sensor->ch;
+ indio_dev->num_channels = ARRAY_SIZE(st_press_channels);
+
+ pdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+ &pdata->sensor->fs.fs_avl[0];
+ pdata->odr = pdata->sensor->odr.odr_avl[0].hz;
+
+ err = st_sensors_init_sensor(indio_dev);
+ if (err < 0)
+ goto st_press_common_probe_error;
+
+ if (pdata->get_irq_data_ready(indio_dev) > 0) {
+ err = st_press_allocate_ring(indio_dev);
+ if (err < 0)
+ goto st_press_common_probe_error;
+
+ err = st_sensors_allocate_trigger(indio_dev,
+ ST_PRESS_TRIGGER_OPS);
+ if (err < 0)
+ goto st_press_probe_trigger_error;
+ }
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto st_press_device_register_error;
+
+ return err;
+
+st_press_device_register_error:
+ if (pdata->get_irq_data_ready(indio_dev) > 0)
+ st_sensors_deallocate_trigger(indio_dev);
+st_press_probe_trigger_error:
+ if (pdata->get_irq_data_ready(indio_dev) > 0)
+ st_press_deallocate_ring(indio_dev);
+st_press_common_probe_error:
+ return err;
+}
+EXPORT_SYMBOL(st_press_common_probe);
+
+void st_press_common_remove(struct iio_dev *indio_dev)
+{
+ struct st_sensor_data *pdata = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (pdata->get_irq_data_ready(indio_dev) > 0) {
+ st_sensors_deallocate_trigger(indio_dev);
+ st_press_deallocate_ring(indio_dev);
+ }
+ iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_press_common_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics pressures driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c
new file mode 100644
index 000000000000..7cebcc73bfb0
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure_i2c.c
@@ -0,0 +1,77 @@
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include <linux/iio/common/st_sensors_i2c.h>
+#include "st_pressure.h"
+
+static int st_press_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct st_sensor_data *pdata;
+ int err;
+
+ indio_dev = iio_device_alloc(sizeof(*pdata));
+ if (indio_dev == NULL) {
+ err = -ENOMEM;
+ goto iio_device_alloc_error;
+ }
+
+ pdata = iio_priv(indio_dev);
+ pdata->dev = &client->dev;
+
+ st_sensors_i2c_configure(indio_dev, client, pdata);
+
+ err = st_press_common_probe(indio_dev);
+ if (err < 0)
+ goto st_press_common_probe_error;
+
+ return 0;
+
+st_press_common_probe_error:
+ iio_device_free(indio_dev);
+iio_device_alloc_error:
+ return err;
+}
+
+static int st_press_i2c_remove(struct i2c_client *client)
+{
+ st_press_common_remove(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static const struct i2c_device_id st_press_id_table[] = {
+ { LPS331AP_PRESS_DEV_NAME },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, st_press_id_table);
+
+static struct i2c_driver st_press_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "st-press-i2c",
+ },
+ .probe = st_press_i2c_probe,
+ .remove = st_press_i2c_remove,
+ .id_table = st_press_id_table,
+};
+module_i2c_driver(st_press_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics pressures i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c
new file mode 100644
index 000000000000..17a14907940a
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure_spi.c
@@ -0,0 +1,76 @@
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include <linux/iio/common/st_sensors_spi.h>
+#include "st_pressure.h"
+
+static int st_press_spi_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct st_sensor_data *pdata;
+ int err;
+
+ indio_dev = iio_device_alloc(sizeof(*pdata));
+ if (indio_dev == NULL) {
+ err = -ENOMEM;
+ goto iio_device_alloc_error;
+ }
+
+ pdata = iio_priv(indio_dev);
+ pdata->dev = &spi->dev;
+
+ st_sensors_spi_configure(indio_dev, spi, pdata);
+
+ err = st_press_common_probe(indio_dev);
+ if (err < 0)
+ goto st_press_common_probe_error;
+
+ return 0;
+
+st_press_common_probe_error:
+ iio_device_free(indio_dev);
+iio_device_alloc_error:
+ return err;
+}
+
+static int st_press_spi_remove(struct spi_device *spi)
+{
+ st_press_common_remove(spi_get_drvdata(spi));
+
+ return 0;
+}
+
+static const struct spi_device_id st_press_id_table[] = {
+ { LPS331AP_PRESS_DEV_NAME },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, st_press_id_table);
+
+static struct spi_driver st_press_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "st-press-spi",
+ },
+ .probe = st_press_spi_probe,
+ .remove = st_press_spi_remove,
+ .id_table = st_press_id_table,
+};
+module_spi_driver(st_press_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics pressures spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
new file mode 100644
index 000000000000..360fd508b088
--- /dev/null
+++ b/drivers/iio/trigger/Kconfig
@@ -0,0 +1,26 @@
+#
+# Industrial I/O standalone triggers
+#
+menu "Triggers - standalone"
+
+config IIO_INTERRUPT_TRIGGER
+ tristate "Generic interrupt trigger"
+ help
+ Provides support for using an interrupt of any type as an IIO
+ trigger. This may be provided by a gpio driver for example.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iio-trig-interrupt.
+
+config IIO_SYSFS_TRIGGER
+ tristate "SYSFS trigger"
+ depends on SYSFS
+ select IRQ_WORK
+ help
+ Provides support for using SYSFS entry as IIO triggers.
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called iio-trig-sysfs.
+
+endmenu
diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
new file mode 100644
index 000000000000..ce319a51b6af
--- /dev/null
+++ b/drivers/iio/trigger/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for triggers not associated with iio-devices
+#
+
+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-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c
new file mode 100644
index 000000000000..02577ec54c6b
--- /dev/null
+++ b/drivers/iio/trigger/iio-trig-interrupt.c
@@ -0,0 +1,121 @@
+/*
+ * Industrial I/O - generic interrupt based trigger support
+ *
+ * Copyright (c) 2008-2013 Jonathan Cameron
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+
+struct iio_interrupt_trigger_info {
+ unsigned int irq;
+};
+
+static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private)
+{
+ /* Timestamp not currently provided */
+ iio_trigger_poll(private, 0);
+ return IRQ_HANDLED;
+}
+
+static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+static int iio_interrupt_trigger_probe(struct platform_device *pdev)
+{
+ struct iio_interrupt_trigger_info *trig_info;
+ struct iio_trigger *trig;
+ unsigned long irqflags;
+ struct resource *irq_res;
+ int irq, ret = 0;
+
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+ if (irq_res == NULL)
+ return -ENODEV;
+
+ irqflags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED;
+
+ irq = irq_res->start;
+
+ trig = iio_trigger_alloc("irqtrig%d", irq);
+ if (!trig) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
+ if (!trig_info) {
+ ret = -ENOMEM;
+ goto error_put_trigger;
+ }
+ iio_trigger_set_drvdata(trig, trig_info);
+ trig_info->irq = irq;
+ trig->ops = &iio_interrupt_trigger_ops;
+ ret = request_irq(irq, iio_interrupt_trigger_poll,
+ irqflags, trig->name, trig);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "request IRQ-%d failed", irq);
+ goto error_free_trig_info;
+ }
+
+ ret = iio_trigger_register(trig);
+ if (ret)
+ goto error_release_irq;
+ platform_set_drvdata(pdev, trig);
+
+ return 0;
+
+/* First clean up the partly allocated trigger */
+error_release_irq:
+ free_irq(irq, trig);
+error_free_trig_info:
+ kfree(trig_info);
+error_put_trigger:
+ iio_trigger_put(trig);
+error_ret:
+ return ret;
+}
+
+static int iio_interrupt_trigger_remove(struct platform_device *pdev)
+{
+ struct iio_trigger *trig;
+ struct iio_interrupt_trigger_info *trig_info;
+
+ trig = platform_get_drvdata(pdev);
+ trig_info = iio_trigger_get_drvdata(trig);
+ iio_trigger_unregister(trig);
+ free_irq(trig_info->irq, trig);
+ kfree(trig_info);
+ iio_trigger_put(trig);
+
+ return 0;
+}
+
+static struct platform_driver iio_interrupt_trigger_driver = {
+ .probe = iio_interrupt_trigger_probe,
+ .remove = iio_interrupt_trigger_remove,
+ .driver = {
+ .name = "iio_interrupt_trigger",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(iio_interrupt_trigger_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Interrupt trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c
new file mode 100644
index 000000000000..effcd0ac98d8
--- /dev/null
+++ b/drivers/iio/trigger/iio-trig-sysfs.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/irq_work.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+struct iio_sysfs_trig {
+ struct iio_trigger *trig;
+ struct irq_work work;
+ int id;
+ struct list_head l;
+};
+
+static LIST_HEAD(iio_sysfs_trig_list);
+static DEFINE_MUTEX(iio_syfs_trig_list_mut);
+
+static int iio_sysfs_trigger_probe(int id);
+static ssize_t iio_sysfs_trig_add(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ unsigned long input;
+
+ ret = kstrtoul(buf, 10, &input);
+ if (ret)
+ return ret;
+ ret = iio_sysfs_trigger_probe(input);
+ if (ret)
+ return ret;
+ return len;
+}
+static DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_trig_add);
+
+static int iio_sysfs_trigger_remove(int id);
+static ssize_t iio_sysfs_trig_remove(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ unsigned long input;
+
+ ret = kstrtoul(buf, 10, &input);
+ if (ret)
+ return ret;
+ ret = iio_sysfs_trigger_remove(input);
+ if (ret)
+ return ret;
+ return len;
+}
+
+static DEVICE_ATTR(remove_trigger, S_IWUSR, NULL, &iio_sysfs_trig_remove);
+
+static struct attribute *iio_sysfs_trig_attrs[] = {
+ &dev_attr_add_trigger.attr,
+ &dev_attr_remove_trigger.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_sysfs_trig_group = {
+ .attrs = iio_sysfs_trig_attrs,
+};
+
+static const struct attribute_group *iio_sysfs_trig_groups[] = {
+ &iio_sysfs_trig_group,
+ NULL
+};
+
+
+/* Nothing to actually do upon release */
+static void iio_trigger_sysfs_release(struct device *dev)
+{
+}
+
+static struct device iio_sysfs_trig_dev = {
+ .bus = &iio_bus_type,
+ .groups = iio_sysfs_trig_groups,
+ .release = &iio_trigger_sysfs_release,
+};
+
+static void iio_sysfs_trigger_work(struct irq_work *work)
+{
+ struct iio_sysfs_trig *trig = container_of(work, struct iio_sysfs_trig,
+ work);
+
+ iio_trigger_poll(trig->trig, 0);
+}
+
+static ssize_t iio_sysfs_trigger_poll(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct iio_sysfs_trig *sysfs_trig = iio_trigger_get_drvdata(trig);
+
+ irq_work_queue(&sysfs_trig->work);
+
+ return count;
+}
+
+static DEVICE_ATTR(trigger_now, S_IWUSR, NULL, iio_sysfs_trigger_poll);
+
+static struct attribute *iio_sysfs_trigger_attrs[] = {
+ &dev_attr_trigger_now.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_sysfs_trigger_attr_group = {
+ .attrs = iio_sysfs_trigger_attrs,
+};
+
+static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = {
+ &iio_sysfs_trigger_attr_group,
+ NULL
+};
+
+static const struct iio_trigger_ops iio_sysfs_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+static int iio_sysfs_trigger_probe(int id)
+{
+ struct iio_sysfs_trig *t;
+ int ret;
+ bool foundit = false;
+ mutex_lock(&iio_syfs_trig_list_mut);
+ list_for_each_entry(t, &iio_sysfs_trig_list, l)
+ if (id == t->id) {
+ foundit = true;
+ break;
+ }
+ if (foundit) {
+ ret = -EINVAL;
+ goto out1;
+ }
+ t = kmalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+ t->id = id;
+ t->trig = iio_trigger_alloc("sysfstrig%d", id);
+ if (!t->trig) {
+ ret = -ENOMEM;
+ goto free_t;
+ }
+
+ t->trig->dev.groups = iio_sysfs_trigger_attr_groups;
+ t->trig->ops = &iio_sysfs_trigger_ops;
+ t->trig->dev.parent = &iio_sysfs_trig_dev;
+ iio_trigger_set_drvdata(t->trig, t);
+
+ init_irq_work(&t->work, iio_sysfs_trigger_work);
+
+ ret = iio_trigger_register(t->trig);
+ if (ret)
+ goto out2;
+ list_add(&t->l, &iio_sysfs_trig_list);
+ __module_get(THIS_MODULE);
+ mutex_unlock(&iio_syfs_trig_list_mut);
+ return 0;
+
+out2:
+ iio_trigger_put(t->trig);
+free_t:
+ kfree(t);
+out1:
+ mutex_unlock(&iio_syfs_trig_list_mut);
+ return ret;
+}
+
+static int iio_sysfs_trigger_remove(int id)
+{
+ bool foundit = false;
+ struct iio_sysfs_trig *t;
+ mutex_lock(&iio_syfs_trig_list_mut);
+ list_for_each_entry(t, &iio_sysfs_trig_list, l)
+ if (id == t->id) {
+ foundit = true;
+ break;
+ }
+ if (!foundit) {
+ mutex_unlock(&iio_syfs_trig_list_mut);
+ return -EINVAL;
+ }
+
+ iio_trigger_unregister(t->trig);
+ iio_trigger_free(t->trig);
+
+ list_del(&t->l);
+ kfree(t);
+ module_put(THIS_MODULE);
+ mutex_unlock(&iio_syfs_trig_list_mut);
+ return 0;
+}
+
+
+static int __init iio_sysfs_trig_init(void)
+{
+ device_initialize(&iio_sysfs_trig_dev);
+ dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger");
+ return device_add(&iio_sysfs_trig_dev);
+}
+module_init(iio_sysfs_trig_init);
+
+static void __exit iio_sysfs_trig_exit(void)
+{
+ device_unregister(&iio_sysfs_trig_dev);
+}
+module_exit(iio_sysfs_trig_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:iio-trig-sysfs");