From e1562ef30438d4850e3f4ea0fc5711ea658a1f69 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Sat, 13 Oct 2012 09:06:00 +0100 Subject: iio: remove useless irq_enabled variable in at91 irq_enabled is only set, but never read Signed-off-by: Peter Meerwald Acked-by: Maxime Ripard Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 3ed94bf80596..b3ba8af4f261 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -46,7 +46,6 @@ struct at91_adc_state { struct clk *clk; bool done; int irq; - bool irq_enabled; u16 last_value; struct mutex lock; u8 num_channels; @@ -85,7 +84,6 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) buffer->access->store_to(buffer, (u8 *)st->buffer); iio_trigger_notify_done(idev->trig); - st->irq_enabled = true; /* Needed to ACK the DRDY interruption */ at91_adc_readl(st, AT91_ADC_LCDR); @@ -106,7 +104,6 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private) if (iio_buffer_enabled(idev)) { disable_irq_nosync(irq); - st->irq_enabled = false; iio_trigger_poll(idev->trig, iio_get_time_ns()); } else { st->last_value = at91_adc_readl(st, AT91_ADC_LCDR); -- cgit v1.2.3 From 1217c48f51704d01019581cb1baa1b9124eaa0a7 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Sat, 13 Oct 2012 09:06:00 +0100 Subject: iio: use iio_push_to_buffer() in at91 driver Signed-off-by: Peter Meerwald Acked-by: Maxime Ripard Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index b3ba8af4f261..2e2c9a80aa37 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -81,7 +81,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - buffer->access->store_to(buffer, (u8 *)st->buffer); + iio_push_to_buffer(buffer, st->buffer); iio_trigger_notify_done(idev->trig); -- cgit v1.2.3 From ace43fcefb1f7078cc19ab07df9d877eefffa57f Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Sat, 13 Oct 2012 09:06:00 +0100 Subject: iio: fix spelling of Accelerometer in Kconfig Signed-off-by: Peter Meerwald Acked-by: Srinivas pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index b2510c4d9a5a..fe4bcd7c5b12 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -8,7 +8,7 @@ config HID_SENSOR_ACCEL_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON - tristate "HID Acelerometers 3D" + tristate "HID Accelerometers 3D" help Say yes here to build support for the HID SENSOR accelerometers 3D. -- cgit v1.2.3 From 7737fa6d1ea3385428bd36a0dddcb33c526f7d74 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 18 Oct 2012 15:43:00 +0100 Subject: iio: Don't compare boolean values to true/false Fixes the following warnings from coccicheck: drivers/iio/inkern.c:81:6-14: WARNING: Comparison to bool drivers/iio/dac/ad5686.c:191:5-11: WARNING: Comparison to bool Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 2 +- drivers/iio/inkern.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 6948d75e1036..bc92ff9309c2 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -188,7 +188,7 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; - if (readin == true) + if (readin) st->pwr_down_mask |= (0x3 << (chan->channel * 2)); else st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index f2b78d4fe457..5230a33886c0 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -78,7 +78,7 @@ int iio_map_array_unregister(struct iio_dev *indio_dev, found_it = true; break; } - if (found_it == false) { + if (!found_it) { ret = -ENODEV; goto error_ret; } -- cgit v1.2.3 From 45259859492812c8b700ae1d157be01a8d2babfe Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 18 Oct 2012 15:43:00 +0100 Subject: iio: at91_adc: Use devm_kcalloc to allocate arrays Use dev_kcalloc instead of devm_kzalloc to allocate arrays since it is semantically more appropriate. While we are at it the patch also fixes the following coccinelle warning: drivers/iio/adc/at91_adc.c:277:25-31: ERROR: application of sizeof to pointer Signed-off-by: Lars-Peter Clausen Acked-By: Maxime Ripard Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 2e2c9a80aa37..a9176722042f 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -123,10 +123,8 @@ static int at91_adc_channel_init(struct iio_dev *idev) idev->num_channels = bitmap_weight(&st->channels_mask, st->num_channels) + 1; - chan_array = devm_kzalloc(&idev->dev, - ((idev->num_channels + 1) * - sizeof(struct iio_chan_spec)), - GFP_KERNEL); + chan_array = devm_kcalloc(&idev->dev, idev->num_channels + 1, + sizeof(*chan_array), GFP_KERNEL); if (!chan_array) return -ENOMEM; @@ -270,9 +268,8 @@ static int at91_adc_trigger_init(struct iio_dev *idev) struct at91_adc_state *st = iio_priv(idev); int i, ret; - st->trig = devm_kzalloc(&idev->dev, - st->trigger_number * sizeof(st->trig), - GFP_KERNEL); + st->trig = devm_kcalloc(&idev->dev, st->trigger_number, + sizeof(*st->trig), GFP_KERNEL); if (st->trig == NULL) { ret = -ENOMEM; @@ -454,9 +451,8 @@ static int at91_adc_probe_dt(struct at91_adc_state *st, st->registers->trigger_register = prop; st->trigger_number = of_get_child_count(node); - st->trigger_list = devm_kzalloc(&idev->dev, st->trigger_number * - sizeof(struct at91_adc_trigger), - GFP_KERNEL); + st->trigger_list = devm_kcalloc(&idev->dev, st->trigger_number, + sizeof(*st->trigger_list), GFP_KERNEL); if (!st->trigger_list) { dev_err(&idev->dev, "Could not allocate trigger list memory.\n"); ret = -ENOMEM; -- cgit v1.2.3 From 948ad20504894436c008c8a50f74e277edeff9a1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 18 Oct 2012 14:47:00 +0100 Subject: iio: Use strict_strtouint instead of kstrtoul strict_strto* has been deprecated in favor of kstrto*. Use strict_strtouint respective strict_strtoint, since that is what the functions we pass the converted values to expect. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 6 +++--- drivers/iio/industrialio-event.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index d4ad37455a67..722a83fd8d85 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -371,12 +371,12 @@ ssize_t iio_buffer_write_length(struct device *dev, const char *buf, size_t len) { - int ret; - ulong val; struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_buffer *buffer = indio_dev->buffer; + unsigned int val; + int ret; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtouint(buf, 10, &val); if (ret) return ret; diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index fa6543bf6731..857e6306c5c3 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -239,13 +239,13 @@ static ssize_t iio_ev_value_store(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - unsigned long val; + int val; int ret; if (!indio_dev->info->write_event_value) return -EINVAL; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtoint(buf, 10, &val); if (ret) return ret; -- cgit v1.2.3 From 103d9fb907058e4eb052f4f7302d1b07eb6a7792 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 16 Oct 2012 17:29:00 +0100 Subject: iio: Add a logarithmic fractional value type For ADCs or DACs the denominator for fractional types often is a power of two. In this case we can use a shift operation instead of the rather expensive 64 bit division. This patch adds a new fractional type which expects the denominator to be specified as the log2 of the actual denominator. E.g. for ADCs and DACs this will usually be the number of significant bits. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 5 +++++ drivers/iio/inkern.c | 3 +++ include/linux/iio/types.h | 1 + 3 files changed, 9 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 6eb24dbc081e..37650a72b31f 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -397,6 +397,11 @@ static ssize_t iio_read_channel_info(struct device *dev, val2 = do_div(tmp, 1000000000LL); val = tmp; return sprintf(buf, "%d.%09u\n", val, val2); + case IIO_VAL_FRACTIONAL_LOG2: + tmp = (s64)val * 1000000000LL >> val2; + val2 = do_div(tmp, 1000000000LL); + val = tmp; + return sprintf(buf, "%d.%09u\n", val, val2); default: return 0; } diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 5230a33886c0..b394621d362c 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -314,6 +314,9 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, *processed = div_s64(raw64 * (s64)scale_val * scale, scale_val2); break; + case IIO_VAL_FRACTIONAL_LOG2: + *processed = (raw64 * (s64)scale_val * scale) >> scale_val2; + break; default: return -EINVAL; } diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 5c647ecfd5ba..87b196a2d698 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -58,5 +58,6 @@ enum iio_modifier { #define IIO_VAL_INT_PLUS_NANO 3 #define IIO_VAL_INT_PLUS_MICRO_DB 4 #define IIO_VAL_FRACTIONAL 10 +#define IIO_VAL_FRACTIONAL_LOG2 11 #endif /* _IIO_TYPES_H_ */ -- cgit v1.2.3 From 8341dc04dfb33fbae71727ae648e20c51abc40e3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 16 Oct 2012 17:29:00 +0100 Subject: iio:dac: Add support for the ad5449 This patch adds support for the AD5415, AD5426, AD5429, AD5432, AD5439, AD5443 and AD5449 single and dual channel DACs. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 10 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad5449.c | 375 +++++++++++++++++++++++++++++++++++ include/linux/platform_data/ad5449.h | 40 ++++ 4 files changed, 426 insertions(+) create mode 100644 drivers/iio/dac/ad5449.c create mode 100644 include/linux/platform_data/ad5449.h (limited to 'drivers/iio') diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index b1c0ee5294ca..f68756e6dd62 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -67,6 +67,16 @@ config AD5446 To compile this driver as a module, choose M here: the module will be called ad5446. +config AD5449 + tristate "Analog Device AD5449 and similar DACs driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD5415, AD5426, AD5429, + AD5432, AD5439, AD5443, AD5449 Digital to Analog Converters. + + To compile this driver as a module, choose M here: the + module will be called ad5449. + config AD5504 tristate "Analog Devices AD5504/AD5501 DAC SPI driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index c0d333b23ba3..5b528ebb3343 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o obj-$(CONFIG_AD5064) += ad5064.o obj-$(CONFIG_AD5504) += ad5504.o obj-$(CONFIG_AD5446) += ad5446.o +obj-$(CONFIG_AD5449) += ad5449.o obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c new file mode 100644 index 000000000000..5b43030fe6e3 --- /dev/null +++ b/drivers/iio/dac/ad5449.c @@ -0,0 +1,375 @@ +/* + * AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog + * Converter driver. + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define AD5449_MAX_CHANNELS 2 +#define AD5449_MAX_VREFS 2 + +#define AD5449_CMD_NOOP 0x0 +#define AD5449_CMD_LOAD_AND_UPDATE(x) (0x1 + (x) * 3) +#define AD5449_CMD_READ(x) (0x2 + (x) * 3) +#define AD5449_CMD_LOAD(x) (0x3 + (x) * 3) +#define AD5449_CMD_CTRL 13 + +#define AD5449_CTRL_SDO_OFFSET 10 +#define AD5449_CTRL_DAISY_CHAIN BIT(9) +#define AD5449_CTRL_HCLR_TO_MIDSCALE BIT(8) +#define AD5449_CTRL_SAMPLE_RISING BIT(7) + +/** + * struct ad5449_chip_info - chip specific information + * @channels: Channel specification + * @num_channels: Number of channels + * @has_ctrl: Chip has a control register + */ +struct ad5449_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + bool has_ctrl; +}; + +/** + * struct ad5449 - driver instance specific data + * @spi: the SPI device for this driver instance + * @chip_info: chip model specific constants, available modes etc + * @vref_reg: vref supply regulators + * @has_sdo: whether the SDO line is connected + * @dac_cache: Cache for the DAC values + * @data: spi transfer buffers + */ +struct ad5449 { + struct spi_device *spi; + const struct ad5449_chip_info *chip_info; + struct regulator_bulk_data vref_reg[AD5449_MAX_VREFS]; + + bool has_sdo; + uint16_t dac_cache[AD5449_MAX_CHANNELS]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 data[2] ____cacheline_aligned; +}; + +enum ad5449_type { + ID_AD5426, + ID_AD5429, + ID_AD5432, + ID_AD5439, + ID_AD5443, + ID_AD5449, +}; + +static int ad5449_write(struct iio_dev *indio_dev, unsigned int addr, + unsigned int val) +{ + struct ad5449 *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&indio_dev->mlock); + st->data[0] = cpu_to_be16((addr << 12) | val); + ret = spi_write(st->spi, st->data, 2); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr, + unsigned int *val) +{ + struct ad5449 *st = iio_priv(indio_dev); + int ret; + struct spi_message msg; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0], + .len = 2, + .cs_change = 1, + }, { + .tx_buf = &st->data[1], + .rx_buf = &st->data[1], + .len = 2, + }, + }; + + spi_message_init(&msg); + spi_message_add_tail(&t[0], &msg); + spi_message_add_tail(&t[1], &msg); + + mutex_lock(&indio_dev->mlock); + st->data[0] = cpu_to_be16(addr << 12); + st->data[1] = cpu_to_be16(AD5449_CMD_NOOP); + + ret = spi_sync(st->spi, &msg); + if (ret < 0) + return ret; + + *val = be16_to_cpu(st->data[1]); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int ad5449_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct ad5449 *st = iio_priv(indio_dev); + struct regulator_bulk_data *reg; + int scale_uv; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (st->has_sdo) { + ret = ad5449_read(indio_dev, + AD5449_CMD_READ(chan->address), val); + if (ret) + return ret; + *val &= 0xfff; + } else { + *val = st->dac_cache[chan->address]; + } + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + reg = &st->vref_reg[chan->channel]; + scale_uv = regulator_get_voltage(reg->consumer); + if (scale_uv < 0) + return scale_uv; + + *val = scale_uv / 1000; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + + return -EINVAL; +} + +static int ad5449_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long info) +{ + struct ad5449 *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (val < 0 || val >= (1 << chan->scan_type.realbits)) + return -EINVAL; + + ret = ad5449_write(indio_dev, + AD5449_CMD_LOAD_AND_UPDATE(chan->address), + val << chan->scan_type.shift); + if (ret == 0) + st->dac_cache[chan->address] = val; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad5449_info = { + .read_raw = ad5449_read_raw, + .write_raw = ad5449_write_raw, + .driver_module = THIS_MODULE, +}; + +#define AD5449_CHANNEL(chan, bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .address = (chan), \ + .scan_type = IIO_ST('u', (bits), 16, 12 - (bits)), \ +} + +#define DECLARE_AD5449_CHANNELS(name, bits) \ +const struct iio_chan_spec name[] = { \ + AD5449_CHANNEL(0, bits), \ + AD5449_CHANNEL(1, bits), \ +} + +static DECLARE_AD5449_CHANNELS(ad5429_channels, 8); +static DECLARE_AD5449_CHANNELS(ad5439_channels, 10); +static DECLARE_AD5449_CHANNELS(ad5449_channels, 12); + +static const struct ad5449_chip_info ad5449_chip_info[] = { + [ID_AD5426] = { + .channels = ad5429_channels, + .num_channels = 1, + .has_ctrl = false, + }, + [ID_AD5429] = { + .channels = ad5429_channels, + .num_channels = 2, + .has_ctrl = true, + }, + [ID_AD5432] = { + .channels = ad5439_channels, + .num_channels = 1, + .has_ctrl = false, + }, + [ID_AD5439] = { + .channels = ad5439_channels, + .num_channels = 2, + .has_ctrl = true, + }, + [ID_AD5443] = { + .channels = ad5449_channels, + .num_channels = 1, + .has_ctrl = false, + }, + [ID_AD5449] = { + .channels = ad5449_channels, + .num_channels = 2, + .has_ctrl = true, + }, +}; + +static const char *ad5449_vref_name(struct ad5449 *st, int n) +{ + if (st->chip_info->num_channels == 1) + return "VREF"; + + if (n == 0) + return "VREFA"; + else + return "VREFB"; +} + +static int __devinit ad5449_spi_probe(struct spi_device *spi) +{ + struct ad5449_platform_data *pdata = spi->dev.platform_data; + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct ad5449 *st; + unsigned int i; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->chip_info = &ad5449_chip_info[id->driver_data]; + st->spi = spi; + + for (i = 0; i < st->chip_info->num_channels; ++i) + st->vref_reg[i].supply = ad5449_vref_name(st, i); + + ret = regulator_bulk_get(&spi->dev, st->chip_info->num_channels, + st->vref_reg); + if (ret) + goto error_free; + + ret = regulator_bulk_enable(st->chip_info->num_channels, st->vref_reg); + if (ret) + goto error_free_reg; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = id->name; + indio_dev->info = &ad5449_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + + if (st->chip_info->has_ctrl) { + unsigned int ctrl = 0x00; + if (pdata) { + if (pdata->hardware_clear_to_midscale) + ctrl |= AD5449_CTRL_HCLR_TO_MIDSCALE; + ctrl |= pdata->sdo_mode << AD5449_CTRL_SDO_OFFSET; + st->has_sdo = pdata->sdo_mode != AD5449_SDO_DISABLED; + } else { + st->has_sdo = true; + } + ad5449_write(indio_dev, AD5449_CMD_CTRL, ctrl); + } + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + return 0; + +error_disable_reg: + regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg); +error_free_reg: + regulator_bulk_free(st->chip_info->num_channels, st->vref_reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5449_spi_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5449 *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg); + regulator_bulk_free(st->chip_info->num_channels, st->vref_reg); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5449_spi_ids[] = { + { "ad5415", ID_AD5449 }, + { "ad5426", ID_AD5426 }, + { "ad5429", ID_AD5429 }, + { "ad5432", ID_AD5432 }, + { "ad5439", ID_AD5439 }, + { "ad5443", ID_AD5443 }, + { "ad5449", ID_AD5449 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5449_spi_ids); + +static struct spi_driver ad5449_spi_driver = { + .driver = { + .name = "ad5449", + .owner = THIS_MODULE, + }, + .probe = ad5449_spi_probe, + .remove = __devexit_p(ad5449_spi_remove), + .id_table = ad5449_spi_ids, +}; +module_spi_driver(ad5449_spi_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5449 and similar DACs"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/ad5449.h b/include/linux/platform_data/ad5449.h new file mode 100644 index 000000000000..bd712bd4b94e --- /dev/null +++ b/include/linux/platform_data/ad5449.h @@ -0,0 +1,40 @@ +/* + * AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog + * Converter driver. + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#ifndef __LINUX_PLATFORM_DATA_AD5449_H__ +#define __LINUX_PLATFORM_DATA_AD5449_H__ + +/** + * enum ad5449_sdo_mode - AD5449 SDO pin configuration + * @AD5449_SDO_DRIVE_FULL: Drive the SDO pin with full strength. + * @AD5449_SDO_DRIVE_WEAK: Drive the SDO pin with not full strength. + * @AD5449_SDO_OPEN_DRAIN: Operate the SDO pin in open-drain mode. + * @AD5449_SDO_DISABLED: Disable the SDO pin, in this mode it is not possible to + * read back from the device. + */ +enum ad5449_sdo_mode { + AD5449_SDO_DRIVE_FULL = 0x0, + AD5449_SDO_DRIVE_WEAK = 0x1, + AD5449_SDO_OPEN_DRAIN = 0x2, + AD5449_SDO_DISABLED = 0x3, +}; + +/** + * struct ad5449_platform_data - Platform data for the ad5449 DAC driver + * @sdo_mode: SDO pin mode + * @hardware_clear_to_midscale: Whether asserting the hardware CLR pin sets the + * outputs to midscale (true) or to zero scale(false). + */ +struct ad5449_platform_data { + enum ad5449_sdo_mode sdo_mode; + bool hardware_clear_to_midscale; +}; + +#endif -- cgit v1.2.3 From 369130ab9233207d7036ee4578d92a867b150c9c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 23 Oct 2012 13:02:06 -0700 Subject: drivers/iio/dac: remove CONFIG_EXPERIMENTAL This config item has not carried much meaning for a while now and is almost always enabled by default. As agreed during the Linux kernel summit, remove it. CC: Jonathan Cameron CC: Lars-Peter Clausen CC: Greg Kroah-Hartman CC: Michael Hennerich CC: Peter Meerwald Signed-off-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index f68756e6dd62..f4a6f0838327 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -132,7 +132,7 @@ config AD5686 config MAX517 tristate "Maxim MAX517/518/519 DAC driver" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Maxim chips MAX517, MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs). -- cgit v1.2.3 From 6b3aa3131a7804640e7589e5629036f660362f0b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 29 Oct 2012 08:25:00 +0000 Subject: Revert "iio: at91_adc: Use devm_kcalloc to allocate arrays" This reverts commit 45259859492812c8b700ae1d157be01a8d2babfe. This fixes build error because devm_kcalloc does not exist in current tree. Signed-off-by: Axel Lin Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index a9176722042f..2e2c9a80aa37 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -123,8 +123,10 @@ static int at91_adc_channel_init(struct iio_dev *idev) idev->num_channels = bitmap_weight(&st->channels_mask, st->num_channels) + 1; - chan_array = devm_kcalloc(&idev->dev, idev->num_channels + 1, - sizeof(*chan_array), GFP_KERNEL); + chan_array = devm_kzalloc(&idev->dev, + ((idev->num_channels + 1) * + sizeof(struct iio_chan_spec)), + GFP_KERNEL); if (!chan_array) return -ENOMEM; @@ -268,8 +270,9 @@ static int at91_adc_trigger_init(struct iio_dev *idev) struct at91_adc_state *st = iio_priv(idev); int i, ret; - st->trig = devm_kcalloc(&idev->dev, st->trigger_number, - sizeof(*st->trig), GFP_KERNEL); + st->trig = devm_kzalloc(&idev->dev, + st->trigger_number * sizeof(st->trig), + GFP_KERNEL); if (st->trig == NULL) { ret = -ENOMEM; @@ -451,8 +454,9 @@ static int at91_adc_probe_dt(struct at91_adc_state *st, st->registers->trigger_register = prop; st->trigger_number = of_get_child_count(node); - st->trigger_list = devm_kcalloc(&idev->dev, st->trigger_number, - sizeof(*st->trigger_list), GFP_KERNEL); + st->trigger_list = devm_kzalloc(&idev->dev, st->trigger_number * + sizeof(struct at91_adc_trigger), + GFP_KERNEL); if (!st->trigger_list) { dev_err(&idev->dev, "Could not allocate trigger list memory.\n"); ret = -ENOMEM; -- cgit v1.2.3 From 0c88d23c66884e6ac2995d4d7f881b41e0cfd27c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 24 Oct 2012 08:13:00 +0100 Subject: iio:dac:ad5449: unlock on error path There is an unlock missing on this error path. Signed-off-by: Dan Carpenter Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5449.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index 5b43030fe6e3..0ee6f8eeba8d 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -124,12 +124,13 @@ static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr, ret = spi_sync(st->spi, &msg); if (ret < 0) - return ret; + goto out_unlock; *val = be16_to_cpu(st->data[1]); - mutex_unlock(&indio_dev->mlock); - return 0; +out_unlock: + mutex_unlock(&indio_dev->mlock); + return ret; } static int ad5449_read_raw(struct iio_dev *indio_dev, -- cgit v1.2.3 From 8f1b7eb11422d28fbc7432f0dee2f97aead09417 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 22 Oct 2012 12:15:00 +0100 Subject: iio: Do not accept multiple '.' in fixed point numbers Currently when parsing a fix-point number we silently skip any additional '.' found in the string. E.g. '1.2.3.4' gets parsed as '1.234'. This patch disallows this and returns an error if more than one '.' is encountered. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 37650a72b31f..0994239361ba 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -454,7 +454,7 @@ static ssize_t iio_write_channel_info(struct device *dev, break; else return -EINVAL; - } else if (*buf == '.') { + } else if (*buf == '.' && integer_part) { integer_part = false; } else { return -EINVAL; -- cgit v1.2.3 From 02330acda7ef88865e62e2763df2cdd735607cb4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 22 Oct 2012 12:15:00 +0100 Subject: iio: Reject trailing garbage when parsing fixed point numbers When parsing a fixed point number IIO stops parsing the string once it has reached the last requested decimal place. This means that the remainder of the string is silently accepted regardless, of whether it is part of a valid number or not. This patch modifies the code to scan the whole string and only accept valid numbers. Since fract_mult is 0 after the last decimal place any digit that may follows won't affect the result. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 0994239361ba..7bb570b7a4af 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -445,8 +445,6 @@ static ssize_t iio_write_channel_info(struct device *dev, integer = integer*10 + *buf - '0'; else { fract += fract_mult*(*buf - '0'); - if (fract_mult == 1) - break; fract_mult /= 10; } } else if (*buf == '\n') { -- cgit v1.2.3 From ef4f92c064697a66a2a61977dd690af40dc01ff0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 22 Oct 2012 12:15:00 +0100 Subject: iio: Accept a leading '+' sign when parsing fixed point numbers If we encounter a leading '+' sign just skip over it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 7bb570b7a4af..cd700368eed0 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -437,6 +437,8 @@ static ssize_t iio_write_channel_info(struct device *dev, if (buf[0] == '-') { negative = true; buf++; + } else if (buf[0] == '+') { + buf++; } while (*buf) { -- cgit v1.2.3 From 4eb3ccf157639a9d9c7829de94017c46c73d9cc4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 5 Nov 2012 09:56:00 +0000 Subject: staging:iio: Move the ad7887 driver out of staging The driver does not expose any custom API to userspace and none of the standard static code checker tools report any issues, so move it out of staging. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 13 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7887.c | 378 +++++++++++++++++++++++++++++++++++ drivers/staging/iio/adc/Kconfig | 13 -- drivers/staging/iio/adc/Makefile | 2 - drivers/staging/iio/adc/ad7887.c | 378 ----------------------------------- drivers/staging/iio/adc/ad7887.h | 31 --- include/linux/platform_data/ad7887.h | 26 +++ 8 files changed, 418 insertions(+), 424 deletions(-) create mode 100644 drivers/iio/adc/ad7887.c delete mode 100644 drivers/staging/iio/adc/ad7887.c delete mode 100644 drivers/staging/iio/adc/ad7887.h create mode 100644 include/linux/platform_data/ad7887.h (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 492758120338..706386ba02e3 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -45,6 +45,19 @@ config AD7476 To compile this driver as a module, choose M here: the module will be called ad7476. +config AD7887 + tristate "Analog Devices AD7887 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices + AD7887 SPI analog to digital converter (ADC). + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7887. + config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 900995d5e179..034eacb8f7c9 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -6,5 +6,6 @@ obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AD7476) += ad7476.o obj-$(CONFIG_AD7791) += ad7791.o +obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c new file mode 100644 index 000000000000..fd62309b4d3d --- /dev/null +++ b/drivers/iio/adc/ad7887.c @@ -0,0 +1,378 @@ +/* + * AD7887 SPI ADC driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#define AD7887_REF_DIS (1 << 5) /* on-chip reference disable */ +#define AD7887_DUAL (1 << 4) /* dual-channel mode */ +#define AD7887_CH_AIN1 (1 << 3) /* convert on channel 1, DUAL=1 */ +#define AD7887_CH_AIN0 (0 << 3) /* convert on channel 0, DUAL=0,1 */ +#define AD7887_PM_MODE1 (0) /* CS based shutdown */ +#define AD7887_PM_MODE2 (1) /* full on */ +#define AD7887_PM_MODE3 (2) /* auto shutdown after conversion */ +#define AD7887_PM_MODE4 (3) /* standby mode */ + +enum ad7887_channels { + AD7887_CH0, + AD7887_CH0_CH1, + AD7887_CH1, +}; + +#define RES_MASK(bits) ((1 << (bits)) - 1) + +/** + * struct ad7887_chip_info - chip specifc information + * @int_vref_mv: the internal reference voltage + * @channel: channel specification + */ +struct ad7887_chip_info { + u16 int_vref_mv; + struct iio_chan_spec channel[3]; +}; + +struct ad7887_state { + struct spi_device *spi; + const struct ad7887_chip_info *chip_info; + struct regulator *reg; + struct spi_transfer xfer[4]; + struct spi_message msg[3]; + struct spi_message *ring_msg; + unsigned char tx_cmd_buf[4]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * Buffer needs to be large enough to hold two 16 bit samples and a + * 64 bit aligned 64 bit timestamp. + */ + unsigned char data[ALIGN(4, sizeof(s64)) + sizeof(s64)] + ____cacheline_aligned; +}; + +enum ad7887_supported_device_ids { + ID_AD7887 +}; + +static int ad7887_ring_preenable(struct iio_dev *indio_dev) +{ + struct ad7887_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_sw_buffer_preenable(indio_dev); + if (ret < 0) + return ret; + + /* We know this is a single long so can 'cheat' */ + switch (*indio_dev->active_scan_mask) { + case (1 << 0): + st->ring_msg = &st->msg[AD7887_CH0]; + break; + case (1 << 1): + st->ring_msg = &st->msg[AD7887_CH1]; + /* Dummy read: push CH1 setting down to hardware */ + spi_sync(st->spi, st->ring_msg); + break; + case ((1 << 1) | (1 << 0)): + st->ring_msg = &st->msg[AD7887_CH0_CH1]; + break; + } + + return 0; +} + +static int ad7887_ring_postdisable(struct iio_dev *indio_dev) +{ + struct ad7887_state *st = iio_priv(indio_dev); + + /* dummy read: restore default CH0 settin */ + return spi_sync(st->spi, &st->msg[AD7887_CH0]); +} + +/** + * ad7887_trigger_handler() bh of trigger launched polling to ring buffer + * + * Currently there is no option in this driver to disable the saving of + * timestamps within the ring. + **/ +static irqreturn_t ad7887_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7887_state *st = iio_priv(indio_dev); + s64 time_ns; + int b_sent; + + b_sent = spi_sync(st->spi, st->ring_msg); + if (b_sent) + goto done; + + time_ns = iio_get_time_ns(); + + if (indio_dev->scan_timestamp) + memcpy(st->data + indio_dev->scan_bytes - sizeof(s64), + &time_ns, sizeof(time_ns)); + + iio_push_to_buffer(indio_dev->buffer, st->data); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops ad7887_ring_setup_ops = { + .preenable = &ad7887_ring_preenable, + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, + .postdisable = &ad7887_ring_postdisable, +}; + +static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch) +{ + int ret = spi_sync(st->spi, &st->msg[ch]); + if (ret) + return ret; + + return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1]; +} + +static int ad7887_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7887_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) + ret = -EBUSY; + else + ret = ad7887_scan_direct(st, chan->address); + mutex_unlock(&indio_dev->mlock); + + if (ret < 0) + return ret; + *val = ret >> chan->scan_type.shift; + *val &= RES_MASK(chan->scan_type.realbits); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (st->reg) { + *val = regulator_get_voltage(st->reg); + if (*val < 0) + return *val; + *val /= 1000; + } else { + *val = st->chip_info->int_vref_mv; + } + + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + + +static const struct ad7887_chip_info ad7887_chip_info_tbl[] = { + /* + * More devices added in future + */ + [ID_AD7887] = { + .channel[0] = { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 1, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .address = 1, + .scan_index = 1, + .scan_type = IIO_ST('u', 12, 16, 0), + }, + .channel[1] = { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .address = 0, + .scan_index = 0, + .scan_type = IIO_ST('u', 12, 16, 0), + }, + .channel[2] = IIO_CHAN_SOFT_TIMESTAMP(2), + .int_vref_mv = 2500, + }, +}; + +static const struct iio_info ad7887_info = { + .read_raw = &ad7887_read_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad7887_probe(struct spi_device *spi) +{ + struct ad7887_platform_data *pdata = spi->dev.platform_data; + struct ad7887_state *st; + struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st)); + uint8_t mode; + int ret; + + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + if (!pdata || !pdata->use_onchip_ref) { + st->reg = regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto error_free; + } + + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + } + + st->chip_info = + &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + + /* Estabilish that the iio_dev is a child of the spi device */ + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad7887_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + /* Setup default message */ + + mode = AD7887_PM_MODE4; + if (!pdata || !pdata->use_onchip_ref) + mode |= AD7887_REF_DIS; + if (pdata && pdata->en_dual) + mode |= AD7887_DUAL; + + st->tx_cmd_buf[0] = AD7887_CH_AIN0 | mode; + + st->xfer[0].rx_buf = &st->data[0]; + st->xfer[0].tx_buf = &st->tx_cmd_buf[0]; + st->xfer[0].len = 2; + + spi_message_init(&st->msg[AD7887_CH0]); + spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]); + + if (pdata && pdata->en_dual) { + st->tx_cmd_buf[2] = AD7887_CH_AIN1 | mode; + + st->xfer[1].rx_buf = &st->data[0]; + st->xfer[1].tx_buf = &st->tx_cmd_buf[2]; + st->xfer[1].len = 2; + + st->xfer[2].rx_buf = &st->data[2]; + st->xfer[2].tx_buf = &st->tx_cmd_buf[0]; + st->xfer[2].len = 2; + + spi_message_init(&st->msg[AD7887_CH0_CH1]); + spi_message_add_tail(&st->xfer[1], &st->msg[AD7887_CH0_CH1]); + spi_message_add_tail(&st->xfer[2], &st->msg[AD7887_CH0_CH1]); + + st->xfer[3].rx_buf = &st->data[2]; + st->xfer[3].tx_buf = &st->tx_cmd_buf[2]; + st->xfer[3].len = 2; + + spi_message_init(&st->msg[AD7887_CH1]); + spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]); + + indio_dev->channels = st->chip_info->channel; + indio_dev->num_channels = 3; + } else { + indio_dev->channels = &st->chip_info->channel[1]; + indio_dev->num_channels = 2; + } + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &ad7887_trigger_handler, &ad7887_ring_setup_ops); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unregister_ring; + + return 0; +error_unregister_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + if (st->reg) + regulator_disable(st->reg); +error_put_reg: + if (st->reg) + regulator_put(st->reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad7887_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7887_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (st->reg) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7887_id[] = { + {"ad7887", ID_AD7887}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7887_id); + +static struct spi_driver ad7887_driver = { + .driver = { + .name = "ad7887", + .owner = THIS_MODULE, + }, + .probe = ad7887_probe, + .remove = __devexit_p(ad7887_remove), + .id_table = ad7887_id, +}; +module_spi_driver(ad7887_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7887 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 71a515d0a6de..eba64fb64d82 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -68,19 +68,6 @@ config AD799X_RING_BUFFER Say yes here to include ring buffer support in the AD799X ADC driver. -config AD7887 - tristate "Analog Devices AD7887 ADC driver" - depends on SPI - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Say yes here to build support for Analog Devices - AD7887 SPI analog to digital converter (ADC). - If unsure, say N (but it's safe to say "Y"). - - To compile this driver as a module, choose M here: the - module will be called ad7887. - config AD7780 tristate "Analog Devices AD7780 and similar ADCs driver" depends on SPI diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 8036fd14f68a..c56b41ee285b 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -17,8 +17,6 @@ ad799x-y := ad799x_core.o ad799x-$(CONFIG_AD799X_RING_BUFFER) += ad799x_ring.o obj-$(CONFIG_AD799X) += ad799x.o -obj-$(CONFIG_AD7887) += ad7887.o - ad7298-y := ad7298_core.o ad7298-$(CONFIG_IIO_BUFFER) += ad7298_ring.o obj-$(CONFIG_AD7298) += ad7298.o diff --git a/drivers/staging/iio/adc/ad7887.c b/drivers/staging/iio/adc/ad7887.c deleted file mode 100644 index 72cfe191cd83..000000000000 --- a/drivers/staging/iio/adc/ad7887.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * AD7887 SPI ADC driver - * - * Copyright 2010-2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "ad7887.h" - -#define AD7887_REF_DIS (1 << 5) /* on-chip reference disable */ -#define AD7887_DUAL (1 << 4) /* dual-channel mode */ -#define AD7887_CH_AIN1 (1 << 3) /* convert on channel 1, DUAL=1 */ -#define AD7887_CH_AIN0 (0 << 3) /* convert on channel 0, DUAL=0,1 */ -#define AD7887_PM_MODE1 (0) /* CS based shutdown */ -#define AD7887_PM_MODE2 (1) /* full on */ -#define AD7887_PM_MODE3 (2) /* auto shutdown after conversion */ -#define AD7887_PM_MODE4 (3) /* standby mode */ - -enum ad7887_channels { - AD7887_CH0, - AD7887_CH0_CH1, - AD7887_CH1, -}; - -#define RES_MASK(bits) ((1 << (bits)) - 1) - -/** - * struct ad7887_chip_info - chip specifc information - * @int_vref_mv: the internal reference voltage - * @channel: channel specification - */ -struct ad7887_chip_info { - u16 int_vref_mv; - struct iio_chan_spec channel[3]; -}; - -struct ad7887_state { - struct spi_device *spi; - const struct ad7887_chip_info *chip_info; - struct regulator *reg; - struct spi_transfer xfer[4]; - struct spi_message msg[3]; - struct spi_message *ring_msg; - unsigned char tx_cmd_buf[4]; - - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - * Buffer needs to be large enough to hold two 16 bit samples and a - * 64 bit aligned 64 bit timestamp. - */ - unsigned char data[ALIGN(4, sizeof(s64)) + sizeof(s64)] - ____cacheline_aligned; -}; - -enum ad7887_supported_device_ids { - ID_AD7887 -}; - -static int ad7887_ring_preenable(struct iio_dev *indio_dev) -{ - struct ad7887_state *st = iio_priv(indio_dev); - int ret; - - ret = iio_sw_buffer_preenable(indio_dev); - if (ret < 0) - return ret; - - /* We know this is a single long so can 'cheat' */ - switch (*indio_dev->active_scan_mask) { - case (1 << 0): - st->ring_msg = &st->msg[AD7887_CH0]; - break; - case (1 << 1): - st->ring_msg = &st->msg[AD7887_CH1]; - /* Dummy read: push CH1 setting down to hardware */ - spi_sync(st->spi, st->ring_msg); - break; - case ((1 << 1) | (1 << 0)): - st->ring_msg = &st->msg[AD7887_CH0_CH1]; - break; - } - - return 0; -} - -static int ad7887_ring_postdisable(struct iio_dev *indio_dev) -{ - struct ad7887_state *st = iio_priv(indio_dev); - - /* dummy read: restore default CH0 settin */ - return spi_sync(st->spi, &st->msg[AD7887_CH0]); -} - -/** - * ad7887_trigger_handler() bh of trigger launched polling to ring buffer - * - * Currently there is no option in this driver to disable the saving of - * timestamps within the ring. - **/ -static irqreturn_t ad7887_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct ad7887_state *st = iio_priv(indio_dev); - s64 time_ns; - int b_sent; - - b_sent = spi_sync(st->spi, st->ring_msg); - if (b_sent) - goto done; - - time_ns = iio_get_time_ns(); - - if (indio_dev->scan_timestamp) - memcpy(st->data + indio_dev->scan_bytes - sizeof(s64), - &time_ns, sizeof(time_ns)); - - iio_push_to_buffer(indio_dev->buffer, st->data); -done: - iio_trigger_notify_done(indio_dev->trig); - - return IRQ_HANDLED; -} - -static const struct iio_buffer_setup_ops ad7887_ring_setup_ops = { - .preenable = &ad7887_ring_preenable, - .postenable = &iio_triggered_buffer_postenable, - .predisable = &iio_triggered_buffer_predisable, - .postdisable = &ad7887_ring_postdisable, -}; - -static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch) -{ - int ret = spi_sync(st->spi, &st->msg[ch]); - if (ret) - return ret; - - return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1]; -} - -static int ad7887_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - int ret; - struct ad7887_state *st = iio_priv(indio_dev); - - switch (m) { - case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = ad7887_scan_direct(st, chan->address); - mutex_unlock(&indio_dev->mlock); - - if (ret < 0) - return ret; - *val = ret >> chan->scan_type.shift; - *val &= RES_MASK(chan->scan_type.realbits); - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - if (st->reg) { - *val = regulator_get_voltage(st->reg); - if (*val < 0) - return *val; - *val /= 1000; - } else { - *val = st->chip_info->int_vref_mv; - } - - *val2 = chan->scan_type.realbits; - - return IIO_VAL_FRACTIONAL_LOG2; - } - return -EINVAL; -} - - -static const struct ad7887_chip_info ad7887_chip_info_tbl[] = { - /* - * More devices added in future - */ - [ID_AD7887] = { - .channel[0] = { - .type = IIO_VOLTAGE, - .indexed = 1, - .channel = 1, - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | - IIO_CHAN_INFO_SCALE_SHARED_BIT, - .address = 1, - .scan_index = 1, - .scan_type = IIO_ST('u', 12, 16, 0), - }, - .channel[1] = { - .type = IIO_VOLTAGE, - .indexed = 1, - .channel = 0, - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | - IIO_CHAN_INFO_SCALE_SHARED_BIT, - .address = 0, - .scan_index = 0, - .scan_type = IIO_ST('u', 12, 16, 0), - }, - .channel[2] = IIO_CHAN_SOFT_TIMESTAMP(2), - .int_vref_mv = 2500, - }, -}; - -static const struct iio_info ad7887_info = { - .read_raw = &ad7887_read_raw, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad7887_probe(struct spi_device *spi) -{ - struct ad7887_platform_data *pdata = spi->dev.platform_data; - struct ad7887_state *st; - struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st)); - uint8_t mode; - int ret; - - if (indio_dev == NULL) - return -ENOMEM; - - st = iio_priv(indio_dev); - - if (!pdata || !pdata->use_onchip_ref) { - st->reg = regulator_get(&spi->dev, "vref"); - if (IS_ERR(st->reg)) { - ret = PTR_ERR(st->reg); - goto error_free; - } - - ret = regulator_enable(st->reg); - if (ret) - goto error_put_reg; - } - - st->chip_info = - &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - - spi_set_drvdata(spi, indio_dev); - st->spi = spi; - - /* Estabilish that the iio_dev is a child of the spi device */ - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad7887_info; - indio_dev->modes = INDIO_DIRECT_MODE; - - /* Setup default message */ - - mode = AD7887_PM_MODE4; - if (!pdata || !pdata->use_onchip_ref) - mode |= AD7887_REF_DIS; - if (pdata && pdata->en_dual) - mode |= AD7887_DUAL; - - st->tx_cmd_buf[0] = AD7887_CH_AIN0 | mode; - - st->xfer[0].rx_buf = &st->data[0]; - st->xfer[0].tx_buf = &st->tx_cmd_buf[0]; - st->xfer[0].len = 2; - - spi_message_init(&st->msg[AD7887_CH0]); - spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]); - - if (pdata && pdata->en_dual) { - st->tx_cmd_buf[2] = AD7887_CH_AIN1 | mode; - - st->xfer[1].rx_buf = &st->data[0]; - st->xfer[1].tx_buf = &st->tx_cmd_buf[2]; - st->xfer[1].len = 2; - - st->xfer[2].rx_buf = &st->data[2]; - st->xfer[2].tx_buf = &st->tx_cmd_buf[0]; - st->xfer[2].len = 2; - - spi_message_init(&st->msg[AD7887_CH0_CH1]); - spi_message_add_tail(&st->xfer[1], &st->msg[AD7887_CH0_CH1]); - spi_message_add_tail(&st->xfer[2], &st->msg[AD7887_CH0_CH1]); - - st->xfer[3].rx_buf = &st->data[2]; - st->xfer[3].tx_buf = &st->tx_cmd_buf[2]; - st->xfer[3].len = 2; - - spi_message_init(&st->msg[AD7887_CH1]); - spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]); - - indio_dev->channels = st->chip_info->channel; - indio_dev->num_channels = 3; - } else { - indio_dev->channels = &st->chip_info->channel[1]; - indio_dev->num_channels = 2; - } - - ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, - &ad7887_trigger_handler, &ad7887_ring_setup_ops); - if (ret) - goto error_disable_reg; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_unregister_ring; - - return 0; -error_unregister_ring: - iio_triggered_buffer_cleanup(indio_dev); -error_disable_reg: - if (st->reg) - regulator_disable(st->reg); -error_put_reg: - if (st->reg) - regulator_put(st->reg); -error_free: - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad7887_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7887_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - if (st->reg) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad7887_id[] = { - {"ad7887", ID_AD7887}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad7887_id); - -static struct spi_driver ad7887_driver = { - .driver = { - .name = "ad7887", - .owner = THIS_MODULE, - }, - .probe = ad7887_probe, - .remove = __devexit_p(ad7887_remove), - .id_table = ad7887_id, -}; -module_spi_driver(ad7887_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD7887 ADC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7887.h b/drivers/staging/iio/adc/ad7887.h deleted file mode 100644 index 16c2d05e5e05..000000000000 --- a/drivers/staging/iio/adc/ad7887.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * AD7887 SPI ADC driver - * - * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ -#ifndef IIO_ADC_AD7887_H_ -#define IIO_ADC_AD7887_H_ - -/* - * TODO: struct ad7887_platform_data needs to go into include/linux/iio - */ - - -/** - * struct ad7887_platform_data - AD7887 ADC driver platform data - * @en_dual: Whether to use dual channel mode. If set to true AIN1 becomes the - * second input channel, and Vref is internally connected to Vdd. If set to - * false the device is used in single channel mode and AIN1/Vref is used as - * VREF input. - * @use_onchip_ref: Whether to use the onchip reference. If set to true the - * internal 2.5V reference is used. If set to false a external reference is - * used. - */ -struct ad7887_platform_data { - bool en_dual; - bool use_onchip_ref; -}; - -#endif /* IIO_ADC_AD7887_H_ */ diff --git a/include/linux/platform_data/ad7887.h b/include/linux/platform_data/ad7887.h new file mode 100644 index 000000000000..1e06eac3174d --- /dev/null +++ b/include/linux/platform_data/ad7887.h @@ -0,0 +1,26 @@ +/* + * AD7887 SPI ADC driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ +#ifndef IIO_ADC_AD7887_H_ +#define IIO_ADC_AD7887_H_ + +/** + * struct ad7887_platform_data - AD7887 ADC driver platform data + * @en_dual: Whether to use dual channel mode. If set to true AIN1 becomes the + * second input channel, and Vref is internally connected to Vdd. If set to + * false the device is used in single channel mode and AIN1/Vref is used as + * VREF input. + * @use_onchip_ref: Whether to use the onchip reference. If set to true the + * internal 2.5V reference is used. If set to false a external reference is + * used. + */ +struct ad7887_platform_data { + bool en_dual; + bool use_onchip_ref; +}; + +#endif /* IIO_ADC_AD7887_H_ */ -- cgit v1.2.3 From 5e53a69b44e893227b046a7bc74db3cb40d7f39b Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Tue, 16 Oct 2012 12:55:45 +0530 Subject: IIO : ADC: tiadc: Add support of TI's ADC driver This patch adds support for TI's ADC driver. This is a multifunctional device. Analog input lines are provided on which voltage measurements can be carried out. You can have upto 8 input lines. Signed-off-by: Patil, Rachna Acked-by: Jonathan Cameron Signed-off-by: Samuel Ortiz --- drivers/iio/adc/Kconfig | 7 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti_am335x_adc.c | 260 ++++++++++++++++++++++++++++ drivers/mfd/ti_am335x_tscadc.c | 18 +- include/linux/mfd/ti_am335x_tscadc.h | 9 +- include/linux/platform_data/ti_am335x_adc.h | 14 ++ 6 files changed, 307 insertions(+), 2 deletions(-) create mode 100644 drivers/iio/adc/ti_am335x_adc.c create mode 100644 include/linux/platform_data/ti_am335x_adc.h (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 492758120338..1401ed1af39f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -60,4 +60,11 @@ config LP8788_ADC help Say yes here to build support for TI LP8788 ADC. +config TI_AM335X_ADC + tristate "TI's ADC driver" + depends on MFD_TI_AM335X_TSCADC + help + Say yes here to build support for Texas Instruments ADC + driver which is also a MFD client. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 900995d5e179..4410a90fc84b 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_AD7476) += ad7476.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o +obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c new file mode 100644 index 000000000000..02a43c87a8a3 --- /dev/null +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -0,0 +1,260 @@ +/* + * TI ADC MFD driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct tiadc_device { + struct ti_tscadc_dev *mfd_tscadc; + int channels; +}; + +static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg) +{ + return readl(adc->mfd_tscadc->tscadc_base + reg); +} + +static void tiadc_writel(struct tiadc_device *adc, unsigned int reg, + unsigned int val) +{ + writel(val, adc->mfd_tscadc->tscadc_base + reg); +} + +static void tiadc_step_config(struct tiadc_device *adc_dev) +{ + unsigned int stepconfig; + int i, channels = 0, steps; + + /* + * There are 16 configurable steps and 8 analog input + * lines available which are shared between Touchscreen and ADC. + * + * Steps backwards i.e. from 16 towards 0 are used by ADC + * depending on number of input lines needed. + * Channel would represent which analog input + * needs to be given to ADC to digitalize data. + */ + + steps = TOTAL_STEPS - adc_dev->channels; + channels = TOTAL_CHANNELS - adc_dev->channels; + + stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1; + + for (i = (steps + 1); i <= TOTAL_STEPS; i++) { + tiadc_writel(adc_dev, REG_STEPCONFIG(i), + stepconfig | STEPCONFIG_INP(channels)); + tiadc_writel(adc_dev, REG_STEPDELAY(i), + STEPCONFIG_OPENDLY); + channels++; + } + tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB); +} + +static int tiadc_channel_init(struct iio_dev *indio_dev, int channels) +{ + struct iio_chan_spec *chan_array; + int i; + + indio_dev->num_channels = channels; + chan_array = kcalloc(indio_dev->num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + + if (chan_array == NULL) + return -ENOMEM; + + for (i = 0; i < (indio_dev->num_channels); i++) { + struct iio_chan_spec *chan = chan_array + i; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = i; + chan->info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT; + } + + indio_dev->channels = chan_array; + + return indio_dev->num_channels; +} + +static void tiadc_channels_remove(struct iio_dev *indio_dev) +{ + kfree(indio_dev->channels); +} + +static int tiadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + int i; + unsigned int fifo1count, readx1; + + /* + * When the sub-system is first enabled, + * the sequencer will always start with the + * lowest step (1) and continue until step (16). + * For ex: If we have enabled 4 ADC channels and + * currently use only 1 out of them, the + * sequencer still configures all the 4 steps, + * leading to 3 unwanted data. + * Hence we need to flush out this data. + */ + + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + for (i = 0; i < fifo1count; i++) { + readx1 = tiadc_readl(adc_dev, REG_FIFO1); + if (i == chan->channel) + *val = readx1 & 0xfff; + } + tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB); + + return IIO_VAL_INT; +} + +static const struct iio_info tiadc_info = { + .read_raw = &tiadc_read_raw, +}; + +static int __devinit tiadc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct tiadc_device *adc_dev; + struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; + struct mfd_tscadc_board *pdata; + int err; + + pdata = tscadc_dev->dev->platform_data; + if (!pdata || !pdata->adc_init) { + dev_err(&pdev->dev, "Could not find platform data\n"); + return -EINVAL; + } + + indio_dev = iio_device_alloc(sizeof(struct tiadc_device)); + if (indio_dev == NULL) { + dev_err(&pdev->dev, "failed to allocate iio device\n"); + err = -ENOMEM; + goto err_ret; + } + adc_dev = iio_priv(indio_dev); + + adc_dev->mfd_tscadc = tscadc_dev; + adc_dev->channels = pdata->adc_init->adc_channels; + + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = dev_name(&pdev->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &tiadc_info; + + tiadc_step_config(adc_dev); + + err = tiadc_channel_init(indio_dev, adc_dev->channels); + if (err < 0) + goto err_free_device; + + err = iio_device_register(indio_dev); + if (err) + goto err_free_channels; + + platform_set_drvdata(pdev, indio_dev); + + return 0; + +err_free_channels: + tiadc_channels_remove(indio_dev); +err_free_device: + iio_device_free(indio_dev); +err_ret: + return err; +} + +static int __devexit tiadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + tiadc_channels_remove(indio_dev); + + iio_device_free(indio_dev); + + return 0; +} + +#ifdef CONFIG_PM +static int tiadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct ti_tscadc_dev *tscadc_dev = dev->platform_data; + unsigned int idle; + + if (!device_may_wakeup(tscadc_dev->dev)) { + idle = tiadc_readl(adc_dev, REG_CTRL); + idle &= ~(CNTRLREG_TSCSSENB); + tiadc_writel(adc_dev, REG_CTRL, (idle | + CNTRLREG_POWERDOWN)); + } + + return 0; +} + +static int tiadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + unsigned int restore; + + /* Make sure ADC is powered up */ + restore = tiadc_readl(adc_dev, REG_CTRL); + restore &= ~(CNTRLREG_POWERDOWN); + tiadc_writel(adc_dev, REG_CTRL, restore); + + tiadc_step_config(adc_dev); + + return 0; +} + +static const struct dev_pm_ops tiadc_pm_ops = { + .suspend = tiadc_suspend, + .resume = tiadc_resume, +}; +#define TIADC_PM_OPS (&tiadc_pm_ops) +#else +#define TIADC_PM_OPS NULL +#endif + +static struct platform_driver tiadc_driver = { + .driver = { + .name = "tiadc", + .owner = THIS_MODULE, + .pm = TIADC_PM_OPS, + }, + .probe = tiadc_probe, + .remove = __devexit_p(tiadc_remove), +}; + +module_platform_driver(tiadc_driver); + +MODULE_DESCRIPTION("TI ADC controller driver"); +MODULE_AUTHOR("Rachna Patil "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index d812be4b61df..e947dd8bbcc3 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -25,6 +25,7 @@ #include #include +#include static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg) { @@ -67,14 +68,23 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) int irq; int err, ctrl; int clk_value, clock_rate; - int tsc_wires; + int tsc_wires, adc_channels = 0, total_channels; if (!pdata) { dev_err(&pdev->dev, "Could not find platform data\n"); return -EINVAL; } + if (pdata->adc_init) + adc_channels = pdata->adc_init->adc_channels; + tsc_wires = pdata->tsc_init->wires; + total_channels = tsc_wires + adc_channels; + + if (total_channels > 8) { + dev_err(&pdev->dev, "Number of i/p channels more than 8\n"); + return -EINVAL; + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -172,6 +182,12 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev) cell->platform_data = tscadc; cell->pdata_size = sizeof(*tscadc); + /* ADC Cell */ + cell = &tscadc->cells[ADC_CELL]; + cell->name = "tiadc"; + cell->platform_data = tscadc; + cell->pdata_size = sizeof(*tscadc); + err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, TSCADC_CELLS, NULL, 0, NULL); if (err < 0) diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index fc18b2ef753f..c79ad5d2f271 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -120,15 +120,19 @@ #define ADC_CLK 3000000 #define MAX_CLK_DIV 7 +#define TOTAL_STEPS 16 +#define TOTAL_CHANNELS 8 -#define TSCADC_CELLS 1 +#define TSCADC_CELLS 2 enum tscadc_cells { TSC_CELL, + ADC_CELL, }; struct mfd_tscadc_board { struct tsc_data *tsc_init; + struct adc_data *adc_init; }; struct ti_tscadc_dev { @@ -140,6 +144,9 @@ struct ti_tscadc_dev { /* tsc device */ struct titsc *tsc; + + /* adc device */ + struct adc_device *adc; }; #endif diff --git a/include/linux/platform_data/ti_am335x_adc.h b/include/linux/platform_data/ti_am335x_adc.h new file mode 100644 index 000000000000..e41d5834cb84 --- /dev/null +++ b/include/linux/platform_data/ti_am335x_adc.h @@ -0,0 +1,14 @@ +#ifndef __LINUX_TI_AM335X_ADC_H +#define __LINUX_TI_AM335X_ADC_H + +/** + * struct adc_data ADC Input information + * @adc_channels: Number of analog inputs + * available for ADC. + */ + +struct adc_data { + unsigned int adc_channels; +}; + +#endif -- cgit v1.2.3 From 84b36ce5f79c01f792c623f14e92ed86cdccb42f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 30 Jun 2012 20:06:00 +0100 Subject: staging:iio: Add support for multiple buffers Route all buffer writes through the demux. Addition or removal of a buffer results in tear down and setup of all the buffers for a given device. Signed-off-by: Jonathan Cameron Tested-by: srinivas pandruvada --- drivers/iio/accel/hid-sensor-accel-3d.c | 15 +- drivers/iio/adc/ad7266.c | 3 +- drivers/iio/adc/ad7476.c | 2 +- drivers/iio/adc/ad7887.c | 2 +- drivers/iio/adc/ad_sigma_delta.c | 2 +- drivers/iio/adc/at91_adc.c | 3 +- drivers/iio/gyro/hid-sensor-gyro-3d.c | 15 +- drivers/iio/industrialio-buffer.c | 380 ++++++++++++++++-------- drivers/iio/industrialio-core.c | 1 + drivers/iio/light/adjd_s311.c | 3 +- drivers/iio/light/hid-sensor-als.c | 15 +- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 15 +- drivers/staging/iio/accel/adis16201_ring.c | 2 +- drivers/staging/iio/accel/adis16203_ring.c | 2 +- drivers/staging/iio/accel/adis16204_ring.c | 2 +- drivers/staging/iio/accel/adis16209_ring.c | 2 +- drivers/staging/iio/accel/adis16240_ring.c | 2 +- drivers/staging/iio/accel/lis3l02dq_ring.c | 2 +- drivers/staging/iio/adc/ad7298_ring.c | 2 +- drivers/staging/iio/adc/ad7606_ring.c | 2 +- drivers/staging/iio/adc/ad799x_ring.c | 2 +- drivers/staging/iio/adc/max1363_ring.c | 2 +- drivers/staging/iio/adc/mxs-lradc.c | 3 +- drivers/staging/iio/gyro/adis16260_ring.c | 2 +- drivers/staging/iio/iio_simple_dummy_buffer.c | 5 +- drivers/staging/iio/impedance-analyzer/ad5933.c | 4 +- drivers/staging/iio/imu/adis16400_ring.c | 5 +- drivers/staging/iio/meter/ade7758_ring.c | 2 +- include/linux/iio/buffer.h | 24 +- include/linux/iio/iio.h | 2 + 30 files changed, 313 insertions(+), 210 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 314a4057879e..a95cda0e387f 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -197,21 +197,8 @@ static const struct iio_info accel_3d_info = { /* Function to push data to buffer */ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { - struct iio_buffer *buffer = indio_dev->buffer; - int datum_sz; - dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - if (!buffer) { - dev_err(&indio_dev->dev, "Buffer == NULL\n"); - return; - } - datum_sz = buffer->access->get_bytes_per_datum(buffer); - if (len > datum_sz) { - dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, - datum_sz); - return; - } - iio_push_to_buffer(buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index b11f214779a2..a6f4fc5f8201 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -91,7 +91,6 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - struct iio_buffer *buffer = indio_dev->buffer; struct ad7266_state *st = iio_priv(indio_dev); int ret; @@ -99,7 +98,7 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p) if (ret == 0) { if (indio_dev->scan_timestamp) ((s64 *)st->data)[1] = pf->timestamp; - iio_push_to_buffer(buffer, (u8 *)st->data); + iio_push_to_buffers(indio_dev, (u8 *)st->data); } iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 7f2f45a0a48d..330248bfebae 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -76,7 +76,7 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) ((s64 *)st->data)[1] = time_ns; - iio_push_to_buffer(indio_dev->buffer, st->data); + iio_push_to_buffers(indio_dev, st->data); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index fd62309b4d3d..81153fafac7a 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -134,7 +134,7 @@ static irqreturn_t ad7887_trigger_handler(int irq, void *p) memcpy(st->data + indio_dev->scan_bytes - sizeof(s64), &time_ns, sizeof(time_ns)); - iio_push_to_buffer(indio_dev->buffer, st->data); + iio_push_to_buffers(indio_dev, st->data); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 67baa1363d7a..afe6d78c8ff0 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -391,7 +391,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) break; } - iio_push_to_buffer(indio_dev->buffer, (uint8_t *)data); + iio_push_to_buffers(indio_dev, (uint8_t *)data); iio_trigger_notify_done(indio_dev->trig); sigma_delta->irq_dis = false; diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 2e2c9a80aa37..03b85940f4ba 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -65,7 +65,6 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *idev = pf->indio_dev; struct at91_adc_state *st = iio_priv(idev); - struct iio_buffer *buffer = idev->buffer; int i, j = 0; for (i = 0; i < idev->masklength; i++) { @@ -81,7 +80,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - iio_push_to_buffer(buffer, st->buffer); + iio_push_to_buffers(indio_dev, (u8 *)st->buffer); iio_trigger_notify_done(idev->trig); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 4c56ada51c39..02ef989b830d 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -197,21 +197,8 @@ static const struct iio_info gyro_3d_info = { /* Function to push data to buffer */ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { - struct iio_buffer *buffer = indio_dev->buffer; - int datum_sz; - dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - if (!buffer) { - dev_err(&indio_dev->dev, "Buffer == NULL\n"); - return; - } - datum_sz = buffer->access->get_bytes_per_datum(buffer); - if (len > datum_sz) { - dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, - datum_sz); - return; - } - iio_push_to_buffer(buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 722a83fd8d85..aaadd32f9f0d 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -31,6 +31,18 @@ static const char * const iio_endian_prefix[] = { [IIO_LE] = "le", }; +static bool iio_buffer_is_active(struct iio_dev *indio_dev, + struct iio_buffer *buf) +{ + struct list_head *p; + + list_for_each(p, &indio_dev->buffer_list) + if (p == &buf->buffer_list) + return true; + + return false; +} + /** * iio_buffer_read_first_n_outer() - chrdev read for buffer access * @@ -134,7 +146,7 @@ static ssize_t iio_scan_el_store(struct device *dev, if (ret < 0) return ret; mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { + if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) { ret = -EBUSY; goto error_ret; } @@ -180,12 +192,11 @@ static ssize_t iio_scan_el_ts_store(struct device *dev, return ret; mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { + if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) { ret = -EBUSY; goto error_ret; } indio_dev->buffer->scan_timestamp = state; - indio_dev->scan_timestamp = state; error_ret: mutex_unlock(&indio_dev->mlock); @@ -385,7 +396,7 @@ ssize_t iio_buffer_write_length(struct device *dev, return len; mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { + if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) { ret = -EBUSY; } else { if (buffer->access->set_length) @@ -398,102 +409,14 @@ ssize_t iio_buffer_write_length(struct device *dev, } EXPORT_SYMBOL(iio_buffer_write_length); -ssize_t iio_buffer_store_enable(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - int ret; - bool requested_state, current_state; - int previous_mode; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; - - mutex_lock(&indio_dev->mlock); - previous_mode = indio_dev->currentmode; - requested_state = !(buf[0] == '0'); - current_state = iio_buffer_enabled(indio_dev); - if (current_state == requested_state) { - printk(KERN_INFO "iio-buffer, current state requested again\n"); - goto done; - } - if (requested_state) { - if (indio_dev->setup_ops->preenable) { - ret = indio_dev->setup_ops->preenable(indio_dev); - if (ret) { - printk(KERN_ERR - "Buffer not started: " - "buffer preenable failed\n"); - goto error_ret; - } - } - if (buffer->access->request_update) { - ret = buffer->access->request_update(buffer); - if (ret) { - printk(KERN_INFO - "Buffer not started: " - "buffer parameter update failed\n"); - goto error_ret; - } - } - /* Definitely possible for devices to support both of these. */ - if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) { - if (!indio_dev->trig) { - printk(KERN_INFO - "Buffer not started: no trigger\n"); - ret = -EINVAL; - goto error_ret; - } - indio_dev->currentmode = INDIO_BUFFER_TRIGGERED; - } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) - indio_dev->currentmode = INDIO_BUFFER_HARDWARE; - else { /* should never be reached */ - ret = -EINVAL; - goto error_ret; - } - - if (indio_dev->setup_ops->postenable) { - ret = indio_dev->setup_ops->postenable(indio_dev); - if (ret) { - printk(KERN_INFO - "Buffer not started: " - "postenable failed\n"); - indio_dev->currentmode = previous_mode; - if (indio_dev->setup_ops->postdisable) - indio_dev->setup_ops-> - postdisable(indio_dev); - goto error_ret; - } - } - } else { - if (indio_dev->setup_ops->predisable) { - ret = indio_dev->setup_ops->predisable(indio_dev); - if (ret) - goto error_ret; - } - indio_dev->currentmode = INDIO_DIRECT_MODE; - if (indio_dev->setup_ops->postdisable) { - ret = indio_dev->setup_ops->postdisable(indio_dev); - if (ret) - goto error_ret; - } - } -done: - mutex_unlock(&indio_dev->mlock); - return len; - -error_ret: - mutex_unlock(&indio_dev->mlock); - return ret; -} -EXPORT_SYMBOL(iio_buffer_store_enable); - ssize_t iio_buffer_show_enable(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); - return sprintf(buf, "%d\n", iio_buffer_enabled(indio_dev)); + return sprintf(buf, "%d\n", + iio_buffer_is_active(indio_dev, + indio_dev->buffer)); } EXPORT_SYMBOL(iio_buffer_show_enable); @@ -537,35 +460,220 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask, return bytes; } -int iio_sw_buffer_preenable(struct iio_dev *indio_dev) +int iio_update_buffers(struct iio_dev *indio_dev, + struct iio_buffer *insert_buffer, + struct iio_buffer *remove_buffer) { - struct iio_buffer *buffer = indio_dev->buffer; - dev_dbg(&indio_dev->dev, "%s\n", __func__); + int ret; + int success = 0; + struct iio_buffer *buffer; + unsigned long *compound_mask; + const unsigned long *old_mask; - /* How much space will the demuxed element take? */ - indio_dev->scan_bytes = - iio_compute_scan_bytes(indio_dev, buffer->scan_mask, - buffer->scan_timestamp); - buffer->access->set_bytes_per_datum(buffer, indio_dev->scan_bytes); + /* Wind down existing buffers - iff there are any */ + if (!list_empty(&indio_dev->buffer_list)) { + if (indio_dev->setup_ops->predisable) { + ret = indio_dev->setup_ops->predisable(indio_dev); + if (ret) + goto error_ret; + } + indio_dev->currentmode = INDIO_DIRECT_MODE; + if (indio_dev->setup_ops->postdisable) { + ret = indio_dev->setup_ops->postdisable(indio_dev); + if (ret) + goto error_ret; + } + } + /* Keep a copy of current setup to allow roll back */ + old_mask = indio_dev->active_scan_mask; + if (!indio_dev->available_scan_masks) + indio_dev->active_scan_mask = NULL; + + if (remove_buffer) + list_del(&remove_buffer->buffer_list); + if (insert_buffer) + list_add(&insert_buffer->buffer_list, &indio_dev->buffer_list); + + /* If no buffers in list, we are done */ + if (list_empty(&indio_dev->buffer_list)) { + indio_dev->currentmode = INDIO_DIRECT_MODE; + if (indio_dev->available_scan_masks == NULL) + kfree(old_mask); + return 0; + } /* What scan mask do we actually have ?*/ - if (indio_dev->available_scan_masks) + compound_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), + sizeof(long), GFP_KERNEL); + if (compound_mask == NULL) { + if (indio_dev->available_scan_masks == NULL) + kfree(old_mask); + return -ENOMEM; + } + indio_dev->scan_timestamp = 0; + + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { + bitmap_or(compound_mask, compound_mask, buffer->scan_mask, + indio_dev->masklength); + indio_dev->scan_timestamp |= buffer->scan_timestamp; + } + if (indio_dev->available_scan_masks) { indio_dev->active_scan_mask = iio_scan_mask_match(indio_dev->available_scan_masks, indio_dev->masklength, - buffer->scan_mask); - else - indio_dev->active_scan_mask = buffer->scan_mask; - - if (indio_dev->active_scan_mask == NULL) - return -EINVAL; + compound_mask); + if (indio_dev->active_scan_mask == NULL) { + /* + * Roll back. + * Note can only occur when adding a buffer. + */ + list_del(&insert_buffer->buffer_list); + indio_dev->active_scan_mask = old_mask; + success = -EINVAL; + } + } else { + indio_dev->active_scan_mask = compound_mask; + } iio_update_demux(indio_dev); - if (indio_dev->info->update_scan_mode) - return indio_dev->info + /* Wind up again */ + if (indio_dev->setup_ops->preenable) { + ret = indio_dev->setup_ops->preenable(indio_dev); + if (ret) { + printk(KERN_ERR + "Buffer not started:" + "buffer preenable failed\n"); + goto error_remove_inserted; + } + } + indio_dev->scan_bytes = + iio_compute_scan_bytes(indio_dev, + indio_dev->active_scan_mask, + indio_dev->scan_timestamp); + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) + if (buffer->access->request_update) { + ret = buffer->access->request_update(buffer); + if (ret) { + printk(KERN_INFO + "Buffer not started:" + "buffer parameter update failed\n"); + goto error_run_postdisable; + } + } + if (indio_dev->info->update_scan_mode) { + ret = indio_dev->info ->update_scan_mode(indio_dev, indio_dev->active_scan_mask); + if (ret < 0) { + printk(KERN_INFO "update scan mode failed\n"); + goto error_run_postdisable; + } + } + /* Definitely possible for devices to support both of these.*/ + if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) { + if (!indio_dev->trig) { + printk(KERN_INFO "Buffer not started: no trigger\n"); + ret = -EINVAL; + /* Can only occur on first buffer */ + goto error_run_postdisable; + } + indio_dev->currentmode = INDIO_BUFFER_TRIGGERED; + } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) { + indio_dev->currentmode = INDIO_BUFFER_HARDWARE; + } else { /* should never be reached */ + ret = -EINVAL; + goto error_run_postdisable; + } + + if (indio_dev->setup_ops->postenable) { + ret = indio_dev->setup_ops->postenable(indio_dev); + if (ret) { + printk(KERN_INFO + "Buffer not started: postenable failed\n"); + indio_dev->currentmode = INDIO_DIRECT_MODE; + if (indio_dev->setup_ops->postdisable) + indio_dev->setup_ops->postdisable(indio_dev); + goto error_disable_all_buffers; + } + } + + if (indio_dev->available_scan_masks) + kfree(compound_mask); + else + kfree(old_mask); + + return success; + +error_disable_all_buffers: + indio_dev->currentmode = INDIO_DIRECT_MODE; +error_run_postdisable: + if (indio_dev->setup_ops->postdisable) + indio_dev->setup_ops->postdisable(indio_dev); +error_remove_inserted: + + if (insert_buffer) + list_del(&insert_buffer->buffer_list); + indio_dev->active_scan_mask = old_mask; + kfree(compound_mask); +error_ret: + + return ret; +} +EXPORT_SYMBOL_GPL(iio_update_buffers); + +ssize_t iio_buffer_store_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + int ret; + bool requested_state; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_buffer *pbuf = indio_dev->buffer; + bool inlist; + + ret = strtobool(buf, &requested_state); + if (ret < 0) + return ret; + + mutex_lock(&indio_dev->mlock); + + /* Find out if it is in the list */ + inlist = iio_buffer_is_active(indio_dev, pbuf); + /* Already in desired state */ + if (inlist == requested_state) + goto done; + + if (requested_state) + ret = iio_update_buffers(indio_dev, + indio_dev->buffer, NULL); + else + ret = iio_update_buffers(indio_dev, + NULL, indio_dev->buffer); + + if (ret < 0) + goto done; +done: + mutex_unlock(&indio_dev->mlock); + return (ret < 0) ? ret : len; +} +EXPORT_SYMBOL(iio_buffer_store_enable); + +int iio_sw_buffer_preenable(struct iio_dev *indio_dev) +{ + struct iio_buffer *buffer; + unsigned bytes; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) + if (buffer->access->set_bytes_per_datum) { + bytes = iio_compute_scan_bytes(indio_dev, + buffer->scan_mask, + buffer->scan_timestamp); + + buffer->access->set_bytes_per_datum(buffer, bytes); + } return 0; } EXPORT_SYMBOL(iio_sw_buffer_preenable); @@ -599,7 +707,11 @@ static bool iio_validate_scan_mask(struct iio_dev *indio_dev, * iio_scan_mask_set() - set particular bit in the scan mask * @buffer: the buffer whose scan mask we are interested in * @bit: the bit to be set. - **/ + * + * Note that at this point we have no way of knowing what other + * buffers might request, hence this code only verifies that the + * individual buffers request is plausible. + */ int iio_scan_mask_set(struct iio_dev *indio_dev, struct iio_buffer *buffer, int bit) { @@ -682,13 +794,12 @@ static unsigned char *iio_demux(struct iio_buffer *buffer, return buffer->demux_bounce; } -int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data) +static int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data) { unsigned char *dataout = iio_demux(buffer, data); return buffer->access->store_to(buffer, dataout); } -EXPORT_SYMBOL_GPL(iio_push_to_buffer); static void iio_buffer_demux_free(struct iio_buffer *buffer) { @@ -699,10 +810,26 @@ static void iio_buffer_demux_free(struct iio_buffer *buffer) } } -int iio_update_demux(struct iio_dev *indio_dev) + +int iio_push_to_buffers(struct iio_dev *indio_dev, unsigned char *data) +{ + int ret; + struct iio_buffer *buf; + + list_for_each_entry(buf, &indio_dev->buffer_list, buffer_list) { + ret = iio_push_to_buffer(buf, data); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(iio_push_to_buffers); + +static int iio_buffer_update_demux(struct iio_dev *indio_dev, + struct iio_buffer *buffer) { const struct iio_chan_spec *ch; - struct iio_buffer *buffer = indio_dev->buffer; int ret, in_ind = -1, out_ind, length; unsigned in_loc = 0, out_loc = 0; struct iio_demux_table *p; @@ -787,4 +914,23 @@ error_clear_mux_table: return ret; } + +int iio_update_demux(struct iio_dev *indio_dev) +{ + struct iio_buffer *buffer; + int ret; + + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { + ret = iio_buffer_update_demux(indio_dev, buffer); + if (ret < 0) + goto error_clear_mux_table; + } + return 0; + +error_clear_mux_table: + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) + iio_buffer_demux_free(buffer); + + return ret; +} EXPORT_SYMBOL_GPL(iio_update_demux); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index cd700368eed0..060a4045be85 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -856,6 +856,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv) return NULL; } dev_set_name(&dev->dev, "iio:device%d", dev->id); + INIT_LIST_HEAD(&dev->buffer_list); } return dev; diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 164b62b91a4b..36d210a06b28 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -164,7 +164,6 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adjd_s311_data *data = iio_priv(indio_dev); - struct iio_buffer *buffer = indio_dev->buffer; s64 time_ns = iio_get_time_ns(); int len = 0; int i, j = 0; @@ -187,7 +186,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64))) = time_ns; - iio_push_to_buffer(buffer, (u8 *)data->buffer); + iio_push_to_buffers(indio_dev, (u8 *)data->buffer); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 96e3691e42c4..8e1f69844eea 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -176,21 +176,8 @@ static const struct iio_info als_info = { /* Function to push data to buffer */ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { - struct iio_buffer *buffer = indio_dev->buffer; - int datum_sz; - dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - if (!buffer) { - dev_err(&indio_dev->dev, "Buffer == NULL\n"); - return; - } - datum_sz = buffer->access->get_bytes_per_datum(buffer); - if (len > datum_sz) { - dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, - datum_sz); - return; - } - iio_push_to_buffer(buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index c4f0d274f577..d1b5fb74b9bf 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -198,21 +198,8 @@ static const struct iio_info magn_3d_info = { /* Function to push data to buffer */ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { - struct iio_buffer *buffer = indio_dev->buffer; - int datum_sz; - dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - if (!buffer) { - dev_err(&indio_dev->dev, "Buffer == NULL\n"); - return; - } - datum_sz = buffer->access->get_bytes_per_datum(buffer); - if (len > datum_sz) { - dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, - datum_sz); - return; - } - iio_push_to_buffer(buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ diff --git a/drivers/staging/iio/accel/adis16201_ring.c b/drivers/staging/iio/accel/adis16201_ring.c index 97c09f0c26ae..e14ca60092a2 100644 --- a/drivers/staging/iio/accel/adis16201_ring.c +++ b/drivers/staging/iio/accel/adis16201_ring.c @@ -82,7 +82,7 @@ static irqreturn_t adis16201_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16203_ring.c b/drivers/staging/iio/accel/adis16203_ring.c index 7507e1a04591..eba2e285c84a 100644 --- a/drivers/staging/iio/accel/adis16203_ring.c +++ b/drivers/staging/iio/accel/adis16203_ring.c @@ -81,7 +81,7 @@ static irqreturn_t adis16203_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16204_ring.c b/drivers/staging/iio/accel/adis16204_ring.c index 4c976bec986b..3611a13836c9 100644 --- a/drivers/staging/iio/accel/adis16204_ring.c +++ b/drivers/staging/iio/accel/adis16204_ring.c @@ -78,7 +78,7 @@ static irqreturn_t adis16204_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16209_ring.c b/drivers/staging/iio/accel/adis16209_ring.c index f939e29d6c82..6af9a5dbc709 100644 --- a/drivers/staging/iio/accel/adis16209_ring.c +++ b/drivers/staging/iio/accel/adis16209_ring.c @@ -78,7 +78,7 @@ static irqreturn_t adis16209_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/staging/iio/accel/adis16240_ring.c index caff8e25e0a2..e2ac8a8c8107 100644 --- a/drivers/staging/iio/accel/adis16240_ring.c +++ b/drivers/staging/iio/accel/adis16240_ring.c @@ -76,7 +76,7 @@ static irqreturn_t adis16240_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 246352716537..bc38651c315e 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -154,7 +154,7 @@ static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/adc/ad7298_ring.c b/drivers/staging/iio/adc/ad7298_ring.c index c2906a85fedb..b3dd514b9627 100644 --- a/drivers/staging/iio/adc/ad7298_ring.c +++ b/drivers/staging/iio/adc/ad7298_ring.c @@ -93,7 +93,7 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p) indio_dev->masklength); i++) buf[i] = be16_to_cpu(st->rx_buf[i]); - iio_push_to_buffer(indio_dev->buffer, (u8 *)buf); + iio_push_to_buffers(indio_dev, (u8 *)buf); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c index ba04d0ffd4f4..2b25cb07fe41 100644 --- a/drivers/staging/iio/adc/ad7606_ring.c +++ b/drivers/staging/iio/adc/ad7606_ring.c @@ -83,7 +83,7 @@ static void ad7606_poll_bh_to_ring(struct work_struct *work_s) if (indio_dev->scan_timestamp) *((s64 *)(buf + indio_dev->scan_bytes - sizeof(s64))) = time_ns; - iio_push_to_buffer(indio_dev->buffer, buf); + iio_push_to_buffers(indio_dev, buf); done: gpio_set_value(st->pdata->gpio_convst, 0); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/adc/ad799x_ring.c b/drivers/staging/iio/adc/ad799x_ring.c index 86026d9b20bc..2c5f38475a8e 100644 --- a/drivers/staging/iio/adc/ad799x_ring.c +++ b/drivers/staging/iio/adc/ad799x_ring.c @@ -77,7 +77,7 @@ static irqreturn_t ad799x_trigger_handler(int irq, void *p) memcpy(rxbuf + indio_dev->scan_bytes - sizeof(s64), &time_ns, sizeof(time_ns)); - iio_push_to_buffer(indio_dev->buffer, rxbuf); + iio_push_to_buffers(indio_dev, rxbuf); done: kfree(rxbuf); out: diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index 5f74f3b7671a..688304bdbaf0 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -80,7 +80,7 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); - iio_push_to_buffer(indio_dev->buffer, rxbuf); + iio_push_to_buffers(indio_dev, rxbuf); done_free: kfree(rxbuf); diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index df5bba284b73..3b467d8c5882 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -237,7 +237,6 @@ static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *iio = pf->indio_dev; struct mxs_lradc *lradc = iio_priv(iio); - struct iio_buffer *buffer = iio->buffer; const uint32_t chan_value = LRADC_CH_ACCUMULATE | ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); int i, j = 0; @@ -256,7 +255,7 @@ static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - iio_push_to_buffer(buffer, (u8 *)lradc->buffer); + iio_push_to_buffers(iio, (u8 *)lradc->buffer); iio_trigger_notify_done(iio->trig); diff --git a/drivers/staging/iio/gyro/adis16260_ring.c b/drivers/staging/iio/gyro/adis16260_ring.c index e294cb49736d..d6c48f850a9e 100644 --- a/drivers/staging/iio/gyro/adis16260_ring.c +++ b/drivers/staging/iio/gyro/adis16260_ring.c @@ -81,7 +81,7 @@ static irqreturn_t adis16260_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c index 697d9700db2f..dee16f0e7570 100644 --- a/drivers/staging/iio/iio_simple_dummy_buffer.c +++ b/drivers/staging/iio/iio_simple_dummy_buffer.c @@ -46,7 +46,6 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - struct iio_buffer *buffer = indio_dev->buffer; int len = 0; u16 *data; @@ -76,7 +75,7 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) i < bitmap_weight(indio_dev->active_scan_mask, indio_dev->masklength); i++, j++) { - j = find_next_bit(buffer->scan_mask, + j = find_next_bit(indio_dev->active_scan_mask, indio_dev->masklength, j); /* random access read from the 'device' */ data[i] = fakedata[j]; @@ -87,7 +86,7 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = iio_get_time_ns(); - iio_push_to_buffer(buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); kfree(data); diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index de21d47f33e9..b1fef147d5b5 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -647,7 +647,6 @@ static void ad5933_work(struct work_struct *work) struct ad5933_state *st = container_of(work, struct ad5933_state, work.work); struct iio_dev *indio_dev = i2c_get_clientdata(st->client); - struct iio_buffer *ring = indio_dev->buffer; signed short buf[2]; unsigned char status; @@ -677,8 +676,7 @@ static void ad5933_work(struct work_struct *work) } else { buf[0] = be16_to_cpu(buf[0]); } - /* save datum to the ring */ - iio_push_to_buffer(ring, (u8 *)buf); + iio_push_to_buffers(indio_dev, (u8 *)buf); } else { /* no data available - try again later */ schedule_delayed_work(&st->work, st->poll_time_jiffies); diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c index 260bdd1a4681..d46c1e38cf7b 100644 --- a/drivers/staging/iio/imu/adis16400_ring.c +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -114,7 +114,6 @@ static irqreturn_t adis16400_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adis16400_state *st = iio_priv(indio_dev); - struct iio_buffer *ring = indio_dev->buffer; int i = 0, j, ret = 0; s16 *data; @@ -148,9 +147,9 @@ static irqreturn_t adis16400_trigger_handler(int irq, void *p) } } /* Guaranteed to be aligned with 8 byte boundary */ - if (ring->scan_timestamp) + if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - iio_push_to_buffer(ring, (u8 *) data); + iio_push_to_buffers(indio_dev, (u8 *) data); done: kfree(data); diff --git a/drivers/staging/iio/meter/ade7758_ring.c b/drivers/staging/iio/meter/ade7758_ring.c index 9e49baccf660..4552a4c7fe33 100644 --- a/drivers/staging/iio/meter/ade7758_ring.c +++ b/drivers/staging/iio/meter/ade7758_ring.c @@ -73,7 +73,7 @@ static irqreturn_t ade7758_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) dat64[1] = pf->timestamp; - iio_push_to_buffer(indio_dev->buffer, (u8 *)dat64); + iio_push_to_buffers(indio_dev, (u8 *)dat64); iio_trigger_notify_done(indio_dev->trig); diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index c629b3a1d9a9..027040569180 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -66,7 +66,8 @@ struct iio_buffer_access_funcs { * @stufftoread: [INTERN] flag to indicate new data. * @demux_list: [INTERN] list of operations required to demux the scan. * @demux_bounce: [INTERN] buffer for doing gather from incoming scan. - **/ + * @buffer_list: [INTERN] entry in the devices list of current buffers. + */ struct iio_buffer { int length; int bytes_per_datum; @@ -81,8 +82,21 @@ struct iio_buffer { const struct attribute_group *attrs; struct list_head demux_list; unsigned char *demux_bounce; + struct list_head buffer_list; }; +/** + * iio_update_buffers() - add or remove buffer from active list + * @indio_dev: device to add buffer to + * @insert_buffer: buffer to insert + * @remove_buffer: buffer_to_remove + * + * Note this will tear down the all buffering and build it up again + */ +int iio_update_buffers(struct iio_dev *indio_dev, + struct iio_buffer *insert_buffer, + struct iio_buffer *remove_buffer); + /** * iio_buffer_init() - Initialize the buffer structure * @buffer: buffer to be initialized @@ -115,11 +129,11 @@ int iio_scan_mask_set(struct iio_dev *indio_dev, struct iio_buffer *buffer, int bit); /** - * iio_push_to_buffer() - push to a registered buffer. - * @buffer: IIO buffer structure for device - * @data: the data to push to the buffer + * iio_push_to_buffers() - push to a registered buffer. + * @indio_dev: iio_dev structure for device. + * @data: Full scan. */ -int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data); +int iio_push_to_buffers(struct iio_dev *indio_dev, unsigned char *data); int iio_update_demux(struct iio_dev *indio_dev); diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 7806c24e5bc8..adca93a999a7 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -410,6 +410,7 @@ struct iio_buffer_setup_ops { * and owner * @event_interface: [INTERN] event chrdevs associated with interrupt lines * @buffer: [DRIVER] any buffer present + * @buffer_list: [INTERN] list of all buffers currently attached * @scan_bytes: [INTERN] num bytes captured to be fed to buffer demux * @mlock: [INTERN] lock used to prevent simultaneous device state * changes @@ -448,6 +449,7 @@ struct iio_dev { struct iio_event_interface *event_interface; struct iio_buffer *buffer; + struct list_head buffer_list; int scan_bytes; struct mutex mlock; -- cgit v1.2.3 From 0464415dd21785aa8e8b12dbc939fcb5ca52f464 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 30 Jun 2012 20:06:00 +0100 Subject: staging:iio:in kernel users: Add a data field for channel specific info. Used to allow information about a given channel mapping to be passed through from board files to the consumer drivers. Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 1 + include/linux/iio/consumer.h | 2 ++ include/linux/iio/machine.h | 2 ++ 3 files changed, 5 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index b394621d362c..d55e98fb300e 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -203,6 +203,7 @@ struct iio_channel *iio_channel_get_all(const char *name) if (name && strcmp(name, c->map->consumer_dev_name) != 0) continue; chans[mapind].indio_dev = c->indio_dev; + chans[mapind].data = c->map->consumer_data; chans[mapind].channel = iio_chan_spec_from_name(chans[mapind].indio_dev, c->map->adc_channel_label); diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index e875bcf0478f..57efee63a6da 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -18,10 +18,12 @@ struct iio_chan_spec; * struct iio_channel - everything needed for a consumer to use a channel * @indio_dev: Device on which the channel exists. * @channel: Full description of the channel. + * @data: Data about the channel used by consumer. */ struct iio_channel { struct iio_dev *indio_dev; const struct iio_chan_spec *channel; + void *data; }; /** diff --git a/include/linux/iio/machine.h b/include/linux/iio/machine.h index 809a3f08d5a5..1601a2a63a72 100644 --- a/include/linux/iio/machine.h +++ b/include/linux/iio/machine.h @@ -19,11 +19,13 @@ * @consumer_dev_name: Name to uniquely identify the consumer device. * @consumer_channel: Unique name used to identify the channel on the * consumer side. + * @consumer_data: Data about the channel for use by the consumer driver. */ struct iio_map { const char *adc_channel_label; const char *consumer_dev_name; const char *consumer_channel; + void *consumer_data; }; #endif -- cgit v1.2.3 From 92d1079b281f89f1c65c6aece3cfab4fb422c797 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 30 Jun 2012 20:06:00 +0100 Subject: staging:iio: add a callback buffer for in kernel push interface This callback buffer is meant to be opaque to users, but basically adds a very simple pass through buffer to which data may be pushed when it is inserted into the buffer list. Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 6 +++ drivers/iio/Makefile | 1 + drivers/iio/buffer_cb.c | 113 +++++++++++++++++++++++++++++++++++++++++++ include/linux/iio/consumer.h | 46 ++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 drivers/iio/buffer_cb.c (limited to 'drivers/iio') diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index fc937aca71fb..65ae734c607d 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -20,6 +20,12 @@ config IIO_BUFFER if IIO_BUFFER +config IIO_BUFFER_CB +boolean "IIO callback buffer used for push in-kernel interfaces" + help + Should be selected by any drivers that do-inkernel push + usage. That is, those where the data is pushed to the consumer. + config IIO_KFIFO_BUF select IIO_TRIGGER tristate "Industrial I/O buffering based on kfifo" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 761f2b65ac52..31d76a07ec65 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o industrialio-y := industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o +industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c new file mode 100644 index 000000000000..4d40e24f3721 --- /dev/null +++ b/drivers/iio/buffer_cb.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include + +struct iio_cb_buffer { + struct iio_buffer buffer; + int (*cb)(u8 *data, void *private); + void *private; + struct iio_channel *channels; +}; + +static int iio_buffer_cb_store_to(struct iio_buffer *buffer, u8 *data) +{ + struct iio_cb_buffer *cb_buff = container_of(buffer, + struct iio_cb_buffer, + buffer); + + return cb_buff->cb(data, cb_buff->private); +} + +static struct iio_buffer_access_funcs iio_cb_access = { + .store_to = &iio_buffer_cb_store_to, +}; + +struct iio_cb_buffer *iio_channel_get_all_cb(const char *name, + int (*cb)(u8 *data, + void *private), + void *private) +{ + int ret; + struct iio_cb_buffer *cb_buff; + struct iio_dev *indio_dev; + struct iio_channel *chan; + + cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); + if (cb_buff == NULL) { + ret = -ENOMEM; + goto error_ret; + } + + cb_buff->private = private; + cb_buff->cb = cb; + cb_buff->buffer.access = &iio_cb_access; + INIT_LIST_HEAD(&cb_buff->buffer.demux_list); + + cb_buff->channels = iio_channel_get_all(name); + if (IS_ERR(cb_buff->channels)) { + ret = PTR_ERR(cb_buff->channels); + goto error_free_cb_buff; + } + + indio_dev = cb_buff->channels[0].indio_dev; + cb_buff->buffer.scan_mask + = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long), + GFP_KERNEL); + if (cb_buff->buffer.scan_mask == NULL) { + ret = -ENOMEM; + goto error_release_channels; + } + chan = &cb_buff->channels[0]; + while (chan->indio_dev) { + if (chan->indio_dev != indio_dev) { + ret = -EINVAL; + goto error_release_channels; + } + set_bit(chan->channel->scan_index, + cb_buff->buffer.scan_mask); + chan++; + } + + return cb_buff; + +error_release_channels: + iio_channel_release_all(cb_buff->channels); +error_free_cb_buff: + kfree(cb_buff); +error_ret: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); + +int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff) +{ + return iio_update_buffers(cb_buff->channels[0].indio_dev, + &cb_buff->buffer, + NULL); +} +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb); + +void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff) +{ + iio_update_buffers(cb_buff->channels[0].indio_dev, + NULL, + &cb_buff->buffer); +} +EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb); + +void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff) +{ + iio_channel_release_all(cb_buff->channels); + kfree(cb_buff); +} +EXPORT_SYMBOL_GPL(iio_channel_release_all_cb); + +struct iio_channel +*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer) +{ + return cb_buffer->channels; +} +EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels); diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 57efee63a6da..126c0a9375f2 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -61,6 +61,52 @@ struct iio_channel *iio_channel_get_all(const char *name); */ void iio_channel_release_all(struct iio_channel *chan); +struct iio_cb_buffer; +/** + * iio_channel_get_all_cb() - register callback for triggered capture + * @name: Name of client device. + * @cb: Callback function. + * @private: Private data passed to callback. + * + * NB right now we have no ability to mux data from multiple devices. + * So if the channels requested come from different devices this will + * fail. + */ +struct iio_cb_buffer *iio_channel_get_all_cb(const char *name, + int (*cb)(u8 *data, + void *private), + void *private); +/** + * iio_channel_release_all_cb() - release and unregister the callback. + * @cb_buffer: The callback buffer that was allocated. + */ +void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buffer); + +/** + * iio_channel_start_all_cb() - start the flow of data through callback. + * @cb_buff: The callback buffer we are starting. + */ +int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff); + +/** + * iio_channel_stop_all_cb() - stop the flow of data through the callback. + * @cb_buff: The callback buffer we are stopping. + */ +void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff); + +/** + * iio_channel_cb_get_channels() - get access to the underlying channels. + * @cb_buff: The callback buffer from whom we want the channel + * information. + * + * This function allows one to obtain information about the channels. + * Whilst this may allow direct reading if all buffers are disabled, the + * primary aim is to allow drivers that are consuming a channel to query + * things like scaling of the channel. + */ +struct iio_channel +*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer); + /** * iio_read_channel_raw() - read from a given channel * @chan: The channel being queried. -- cgit v1.2.3 From 168c9d95a94077a42e5bf5a4a660b45ccee4fc63 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 6 Oct 2012 14:42:27 +0100 Subject: iio:adc:max1363 move from staging. Now this driver is using kfifo we can move it out of staging. Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 18 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/max1363.c | 1700 +++++++++++++++++++++++++++++++++++++ drivers/staging/iio/adc/Kconfig | 18 - drivers/staging/iio/adc/Makefile | 2 - drivers/staging/iio/adc/max1363.c | 1700 ------------------------------------- 6 files changed, 1719 insertions(+), 1720 deletions(-) create mode 100644 drivers/iio/adc/max1363.c delete mode 100644 drivers/staging/iio/adc/max1363.c (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 706386ba02e3..ef5200a6850e 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -73,4 +73,22 @@ config LP8788_ADC help Say yes here to build support for TI LP8788 ADC. +config MAX1363 + tristate "Maxim max1363 ADC driver" + depends on I2C + select IIO_TRIGGER + select MAX1363_RING_BUFFER + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Say yes here to build support for many Maxim i2c analog to digital + converters (ADC). (max1361, max1362, max1363, max1364, max1036, + max1037, max1038, max1039, max1136, max1136, max1137, max1138, + max1139, max1236, max1237, max11238, max1239, max11600, max11601, + max11602, max11603, max11604, max11605, max11606, max11607, + max11608, max11609, max11610, max11611, max11612, max11613, + max11614, max11615, max11616, max11617, max11644, max11645, + max11646, max11647) Provides direct access via sysfs and buffered + data via the iio dev interface. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 034eacb8f7c9..54ac7bbcd01b 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o +obj-$(CONFIG_MAX1363) += max1363.o diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c new file mode 100644 index 000000000000..1e84b5b55093 --- /dev/null +++ b/drivers/iio/adc/max1363.c @@ -0,0 +1,1700 @@ + /* + * iio/adc/max1363.c + * Copyright (C) 2008-2010 Jonathan Cameron + * + * based on linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * max1363.c + * + * Partial support for max1363 and similar chips. + * + * Not currently implemented. + * + * - Control of internal reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX1363_SETUP_BYTE(a) ((a) | 0x80) + +/* There is a fair bit more defined here than currently + * used, but the intention is to support everything these + * chips do in the long run */ + +/* see data sheets */ +/* max1363 and max1236, max1237, max1238, max1239 */ +#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD 0x00 +#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF 0x20 +#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT 0x40 +#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT 0x60 +#define MAX1363_SETUP_POWER_UP_INT_REF 0x10 +#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00 + +/* think about includeing max11600 etc - more settings */ +#define MAX1363_SETUP_EXT_CLOCK 0x08 +#define MAX1363_SETUP_INT_CLOCK 0x00 +#define MAX1363_SETUP_UNIPOLAR 0x00 +#define MAX1363_SETUP_BIPOLAR 0x04 +#define MAX1363_SETUP_RESET 0x00 +#define MAX1363_SETUP_NORESET 0x02 +/* max1363 only - though don't care on others. + * For now monitor modes are not implemented as the relevant + * line is not connected on my test board. + * The definitions are here as I intend to add this soon. + */ +#define MAX1363_SETUP_MONITOR_SETUP 0x01 + +/* Specific to the max1363 */ +#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4)) +#define MAX1363_MON_INT_ENABLE 0x01 + +/* defined for readability reasons */ +/* All chips */ +#define MAX1363_CONFIG_BYTE(a) ((a)) + +#define MAX1363_CONFIG_SE 0x01 +#define MAX1363_CONFIG_DE 0x00 +#define MAX1363_CONFIG_SCAN_TO_CS 0x00 +#define MAX1363_CONFIG_SCAN_SINGLE_8 0x20 +#define MAX1363_CONFIG_SCAN_MONITOR_MODE 0x40 +#define MAX1363_CONFIG_SCAN_SINGLE_1 0x60 +/* max123{6-9} only */ +#define MAX1236_SCAN_MID_TO_CHANNEL 0x40 + +/* max1363 only - merely part of channel selects or don't care for others*/ +#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18 + +#define MAX1363_CHANNEL_SEL(a) ((a) << 1) + +/* max1363 strictly 0x06 - but doesn't matter */ +#define MAX1363_CHANNEL_SEL_MASK 0x1E +#define MAX1363_SCAN_MASK 0x60 +#define MAX1363_SE_DE_MASK 0x01 + +#define MAX1363_MAX_CHANNELS 25 +/** + * struct max1363_mode - scan mode information + * @conf: The corresponding value of the configuration register + * @modemask: Bit mask corresponding to channels enabled in this mode + */ +struct max1363_mode { + int8_t conf; + DECLARE_BITMAP(modemask, MAX1363_MAX_CHANNELS); +}; + +/* This must be maintained along side the max1363_mode_table in max1363_core */ +enum max1363_modes { + /* Single read of a single channel */ + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, + /* Differential single read */ + d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, + d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, + /* Scan to channel and mid to channel where overlapping */ + s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6, + s6to7, s0to7, s6to8, s0to8, s6to9, + s0to9, s6to10, s0to10, s6to11, s0to11, + /* Differential scan to channel and mid to channel where overlapping */ + d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9, + d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2, + d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8, + d7m6to11m10, d1m0to11m10, +}; + +/** + * struct max1363_chip_info - chip specifc information + * @info: iio core function callbacks structure + * @channels: channel specification + * @num_channels: number of channels + * @mode_list: array of available scan modes + * @default_mode: the scan mode in which the chip starts up + * @int_vref_mv: the internal reference voltage + * @num_channels: number of channels + * @bits: accuracy of the adc in bits + */ +struct max1363_chip_info { + const struct iio_info *info; + const struct iio_chan_spec *channels; + int num_channels; + const enum max1363_modes *mode_list; + enum max1363_modes default_mode; + u16 int_vref_mv; + u8 num_modes; + u8 bits; +}; + +/** + * struct max1363_state - driver instance specific data + * @client: i2c_client + * @setupbyte: cache of current device setup byte + * @configbyte: cache of current device config byte + * @chip_info: chip model specific constants, available modes etc + * @current_mode: the scan mode of this chip + * @requestedmask: a valid requested set of channels + * @reg: supply regulator + * @monitor_on: whether monitor mode is enabled + * @monitor_speed: parameter corresponding to device monitor speed setting + * @mask_high: bitmask for enabled high thresholds + * @mask_low: bitmask for enabled low thresholds + * @thresh_high: high threshold values + * @thresh_low: low threshold values + */ +struct max1363_state { + struct i2c_client *client; + u8 setupbyte; + u8 configbyte; + const struct max1363_chip_info *chip_info; + const struct max1363_mode *current_mode; + u32 requestedmask; + struct regulator *reg; + + /* Using monitor modes and buffer at the same time is + currently not supported */ + bool monitor_on; + unsigned int monitor_speed:3; + u8 mask_high; + u8 mask_low; + /* 4x unipolar first then the fours bipolar ones */ + s16 thresh_high[8]; + s16 thresh_low[8]; +}; + +#define MAX1363_MODE_SINGLE(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_SINGLE_1 \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask, \ + } + +#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_TO_CS \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask, \ + } + +/* note not available for max1363 hence naming */ +#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1236_SCAN_MID_TO_CHANNEL \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask \ +} + +#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_nump) \ + | MAX1363_CONFIG_SCAN_SINGLE_1 \ + | MAX1363_CONFIG_DE, \ + .modemask[0] = _mask \ + } + +/* Can't think how to automate naming so specify for now */ +#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_TO_CS \ + | MAX1363_CONFIG_DE, \ + .modemask[0] = _mask \ + } + +/* note only available for max1363 hence naming */ +#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1236_SCAN_MID_TO_CHANNEL \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask \ +} + +static const struct max1363_mode max1363_mode_table[] = { + /* All of the single channel options first */ + MAX1363_MODE_SINGLE(0, 1 << 0), + MAX1363_MODE_SINGLE(1, 1 << 1), + MAX1363_MODE_SINGLE(2, 1 << 2), + MAX1363_MODE_SINGLE(3, 1 << 3), + MAX1363_MODE_SINGLE(4, 1 << 4), + MAX1363_MODE_SINGLE(5, 1 << 5), + MAX1363_MODE_SINGLE(6, 1 << 6), + MAX1363_MODE_SINGLE(7, 1 << 7), + MAX1363_MODE_SINGLE(8, 1 << 8), + MAX1363_MODE_SINGLE(9, 1 << 9), + MAX1363_MODE_SINGLE(10, 1 << 10), + MAX1363_MODE_SINGLE(11, 1 << 11), + + MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12), + MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13), + MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14), + MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15), + MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16), + MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17), + MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18), + MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19), + MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20), + MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21), + MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22), + MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23), + + /* The multichannel scans next */ + MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003), + MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C), + MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F), + MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F), + MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F), + MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0), + MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0), + MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0), + MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0), + MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0), + MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF), + + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000), +}; + +static const struct max1363_mode +*max1363_match_mode(const unsigned long *mask, +const struct max1363_chip_info *ci) +{ + int i; + if (mask) + for (i = 0; i < ci->num_modes; i++) + if (bitmap_subset(mask, + max1363_mode_table[ci->mode_list[i]]. + modemask, + MAX1363_MAX_CHANNELS)) + return &max1363_mode_table[ci->mode_list[i]]; + return NULL; +} + +static int max1363_write_basic_config(struct i2c_client *client, + unsigned char d1, + unsigned char d2) +{ + u8 tx_buf[2] = {d1, d2}; + + return i2c_master_send(client, tx_buf, 2); +} + +static int max1363_set_scan_mode(struct max1363_state *st) +{ + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= st->current_mode->conf; + + return max1363_write_basic_config(st->client, + st->setupbyte, + st->configbyte); +} + +static int max1363_read_single_chan(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + long m) +{ + int ret = 0; + s32 data; + char rxbuf[2]; + struct max1363_state *st = iio_priv(indio_dev); + struct i2c_client *client = st->client; + + mutex_lock(&indio_dev->mlock); + /* + * If monitor mode is enabled, the method for reading a single + * channel will have to be rather different and has not yet + * been implemented. + * + * Also, cannot read directly if buffered capture enabled. + */ + if (st->monitor_on || iio_buffer_enabled(indio_dev)) { + ret = -EBUSY; + goto error_ret; + } + + /* Check to see if current scan mode is correct */ + if (st->current_mode != &max1363_mode_table[chan->address]) { + /* Update scan mode if needed */ + st->current_mode = &max1363_mode_table[chan->address]; + ret = max1363_set_scan_mode(st); + if (ret < 0) + goto error_ret; + } + if (st->chip_info->bits != 8) { + /* Get reading */ + data = i2c_master_recv(client, rxbuf, 2); + if (data < 0) { + ret = data; + goto error_ret; + } + data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8; + } else { + /* Get reading */ + data = i2c_master_recv(client, rxbuf, 1); + if (data < 0) { + ret = data; + goto error_ret; + } + data = rxbuf[0]; + } + *val = data; +error_ret: + mutex_unlock(&indio_dev->mlock); + return ret; + +} + +static int max1363_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct max1363_state *st = iio_priv(indio_dev); + int ret; + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = max1363_read_single_chan(indio_dev, chan, val, m); + if (ret < 0) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if ((1 << (st->chip_info->bits + 1)) > + st->chip_info->int_vref_mv) { + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + } else { + *val = (st->chip_info->int_vref_mv) + >> st->chip_info->bits; + return IIO_VAL_INT; + } + default: + return -EINVAL; + } + return 0; +} + +/* Applies to max1363 */ +static const enum max1363_modes max1363_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +#define MAX1363_EV_M \ + (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) \ + | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)) +#define MAX1363_INFO_MASK (IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT) +#define MAX1363_CHAN_U(num, addr, si, bits, evmask) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num, \ + .address = addr, \ + .info_mask = MAX1363_INFO_MASK, \ + .datasheet_name = "AIN"#num, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = bits, \ + .storagebits = (bits > 8) ? 16 : 8, \ + .endianness = IIO_BE, \ + }, \ + .scan_index = si, \ + .event_mask = evmask, \ + } + +/* bipolar channel */ +#define MAX1363_CHAN_B(num, num2, addr, si, bits, evmask) \ + { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .channel = num, \ + .channel2 = num2, \ + .address = addr, \ + .info_mask = MAX1363_INFO_MASK, \ + .datasheet_name = "AIN"#num"-AIN"#num2, \ + .scan_type = { \ + .sign = 's', \ + .realbits = bits, \ + .storagebits = (bits > 8) ? 16 : 8, \ + .endianness = IIO_BE, \ + }, \ + .scan_index = si, \ + .event_mask = evmask, \ + } + +#define MAX1363_4X_CHANS(bits, em) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, em), \ + MAX1363_CHAN_U(1, _s1, 1, bits, em), \ + MAX1363_CHAN_U(2, _s2, 2, bits, em), \ + MAX1363_CHAN_U(3, _s3, 3, bits, em), \ + MAX1363_CHAN_B(0, 1, d0m1, 4, bits, em), \ + MAX1363_CHAN_B(2, 3, d2m3, 5, bits, em), \ + MAX1363_CHAN_B(1, 0, d1m0, 6, bits, em), \ + MAX1363_CHAN_B(3, 2, d3m2, 7, bits, em), \ + IIO_CHAN_SOFT_TIMESTAMP(8) \ + } + +static const struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8, 0); +static const struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10, 0); +static const struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12, 0); +static const struct iio_chan_spec max1361_channels[] = + MAX1363_4X_CHANS(10, MAX1363_EV_M); +static const struct iio_chan_spec max1363_channels[] = + MAX1363_4X_CHANS(12, MAX1363_EV_M); + +/* Applies to max1236, max1237 */ +static const enum max1363_modes max1236_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, + s2to3, +}; + +/* Applies to max1238, max1239 */ +static const enum max1363_modes max1238_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, + s0to7, s0to8, s0to9, s0to10, s0to11, + d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, + d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, + d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, + d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, + s6to7, s6to8, s6to9, s6to10, s6to11, + d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10, +}; + +#define MAX1363_12X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, 0), \ + MAX1363_CHAN_U(2, _s2, 2, bits, 0), \ + MAX1363_CHAN_U(3, _s3, 3, bits, 0), \ + MAX1363_CHAN_U(4, _s4, 4, bits, 0), \ + MAX1363_CHAN_U(5, _s5, 5, bits, 0), \ + MAX1363_CHAN_U(6, _s6, 6, bits, 0), \ + MAX1363_CHAN_U(7, _s7, 7, bits, 0), \ + MAX1363_CHAN_U(8, _s8, 8, bits, 0), \ + MAX1363_CHAN_U(9, _s9, 9, bits, 0), \ + MAX1363_CHAN_U(10, _s10, 10, bits, 0), \ + MAX1363_CHAN_U(11, _s11, 11, bits, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 12, bits, 0), \ + MAX1363_CHAN_B(2, 3, d2m3, 13, bits, 0), \ + MAX1363_CHAN_B(4, 5, d4m5, 14, bits, 0), \ + MAX1363_CHAN_B(6, 7, d6m7, 15, bits, 0), \ + MAX1363_CHAN_B(8, 9, d8m9, 16, bits, 0), \ + MAX1363_CHAN_B(10, 11, d10m11, 17, bits, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 18, bits, 0), \ + MAX1363_CHAN_B(3, 2, d3m2, 19, bits, 0), \ + MAX1363_CHAN_B(5, 4, d5m4, 20, bits, 0), \ + MAX1363_CHAN_B(7, 6, d7m6, 21, bits, 0), \ + MAX1363_CHAN_B(9, 8, d9m8, 22, bits, 0), \ + MAX1363_CHAN_B(11, 10, d11m10, 23, bits, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(24) \ + } +static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8); +static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10); +static const struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12); + +static const enum max1363_modes max11607_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + s2to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +static const enum max1363_modes max11608_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7, + s6to7, + d0m1, d2m3, d4m5, d6m7, + d1m0, d3m2, d5m4, d7m6, + d0m1to2m3, d0m1to4m5, d0m1to6m7, + d1m0to3m2, d1m0to5m4, d1m0to7m6, +}; + +#define MAX1363_8X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, 0), \ + MAX1363_CHAN_U(2, _s2, 2, bits, 0), \ + MAX1363_CHAN_U(3, _s3, 3, bits, 0), \ + MAX1363_CHAN_U(4, _s4, 4, bits, 0), \ + MAX1363_CHAN_U(5, _s5, 5, bits, 0), \ + MAX1363_CHAN_U(6, _s6, 6, bits, 0), \ + MAX1363_CHAN_U(7, _s7, 7, bits, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 8, bits, 0), \ + MAX1363_CHAN_B(2, 3, d2m3, 9, bits, 0), \ + MAX1363_CHAN_B(4, 5, d4m5, 10, bits, 0), \ + MAX1363_CHAN_B(6, 7, d6m7, 11, bits, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 12, bits, 0), \ + MAX1363_CHAN_B(3, 2, d3m2, 13, bits, 0), \ + MAX1363_CHAN_B(5, 4, d5m4, 14, bits, 0), \ + MAX1363_CHAN_B(7, 6, d7m6, 15, bits, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(16) \ +} +static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8); +static const struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10); +static const struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12); + +static const enum max1363_modes max11644_mode_list[] = { + _s0, _s1, s0to1, d0m1, d1m0, +}; + +#define MAX1363_2X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 2, bits, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 3, bits, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4) \ + } + +static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10); +static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12); + +enum { max1361, + max1362, + max1363, + max1364, + max1036, + max1037, + max1038, + max1039, + max1136, + max1137, + max1138, + max1139, + max1236, + max1237, + max1238, + max1239, + max11600, + max11601, + max11602, + max11603, + max11604, + max11605, + max11606, + max11607, + max11608, + max11609, + max11610, + max11611, + max11612, + max11613, + max11614, + max11615, + max11616, + max11617, + max11644, + max11645, + max11646, + max11647 +}; + +static const int max1363_monitor_speeds[] = { 133000, 665000, 33300, 16600, + 8300, 4200, 2000, 1000 }; + +static ssize_t max1363_monitor_show_freq(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max1363_state *st = iio_priv(dev_to_iio_dev(dev)); + return sprintf(buf, "%d\n", max1363_monitor_speeds[st->monitor_speed]); +} + +static ssize_t max1363_monitor_store_freq(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max1363_state *st = iio_priv(indio_dev); + int i, ret; + unsigned long val; + bool found = false; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++) + if (val == max1363_monitor_speeds[i]) { + found = true; + break; + } + if (!found) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + st->monitor_speed = i; + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, + max1363_monitor_show_freq, + max1363_monitor_store_freq); + +static IIO_CONST_ATTR(sampling_frequency_available, + "133000 665000 33300 16600 8300 4200 2000 1000"); + +static int max1363_read_thresh(struct iio_dev *indio_dev, + u64 event_code, + int *val) +{ + struct max1363_state *st = iio_priv(indio_dev); + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) + *val = st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]; + else + *val = st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]; + return 0; +} + +static int max1363_write_thresh(struct iio_dev *indio_dev, + u64 event_code, + int val) +{ + struct max1363_state *st = iio_priv(indio_dev); + /* make it handle signed correctly as well */ + switch (st->chip_info->bits) { + case 10: + if (val > 0x3FF) + return -EINVAL; + break; + case 12: + if (val > 0xFFF) + return -EINVAL; + break; + } + + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { + case IIO_EV_DIR_FALLING: + st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val; + break; + case IIO_EV_DIR_RISING: + st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val; + break; + } + + return 0; +} + +static const u64 max1363_event_codes[] = { + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), +}; + +static irqreturn_t max1363_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct max1363_state *st = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(); + unsigned long mask, loc; + u8 rx; + u8 tx[2] = { st->setupbyte, + MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 }; + + i2c_master_recv(st->client, &rx, 1); + mask = rx; + for_each_set_bit(loc, &mask, 8) + iio_push_event(indio_dev, max1363_event_codes[loc], timestamp); + i2c_master_send(st->client, tx, 2); + + return IRQ_HANDLED; +} + +static int max1363_read_event_config(struct iio_dev *indio_dev, + u64 event_code) +{ + struct max1363_state *st = iio_priv(indio_dev); + int val; + int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); + + mutex_lock(&indio_dev->mlock); + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) + val = (1 << number) & st->mask_low; + else + val = (1 << number) & st->mask_high; + mutex_unlock(&indio_dev->mlock); + + return val; +} + +static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) +{ + u8 *tx_buf; + int ret, i = 3, j; + unsigned long numelements; + int len; + const long *modemask; + + if (!enabled) { + /* transition to buffered capture is not currently supported */ + st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP; + st->configbyte &= ~MAX1363_SCAN_MASK; + st->monitor_on = false; + return max1363_write_basic_config(st->client, + st->setupbyte, + st->configbyte); + } + + /* Ensure we are in the relevant mode */ + st->setupbyte |= MAX1363_SETUP_MONITOR_SETUP; + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= MAX1363_CONFIG_SCAN_MONITOR_MODE; + if ((st->mask_low | st->mask_high) & 0x0F) { + st->configbyte |= max1363_mode_table[s0to3].conf; + modemask = max1363_mode_table[s0to3].modemask; + } else if ((st->mask_low | st->mask_high) & 0x30) { + st->configbyte |= max1363_mode_table[d0m1to2m3].conf; + modemask = max1363_mode_table[d0m1to2m3].modemask; + } else { + st->configbyte |= max1363_mode_table[d1m0to3m2].conf; + modemask = max1363_mode_table[d1m0to3m2].modemask; + } + numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS); + len = 3 * numelements + 3; + tx_buf = kmalloc(len, GFP_KERNEL); + if (!tx_buf) { + ret = -ENOMEM; + goto error_ret; + } + tx_buf[0] = st->configbyte; + tx_buf[1] = st->setupbyte; + tx_buf[2] = (st->monitor_speed << 1); + + /* + * So we need to do yet another bit of nefarious scan mode + * setup to match what we need. + */ + for (j = 0; j < 8; j++) + if (test_bit(j, modemask)) { + /* Establish the mode is in the scan */ + if (st->mask_low & (1 << j)) { + tx_buf[i] = (st->thresh_low[j] >> 4) & 0xFF; + tx_buf[i + 1] = (st->thresh_low[j] << 4) & 0xF0; + } else if (j < 4) { + tx_buf[i] = 0; + tx_buf[i + 1] = 0; + } else { + tx_buf[i] = 0x80; + tx_buf[i + 1] = 0; + } + if (st->mask_high & (1 << j)) { + tx_buf[i + 1] |= + (st->thresh_high[j] >> 8) & 0x0F; + tx_buf[i + 2] = st->thresh_high[j] & 0xFF; + } else if (j < 4) { + tx_buf[i + 1] |= 0x0F; + tx_buf[i + 2] = 0xFF; + } else { + tx_buf[i + 1] |= 0x07; + tx_buf[i + 2] = 0xFF; + } + i += 3; + } + + + ret = i2c_master_send(st->client, tx_buf, len); + if (ret < 0) + goto error_ret; + if (ret != len) { + ret = -EIO; + goto error_ret; + } + + /* + * Now that we hopefully have sensible thresholds in place it is + * time to turn the interrupts on. + * It is unclear from the data sheet if this should be necessary + * (i.e. whether monitor mode setup is atomic) but it appears to + * be in practice. + */ + tx_buf[0] = st->setupbyte; + tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0; + ret = i2c_master_send(st->client, tx_buf, 2); + if (ret < 0) + goto error_ret; + if (ret != 2) { + ret = -EIO; + goto error_ret; + } + ret = 0; + st->monitor_on = true; +error_ret: + + kfree(tx_buf); + + return ret; +} + +/* + * To keep this manageable we always use one of 3 scan modes. + * Scan 0...3, 0-1,2-3 and 1-0,3-2 + */ + +static inline int __max1363_check_event_mask(int thismask, int checkmask) +{ + int ret = 0; + /* Is it unipolar */ + if (thismask < 4) { + if (checkmask & ~0x0F) { + ret = -EBUSY; + goto error_ret; + } + } else if (thismask < 6) { + if (checkmask & ~0x30) { + ret = -EBUSY; + goto error_ret; + } + } else if (checkmask & ~0xC0) + ret = -EBUSY; +error_ret: + return ret; +} + +static int max1363_write_event_config(struct iio_dev *indio_dev, + u64 event_code, + int state) +{ + int ret = 0; + struct max1363_state *st = iio_priv(indio_dev); + u16 unifiedmask; + int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); + + mutex_lock(&indio_dev->mlock); + unifiedmask = st->mask_low | st->mask_high; + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) { + + if (state == 0) + st->mask_low &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + goto error_ret; + st->mask_low |= (1 << number); + } + } else { + if (state == 0) + st->mask_high &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + goto error_ret; + st->mask_high |= (1 << number); + } + } + + max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); +error_ret: + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +/* + * As with scan_elements, only certain sets of these can + * be combined. + */ +static struct attribute *max1363_event_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static struct attribute_group max1363_event_attribute_group = { + .attrs = max1363_event_attributes, + .name = "events", +}; + +static int max1363_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct max1363_state *st = iio_priv(indio_dev); + + /* + * Need to figure out the current mode based upon the requested + * scan mask in iio_dev + */ + st->current_mode = max1363_match_mode(scan_mask, st->chip_info); + if (!st->current_mode) + return -EINVAL; + max1363_set_scan_mode(st); + return 0; +} + +static const struct iio_info max1238_info = { + .read_raw = &max1363_read_raw, + .driver_module = THIS_MODULE, + .update_scan_mode = &max1363_update_scan_mode, +}; + +static const struct iio_info max1363_info = { + .read_event_value = &max1363_read_thresh, + .write_event_value = &max1363_write_thresh, + .read_event_config = &max1363_read_event_config, + .write_event_config = &max1363_write_event_config, + .read_raw = &max1363_read_raw, + .update_scan_mode = &max1363_update_scan_mode, + .driver_module = THIS_MODULE, + .event_attrs = &max1363_event_attribute_group, +}; + +/* max1363 and max1368 tested - rest from data sheet */ +static const struct max1363_chip_info max1363_chip_info_tbl[] = { + [max1361] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1361_channels, + .num_channels = ARRAY_SIZE(max1361_channels), + .info = &max1363_info, + }, + [max1362] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1361_channels, + .num_channels = ARRAY_SIZE(max1361_channels), + .info = &max1363_info, + }, + [max1363] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + .info = &max1363_info, + }, + [max1364] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + .info = &max1363_info, + }, + [max1036] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max1037] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max1038] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max1039] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max1136] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max1137] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max1138] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max1139] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max1236] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1236_channels, + .num_channels = ARRAY_SIZE(max1236_channels), + }, + [max1237] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1236_channels, + .num_channels = ARRAY_SIZE(max1236_channels), + }, + [max1238] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max1239] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11600] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max11601] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max11602] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11602_channels, + .num_channels = ARRAY_SIZE(max11602_channels), + }, + [max11603] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11602_channels, + .num_channels = ARRAY_SIZE(max11602_channels), + }, + [max11604] = { + .bits = 8, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11605] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11606] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max11607] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max11608] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11608_channels, + .num_channels = ARRAY_SIZE(max11608_channels), + }, + [max11609] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11608_channels, + .num_channels = ARRAY_SIZE(max11608_channels), + }, + [max11610] = { + .bits = 10, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11611] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11612] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + }, + [max11613] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + }, + [max11614] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11614_channels, + .num_channels = ARRAY_SIZE(max11614_channels), + }, + [max11615] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11614_channels, + .num_channels = ARRAY_SIZE(max11614_channels), + }, + [max11616] = { + .bits = 12, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11617] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11644] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11644_channels, + .num_channels = ARRAY_SIZE(max11644_channels), + }, + [max11645] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11644_channels, + .num_channels = ARRAY_SIZE(max11644_channels), + }, + [max11646] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11646_channels, + .num_channels = ARRAY_SIZE(max11646_channels), + }, + [max11647] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11646_channels, + .num_channels = ARRAY_SIZE(max11646_channels), + }, +}; + +static int max1363_initial_setup(struct max1363_state *st) +{ + st->setupbyte = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD + | MAX1363_SETUP_POWER_UP_INT_REF + | MAX1363_SETUP_INT_CLOCK + | MAX1363_SETUP_UNIPOLAR + | MAX1363_SETUP_NORESET; + + /* Set scan mode writes the config anyway so wait until then*/ + st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte); + st->current_mode = &max1363_mode_table[st->chip_info->default_mode]; + st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte); + + return max1363_set_scan_mode(st); +} + +static int __devinit max1363_alloc_scan_masks(struct iio_dev *indio_dev) +{ + struct max1363_state *st = iio_priv(indio_dev); + unsigned long *masks; + int i; + + masks = kzalloc(BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*sizeof(long)* + (st->chip_info->num_modes + 1), GFP_KERNEL); + if (!masks) + return -ENOMEM; + + for (i = 0; i < st->chip_info->num_modes; i++) + bitmap_copy(masks + BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i, + max1363_mode_table[st->chip_info->mode_list[i]] + .modemask, MAX1363_MAX_CHANNELS); + + indio_dev->available_scan_masks = masks; + + return 0; +} + + +static irqreturn_t max1363_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct max1363_state *st = iio_priv(indio_dev); + s64 time_ns; + __u8 *rxbuf; + int b_sent; + size_t d_size; + unsigned long numvals = bitmap_weight(st->current_mode->modemask, + MAX1363_MAX_CHANNELS); + + /* Ensure the timestamp is 8 byte aligned */ + if (st->chip_info->bits != 8) + d_size = numvals*2; + else + d_size = numvals; + if (indio_dev->scan_timestamp) { + d_size += sizeof(s64); + if (d_size % sizeof(s64)) + d_size += sizeof(s64) - (d_size % sizeof(s64)); + } + /* Monitor mode prevents reading. Whilst not currently implemented + * might as well have this test in here in the meantime as it does + * no harm. + */ + if (numvals == 0) + goto done; + + rxbuf = kmalloc(d_size, GFP_KERNEL); + if (rxbuf == NULL) + goto done; + if (st->chip_info->bits != 8) + b_sent = i2c_master_recv(st->client, rxbuf, numvals*2); + else + b_sent = i2c_master_recv(st->client, rxbuf, numvals); + if (b_sent < 0) + goto done_free; + + time_ns = iio_get_time_ns(); + + if (indio_dev->scan_timestamp) + memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); + iio_push_to_buffers(indio_dev, rxbuf); + +done_free: + kfree(rxbuf); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops max1363_buffered_setup_ops = { + .postenable = &iio_triggered_buffer_postenable, + .preenable = &iio_sw_buffer_preenable, + .predisable = &iio_triggered_buffer_predisable, +}; + +static int max1363_register_buffered_funcs_and_init(struct iio_dev *indio_dev) +{ + struct max1363_state *st = iio_priv(indio_dev); + int ret = 0; + + indio_dev->buffer = iio_kfifo_allocate(indio_dev); + if (!indio_dev->buffer) { + ret = -ENOMEM; + goto error_ret; + } + indio_dev->pollfunc = iio_alloc_pollfunc(NULL, + &max1363_trigger_handler, + IRQF_ONESHOT, + indio_dev, + "%s_consumer%d", + st->client->name, + indio_dev->id); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_deallocate_sw_rb; + } + /* Buffer functions - here trigger setup related */ + indio_dev->setup_ops = &max1363_buffered_setup_ops; + + /* Flag that polled buffering is possible */ + indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + + return 0; + +error_deallocate_sw_rb: + iio_kfifo_free(indio_dev->buffer); +error_ret: + return ret; +} + +static void max1363_buffer_cleanup(struct iio_dev *indio_dev) +{ + /* ensure that the trigger has been detached */ + iio_dealloc_pollfunc(indio_dev->pollfunc); + iio_kfifo_free(indio_dev->buffer); +} + +static int __devinit max1363_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct max1363_state *st; + struct iio_dev *indio_dev; + + indio_dev = iio_device_alloc(sizeof(struct max1363_state)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_out; + } + + ret = iio_map_array_register(indio_dev, client->dev.platform_data); + if (ret < 0) + goto error_free_device; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&client->dev, "vcc"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto error_unregister_map; + } + + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + st->chip_info = &max1363_chip_info_tbl[id->driver_data]; + st->client = client; + + ret = max1363_alloc_scan_masks(indio_dev); + if (ret) + goto error_disable_reg; + + /* Estabilish that the iio_dev is a child of the i2c device */ + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = st->chip_info->info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + ret = max1363_initial_setup(st); + if (ret < 0) + goto error_free_available_scan_masks; + + ret = max1363_register_buffered_funcs_and_init(indio_dev); + if (ret) + goto error_free_available_scan_masks; + + ret = iio_buffer_register(indio_dev, + st->chip_info->channels, + st->chip_info->num_channels); + if (ret) + goto error_cleanup_buffer; + + if (client->irq) { + ret = request_threaded_irq(st->client->irq, + NULL, + &max1363_event_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "max1363_event", + indio_dev); + + if (ret) + goto error_uninit_buffer; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_free_irq; + + return 0; +error_free_irq: + free_irq(st->client->irq, indio_dev); +error_uninit_buffer: + iio_buffer_unregister(indio_dev); +error_cleanup_buffer: + max1363_buffer_cleanup(indio_dev); +error_free_available_scan_masks: + kfree(indio_dev->available_scan_masks); +error_unregister_map: + iio_map_array_unregister(indio_dev, client->dev.platform_data); +error_disable_reg: + regulator_disable(st->reg); +error_put_reg: + regulator_put(st->reg); +error_free_device: + iio_device_free(indio_dev); +error_out: + return ret; +} + +static int __devexit max1363_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct max1363_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (client->irq) + free_irq(st->client->irq, indio_dev); + iio_buffer_unregister(indio_dev); + max1363_buffer_cleanup(indio_dev); + kfree(indio_dev->available_scan_masks); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_map_array_unregister(indio_dev, client->dev.platform_data); + iio_device_free(indio_dev); + + return 0; +} + +static const struct i2c_device_id max1363_id[] = { + { "max1361", max1361 }, + { "max1362", max1362 }, + { "max1363", max1363 }, + { "max1364", max1364 }, + { "max1036", max1036 }, + { "max1037", max1037 }, + { "max1038", max1038 }, + { "max1039", max1039 }, + { "max1136", max1136 }, + { "max1137", max1137 }, + { "max1138", max1138 }, + { "max1139", max1139 }, + { "max1236", max1236 }, + { "max1237", max1237 }, + { "max1238", max1238 }, + { "max1239", max1239 }, + { "max11600", max11600 }, + { "max11601", max11601 }, + { "max11602", max11602 }, + { "max11603", max11603 }, + { "max11604", max11604 }, + { "max11605", max11605 }, + { "max11606", max11606 }, + { "max11607", max11607 }, + { "max11608", max11608 }, + { "max11609", max11609 }, + { "max11610", max11610 }, + { "max11611", max11611 }, + { "max11612", max11612 }, + { "max11613", max11613 }, + { "max11614", max11614 }, + { "max11615", max11615 }, + { "max11616", max11616 }, + { "max11617", max11617 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, max1363_id); + +static struct i2c_driver max1363_driver = { + .driver = { + .name = "max1363", + }, + .probe = max1363_probe, + .remove = __devexit_p(max1363_remove), + .id_table = max1363_id, +}; +module_i2c_driver(max1363_driver); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("Maxim 1363 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 4d348048b39d..0177f1e02300 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -130,24 +130,6 @@ config AD7280 To compile this driver as a module, choose M here: the module will be called ad7280a -config MAX1363 - tristate "Maxim max1363 ADC driver" - depends on I2C - select IIO_TRIGGER - select MAX1363_RING_BUFFER - select IIO_BUFFER - select IIO_KFIFO_BUF - help - Say yes here to build support for many Maxim i2c analog to digital - converters (ADC). (max1361, max1362, max1363, max1364, max1036, - max1037, max1038, max1039, max1136, max1136, max1137, max1138, - max1139, max1236, max1237, max11238, max1239, max11600, max11601, - max11602, max11603, max11604, max11605, max11606, max11607, - max11608, max11609, max11610, max11611, max11612, max11613, - max11614, max11615, max11616, max11617, max11644, max11645, - max11646, max11647) Provides direct access via sysfs and buffered - data via the iio dev interface. - config LPC32XX_ADC tristate "NXP LPC32XX ADC" depends on ARCH_LPC32XX diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 33979e62871b..12b4bd32437f 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -2,8 +2,6 @@ # Makefile for industrial I/O ADC drivers # -obj-$(CONFIG_MAX1363) += max1363.o - ad7606-y := ad7606_core.o ad7606-$(CONFIG_IIO_BUFFER) += ad7606_ring.o ad7606-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o diff --git a/drivers/staging/iio/adc/max1363.c b/drivers/staging/iio/adc/max1363.c deleted file mode 100644 index 1e84b5b55093..000000000000 --- a/drivers/staging/iio/adc/max1363.c +++ /dev/null @@ -1,1700 +0,0 @@ - /* - * iio/adc/max1363.c - * Copyright (C) 2008-2010 Jonathan Cameron - * - * based on linux/drivers/i2c/chips/max123x - * Copyright (C) 2002-2004 Stefan Eletzhofer - * - * based on linux/drivers/acron/char/pcf8583.c - * Copyright (C) 2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * max1363.c - * - * Partial support for max1363 and similar chips. - * - * Not currently implemented. - * - * - Control of internal reference. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define MAX1363_SETUP_BYTE(a) ((a) | 0x80) - -/* There is a fair bit more defined here than currently - * used, but the intention is to support everything these - * chips do in the long run */ - -/* see data sheets */ -/* max1363 and max1236, max1237, max1238, max1239 */ -#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD 0x00 -#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF 0x20 -#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT 0x40 -#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT 0x60 -#define MAX1363_SETUP_POWER_UP_INT_REF 0x10 -#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00 - -/* think about includeing max11600 etc - more settings */ -#define MAX1363_SETUP_EXT_CLOCK 0x08 -#define MAX1363_SETUP_INT_CLOCK 0x00 -#define MAX1363_SETUP_UNIPOLAR 0x00 -#define MAX1363_SETUP_BIPOLAR 0x04 -#define MAX1363_SETUP_RESET 0x00 -#define MAX1363_SETUP_NORESET 0x02 -/* max1363 only - though don't care on others. - * For now monitor modes are not implemented as the relevant - * line is not connected on my test board. - * The definitions are here as I intend to add this soon. - */ -#define MAX1363_SETUP_MONITOR_SETUP 0x01 - -/* Specific to the max1363 */ -#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4)) -#define MAX1363_MON_INT_ENABLE 0x01 - -/* defined for readability reasons */ -/* All chips */ -#define MAX1363_CONFIG_BYTE(a) ((a)) - -#define MAX1363_CONFIG_SE 0x01 -#define MAX1363_CONFIG_DE 0x00 -#define MAX1363_CONFIG_SCAN_TO_CS 0x00 -#define MAX1363_CONFIG_SCAN_SINGLE_8 0x20 -#define MAX1363_CONFIG_SCAN_MONITOR_MODE 0x40 -#define MAX1363_CONFIG_SCAN_SINGLE_1 0x60 -/* max123{6-9} only */ -#define MAX1236_SCAN_MID_TO_CHANNEL 0x40 - -/* max1363 only - merely part of channel selects or don't care for others*/ -#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18 - -#define MAX1363_CHANNEL_SEL(a) ((a) << 1) - -/* max1363 strictly 0x06 - but doesn't matter */ -#define MAX1363_CHANNEL_SEL_MASK 0x1E -#define MAX1363_SCAN_MASK 0x60 -#define MAX1363_SE_DE_MASK 0x01 - -#define MAX1363_MAX_CHANNELS 25 -/** - * struct max1363_mode - scan mode information - * @conf: The corresponding value of the configuration register - * @modemask: Bit mask corresponding to channels enabled in this mode - */ -struct max1363_mode { - int8_t conf; - DECLARE_BITMAP(modemask, MAX1363_MAX_CHANNELS); -}; - -/* This must be maintained along side the max1363_mode_table in max1363_core */ -enum max1363_modes { - /* Single read of a single channel */ - _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, - /* Differential single read */ - d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, - d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, - /* Scan to channel and mid to channel where overlapping */ - s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6, - s6to7, s0to7, s6to8, s0to8, s6to9, - s0to9, s6to10, s0to10, s6to11, s0to11, - /* Differential scan to channel and mid to channel where overlapping */ - d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9, - d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2, - d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8, - d7m6to11m10, d1m0to11m10, -}; - -/** - * struct max1363_chip_info - chip specifc information - * @info: iio core function callbacks structure - * @channels: channel specification - * @num_channels: number of channels - * @mode_list: array of available scan modes - * @default_mode: the scan mode in which the chip starts up - * @int_vref_mv: the internal reference voltage - * @num_channels: number of channels - * @bits: accuracy of the adc in bits - */ -struct max1363_chip_info { - const struct iio_info *info; - const struct iio_chan_spec *channels; - int num_channels; - const enum max1363_modes *mode_list; - enum max1363_modes default_mode; - u16 int_vref_mv; - u8 num_modes; - u8 bits; -}; - -/** - * struct max1363_state - driver instance specific data - * @client: i2c_client - * @setupbyte: cache of current device setup byte - * @configbyte: cache of current device config byte - * @chip_info: chip model specific constants, available modes etc - * @current_mode: the scan mode of this chip - * @requestedmask: a valid requested set of channels - * @reg: supply regulator - * @monitor_on: whether monitor mode is enabled - * @monitor_speed: parameter corresponding to device monitor speed setting - * @mask_high: bitmask for enabled high thresholds - * @mask_low: bitmask for enabled low thresholds - * @thresh_high: high threshold values - * @thresh_low: low threshold values - */ -struct max1363_state { - struct i2c_client *client; - u8 setupbyte; - u8 configbyte; - const struct max1363_chip_info *chip_info; - const struct max1363_mode *current_mode; - u32 requestedmask; - struct regulator *reg; - - /* Using monitor modes and buffer at the same time is - currently not supported */ - bool monitor_on; - unsigned int monitor_speed:3; - u8 mask_high; - u8 mask_low; - /* 4x unipolar first then the fours bipolar ones */ - s16 thresh_high[8]; - s16 thresh_low[8]; -}; - -#define MAX1363_MODE_SINGLE(_num, _mask) { \ - .conf = MAX1363_CHANNEL_SEL(_num) \ - | MAX1363_CONFIG_SCAN_SINGLE_1 \ - | MAX1363_CONFIG_SE, \ - .modemask[0] = _mask, \ - } - -#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \ - .conf = MAX1363_CHANNEL_SEL(_num) \ - | MAX1363_CONFIG_SCAN_TO_CS \ - | MAX1363_CONFIG_SE, \ - .modemask[0] = _mask, \ - } - -/* note not available for max1363 hence naming */ -#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \ - .conf = MAX1363_CHANNEL_SEL(_num) \ - | MAX1236_SCAN_MID_TO_CHANNEL \ - | MAX1363_CONFIG_SE, \ - .modemask[0] = _mask \ -} - -#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \ - .conf = MAX1363_CHANNEL_SEL(_nump) \ - | MAX1363_CONFIG_SCAN_SINGLE_1 \ - | MAX1363_CONFIG_DE, \ - .modemask[0] = _mask \ - } - -/* Can't think how to automate naming so specify for now */ -#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \ - .conf = MAX1363_CHANNEL_SEL(_num) \ - | MAX1363_CONFIG_SCAN_TO_CS \ - | MAX1363_CONFIG_DE, \ - .modemask[0] = _mask \ - } - -/* note only available for max1363 hence naming */ -#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \ - .conf = MAX1363_CHANNEL_SEL(_num) \ - | MAX1236_SCAN_MID_TO_CHANNEL \ - | MAX1363_CONFIG_SE, \ - .modemask[0] = _mask \ -} - -static const struct max1363_mode max1363_mode_table[] = { - /* All of the single channel options first */ - MAX1363_MODE_SINGLE(0, 1 << 0), - MAX1363_MODE_SINGLE(1, 1 << 1), - MAX1363_MODE_SINGLE(2, 1 << 2), - MAX1363_MODE_SINGLE(3, 1 << 3), - MAX1363_MODE_SINGLE(4, 1 << 4), - MAX1363_MODE_SINGLE(5, 1 << 5), - MAX1363_MODE_SINGLE(6, 1 << 6), - MAX1363_MODE_SINGLE(7, 1 << 7), - MAX1363_MODE_SINGLE(8, 1 << 8), - MAX1363_MODE_SINGLE(9, 1 << 9), - MAX1363_MODE_SINGLE(10, 1 << 10), - MAX1363_MODE_SINGLE(11, 1 << 11), - - MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12), - MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13), - MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14), - MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15), - MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16), - MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17), - MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18), - MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19), - MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20), - MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21), - MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22), - MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23), - - /* The multichannel scans next */ - MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003), - MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C), - MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F), - MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F), - MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F), - MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0), - MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0), - MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0), - MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0), - MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF), - MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0), - MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF), - - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000), - MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000), - MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000), -}; - -static const struct max1363_mode -*max1363_match_mode(const unsigned long *mask, -const struct max1363_chip_info *ci) -{ - int i; - if (mask) - for (i = 0; i < ci->num_modes; i++) - if (bitmap_subset(mask, - max1363_mode_table[ci->mode_list[i]]. - modemask, - MAX1363_MAX_CHANNELS)) - return &max1363_mode_table[ci->mode_list[i]]; - return NULL; -} - -static int max1363_write_basic_config(struct i2c_client *client, - unsigned char d1, - unsigned char d2) -{ - u8 tx_buf[2] = {d1, d2}; - - return i2c_master_send(client, tx_buf, 2); -} - -static int max1363_set_scan_mode(struct max1363_state *st) -{ - st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK - | MAX1363_SCAN_MASK - | MAX1363_SE_DE_MASK); - st->configbyte |= st->current_mode->conf; - - return max1363_write_basic_config(st->client, - st->setupbyte, - st->configbyte); -} - -static int max1363_read_single_chan(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - long m) -{ - int ret = 0; - s32 data; - char rxbuf[2]; - struct max1363_state *st = iio_priv(indio_dev); - struct i2c_client *client = st->client; - - mutex_lock(&indio_dev->mlock); - /* - * If monitor mode is enabled, the method for reading a single - * channel will have to be rather different and has not yet - * been implemented. - * - * Also, cannot read directly if buffered capture enabled. - */ - if (st->monitor_on || iio_buffer_enabled(indio_dev)) { - ret = -EBUSY; - goto error_ret; - } - - /* Check to see if current scan mode is correct */ - if (st->current_mode != &max1363_mode_table[chan->address]) { - /* Update scan mode if needed */ - st->current_mode = &max1363_mode_table[chan->address]; - ret = max1363_set_scan_mode(st); - if (ret < 0) - goto error_ret; - } - if (st->chip_info->bits != 8) { - /* Get reading */ - data = i2c_master_recv(client, rxbuf, 2); - if (data < 0) { - ret = data; - goto error_ret; - } - data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8; - } else { - /* Get reading */ - data = i2c_master_recv(client, rxbuf, 1); - if (data < 0) { - ret = data; - goto error_ret; - } - data = rxbuf[0]; - } - *val = data; -error_ret: - mutex_unlock(&indio_dev->mlock); - return ret; - -} - -static int max1363_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct max1363_state *st = iio_priv(indio_dev); - int ret; - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = max1363_read_single_chan(indio_dev, chan, val, m); - if (ret < 0) - return ret; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - if ((1 << (st->chip_info->bits + 1)) > - st->chip_info->int_vref_mv) { - *val = 0; - *val2 = 500000; - return IIO_VAL_INT_PLUS_MICRO; - } else { - *val = (st->chip_info->int_vref_mv) - >> st->chip_info->bits; - return IIO_VAL_INT; - } - default: - return -EINVAL; - } - return 0; -} - -/* Applies to max1363 */ -static const enum max1363_modes max1363_mode_list[] = { - _s0, _s1, _s2, _s3, - s0to1, s0to2, s0to3, - d0m1, d2m3, d1m0, d3m2, - d0m1to2m3, d1m0to3m2, -}; - -#define MAX1363_EV_M \ - (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) \ - | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)) -#define MAX1363_INFO_MASK (IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT) -#define MAX1363_CHAN_U(num, addr, si, bits, evmask) \ - { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .channel = num, \ - .address = addr, \ - .info_mask = MAX1363_INFO_MASK, \ - .datasheet_name = "AIN"#num, \ - .scan_type = { \ - .sign = 'u', \ - .realbits = bits, \ - .storagebits = (bits > 8) ? 16 : 8, \ - .endianness = IIO_BE, \ - }, \ - .scan_index = si, \ - .event_mask = evmask, \ - } - -/* bipolar channel */ -#define MAX1363_CHAN_B(num, num2, addr, si, bits, evmask) \ - { \ - .type = IIO_VOLTAGE, \ - .differential = 1, \ - .indexed = 1, \ - .channel = num, \ - .channel2 = num2, \ - .address = addr, \ - .info_mask = MAX1363_INFO_MASK, \ - .datasheet_name = "AIN"#num"-AIN"#num2, \ - .scan_type = { \ - .sign = 's', \ - .realbits = bits, \ - .storagebits = (bits > 8) ? 16 : 8, \ - .endianness = IIO_BE, \ - }, \ - .scan_index = si, \ - .event_mask = evmask, \ - } - -#define MAX1363_4X_CHANS(bits, em) { \ - MAX1363_CHAN_U(0, _s0, 0, bits, em), \ - MAX1363_CHAN_U(1, _s1, 1, bits, em), \ - MAX1363_CHAN_U(2, _s2, 2, bits, em), \ - MAX1363_CHAN_U(3, _s3, 3, bits, em), \ - MAX1363_CHAN_B(0, 1, d0m1, 4, bits, em), \ - MAX1363_CHAN_B(2, 3, d2m3, 5, bits, em), \ - MAX1363_CHAN_B(1, 0, d1m0, 6, bits, em), \ - MAX1363_CHAN_B(3, 2, d3m2, 7, bits, em), \ - IIO_CHAN_SOFT_TIMESTAMP(8) \ - } - -static const struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8, 0); -static const struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10, 0); -static const struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12, 0); -static const struct iio_chan_spec max1361_channels[] = - MAX1363_4X_CHANS(10, MAX1363_EV_M); -static const struct iio_chan_spec max1363_channels[] = - MAX1363_4X_CHANS(12, MAX1363_EV_M); - -/* Applies to max1236, max1237 */ -static const enum max1363_modes max1236_mode_list[] = { - _s0, _s1, _s2, _s3, - s0to1, s0to2, s0to3, - d0m1, d2m3, d1m0, d3m2, - d0m1to2m3, d1m0to3m2, - s2to3, -}; - -/* Applies to max1238, max1239 */ -static const enum max1363_modes max1238_mode_list[] = { - _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, - s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, - s0to7, s0to8, s0to9, s0to10, s0to11, - d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, - d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, - d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, - d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, - s6to7, s6to8, s6to9, s6to10, s6to11, - d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10, -}; - -#define MAX1363_12X_CHANS(bits) { \ - MAX1363_CHAN_U(0, _s0, 0, bits, 0), \ - MAX1363_CHAN_U(1, _s1, 1, bits, 0), \ - MAX1363_CHAN_U(2, _s2, 2, bits, 0), \ - MAX1363_CHAN_U(3, _s3, 3, bits, 0), \ - MAX1363_CHAN_U(4, _s4, 4, bits, 0), \ - MAX1363_CHAN_U(5, _s5, 5, bits, 0), \ - MAX1363_CHAN_U(6, _s6, 6, bits, 0), \ - MAX1363_CHAN_U(7, _s7, 7, bits, 0), \ - MAX1363_CHAN_U(8, _s8, 8, bits, 0), \ - MAX1363_CHAN_U(9, _s9, 9, bits, 0), \ - MAX1363_CHAN_U(10, _s10, 10, bits, 0), \ - MAX1363_CHAN_U(11, _s11, 11, bits, 0), \ - MAX1363_CHAN_B(0, 1, d0m1, 12, bits, 0), \ - MAX1363_CHAN_B(2, 3, d2m3, 13, bits, 0), \ - MAX1363_CHAN_B(4, 5, d4m5, 14, bits, 0), \ - MAX1363_CHAN_B(6, 7, d6m7, 15, bits, 0), \ - MAX1363_CHAN_B(8, 9, d8m9, 16, bits, 0), \ - MAX1363_CHAN_B(10, 11, d10m11, 17, bits, 0), \ - MAX1363_CHAN_B(1, 0, d1m0, 18, bits, 0), \ - MAX1363_CHAN_B(3, 2, d3m2, 19, bits, 0), \ - MAX1363_CHAN_B(5, 4, d5m4, 20, bits, 0), \ - MAX1363_CHAN_B(7, 6, d7m6, 21, bits, 0), \ - MAX1363_CHAN_B(9, 8, d9m8, 22, bits, 0), \ - MAX1363_CHAN_B(11, 10, d11m10, 23, bits, 0), \ - IIO_CHAN_SOFT_TIMESTAMP(24) \ - } -static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8); -static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10); -static const struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12); - -static const enum max1363_modes max11607_mode_list[] = { - _s0, _s1, _s2, _s3, - s0to1, s0to2, s0to3, - s2to3, - d0m1, d2m3, d1m0, d3m2, - d0m1to2m3, d1m0to3m2, -}; - -static const enum max1363_modes max11608_mode_list[] = { - _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, - s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7, - s6to7, - d0m1, d2m3, d4m5, d6m7, - d1m0, d3m2, d5m4, d7m6, - d0m1to2m3, d0m1to4m5, d0m1to6m7, - d1m0to3m2, d1m0to5m4, d1m0to7m6, -}; - -#define MAX1363_8X_CHANS(bits) { \ - MAX1363_CHAN_U(0, _s0, 0, bits, 0), \ - MAX1363_CHAN_U(1, _s1, 1, bits, 0), \ - MAX1363_CHAN_U(2, _s2, 2, bits, 0), \ - MAX1363_CHAN_U(3, _s3, 3, bits, 0), \ - MAX1363_CHAN_U(4, _s4, 4, bits, 0), \ - MAX1363_CHAN_U(5, _s5, 5, bits, 0), \ - MAX1363_CHAN_U(6, _s6, 6, bits, 0), \ - MAX1363_CHAN_U(7, _s7, 7, bits, 0), \ - MAX1363_CHAN_B(0, 1, d0m1, 8, bits, 0), \ - MAX1363_CHAN_B(2, 3, d2m3, 9, bits, 0), \ - MAX1363_CHAN_B(4, 5, d4m5, 10, bits, 0), \ - MAX1363_CHAN_B(6, 7, d6m7, 11, bits, 0), \ - MAX1363_CHAN_B(1, 0, d1m0, 12, bits, 0), \ - MAX1363_CHAN_B(3, 2, d3m2, 13, bits, 0), \ - MAX1363_CHAN_B(5, 4, d5m4, 14, bits, 0), \ - MAX1363_CHAN_B(7, 6, d7m6, 15, bits, 0), \ - IIO_CHAN_SOFT_TIMESTAMP(16) \ -} -static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8); -static const struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10); -static const struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12); - -static const enum max1363_modes max11644_mode_list[] = { - _s0, _s1, s0to1, d0m1, d1m0, -}; - -#define MAX1363_2X_CHANS(bits) { \ - MAX1363_CHAN_U(0, _s0, 0, bits, 0), \ - MAX1363_CHAN_U(1, _s1, 1, bits, 0), \ - MAX1363_CHAN_B(0, 1, d0m1, 2, bits, 0), \ - MAX1363_CHAN_B(1, 0, d1m0, 3, bits, 0), \ - IIO_CHAN_SOFT_TIMESTAMP(4) \ - } - -static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10); -static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12); - -enum { max1361, - max1362, - max1363, - max1364, - max1036, - max1037, - max1038, - max1039, - max1136, - max1137, - max1138, - max1139, - max1236, - max1237, - max1238, - max1239, - max11600, - max11601, - max11602, - max11603, - max11604, - max11605, - max11606, - max11607, - max11608, - max11609, - max11610, - max11611, - max11612, - max11613, - max11614, - max11615, - max11616, - max11617, - max11644, - max11645, - max11646, - max11647 -}; - -static const int max1363_monitor_speeds[] = { 133000, 665000, 33300, 16600, - 8300, 4200, 2000, 1000 }; - -static ssize_t max1363_monitor_show_freq(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct max1363_state *st = iio_priv(dev_to_iio_dev(dev)); - return sprintf(buf, "%d\n", max1363_monitor_speeds[st->monitor_speed]); -} - -static ssize_t max1363_monitor_store_freq(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct max1363_state *st = iio_priv(indio_dev); - int i, ret; - unsigned long val; - bool found = false; - - ret = strict_strtoul(buf, 10, &val); - if (ret) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++) - if (val == max1363_monitor_speeds[i]) { - found = true; - break; - } - if (!found) - return -EINVAL; - - mutex_lock(&indio_dev->mlock); - st->monitor_speed = i; - mutex_unlock(&indio_dev->mlock); - - return 0; -} - -static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, - max1363_monitor_show_freq, - max1363_monitor_store_freq); - -static IIO_CONST_ATTR(sampling_frequency_available, - "133000 665000 33300 16600 8300 4200 2000 1000"); - -static int max1363_read_thresh(struct iio_dev *indio_dev, - u64 event_code, - int *val) -{ - struct max1363_state *st = iio_priv(indio_dev); - if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) - *val = st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]; - else - *val = st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]; - return 0; -} - -static int max1363_write_thresh(struct iio_dev *indio_dev, - u64 event_code, - int val) -{ - struct max1363_state *st = iio_priv(indio_dev); - /* make it handle signed correctly as well */ - switch (st->chip_info->bits) { - case 10: - if (val > 0x3FF) - return -EINVAL; - break; - case 12: - if (val > 0xFFF) - return -EINVAL; - break; - } - - switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { - case IIO_EV_DIR_FALLING: - st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val; - break; - case IIO_EV_DIR_RISING: - st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val; - break; - } - - return 0; -} - -static const u64 max1363_event_codes[] = { - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, - IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, - IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, - IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, - IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, - IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, - IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, - IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, - IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), -}; - -static irqreturn_t max1363_event_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct max1363_state *st = iio_priv(indio_dev); - s64 timestamp = iio_get_time_ns(); - unsigned long mask, loc; - u8 rx; - u8 tx[2] = { st->setupbyte, - MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 }; - - i2c_master_recv(st->client, &rx, 1); - mask = rx; - for_each_set_bit(loc, &mask, 8) - iio_push_event(indio_dev, max1363_event_codes[loc], timestamp); - i2c_master_send(st->client, tx, 2); - - return IRQ_HANDLED; -} - -static int max1363_read_event_config(struct iio_dev *indio_dev, - u64 event_code) -{ - struct max1363_state *st = iio_priv(indio_dev); - int val; - int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); - - mutex_lock(&indio_dev->mlock); - if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) - val = (1 << number) & st->mask_low; - else - val = (1 << number) & st->mask_high; - mutex_unlock(&indio_dev->mlock); - - return val; -} - -static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) -{ - u8 *tx_buf; - int ret, i = 3, j; - unsigned long numelements; - int len; - const long *modemask; - - if (!enabled) { - /* transition to buffered capture is not currently supported */ - st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP; - st->configbyte &= ~MAX1363_SCAN_MASK; - st->monitor_on = false; - return max1363_write_basic_config(st->client, - st->setupbyte, - st->configbyte); - } - - /* Ensure we are in the relevant mode */ - st->setupbyte |= MAX1363_SETUP_MONITOR_SETUP; - st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK - | MAX1363_SCAN_MASK - | MAX1363_SE_DE_MASK); - st->configbyte |= MAX1363_CONFIG_SCAN_MONITOR_MODE; - if ((st->mask_low | st->mask_high) & 0x0F) { - st->configbyte |= max1363_mode_table[s0to3].conf; - modemask = max1363_mode_table[s0to3].modemask; - } else if ((st->mask_low | st->mask_high) & 0x30) { - st->configbyte |= max1363_mode_table[d0m1to2m3].conf; - modemask = max1363_mode_table[d0m1to2m3].modemask; - } else { - st->configbyte |= max1363_mode_table[d1m0to3m2].conf; - modemask = max1363_mode_table[d1m0to3m2].modemask; - } - numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS); - len = 3 * numelements + 3; - tx_buf = kmalloc(len, GFP_KERNEL); - if (!tx_buf) { - ret = -ENOMEM; - goto error_ret; - } - tx_buf[0] = st->configbyte; - tx_buf[1] = st->setupbyte; - tx_buf[2] = (st->monitor_speed << 1); - - /* - * So we need to do yet another bit of nefarious scan mode - * setup to match what we need. - */ - for (j = 0; j < 8; j++) - if (test_bit(j, modemask)) { - /* Establish the mode is in the scan */ - if (st->mask_low & (1 << j)) { - tx_buf[i] = (st->thresh_low[j] >> 4) & 0xFF; - tx_buf[i + 1] = (st->thresh_low[j] << 4) & 0xF0; - } else if (j < 4) { - tx_buf[i] = 0; - tx_buf[i + 1] = 0; - } else { - tx_buf[i] = 0x80; - tx_buf[i + 1] = 0; - } - if (st->mask_high & (1 << j)) { - tx_buf[i + 1] |= - (st->thresh_high[j] >> 8) & 0x0F; - tx_buf[i + 2] = st->thresh_high[j] & 0xFF; - } else if (j < 4) { - tx_buf[i + 1] |= 0x0F; - tx_buf[i + 2] = 0xFF; - } else { - tx_buf[i + 1] |= 0x07; - tx_buf[i + 2] = 0xFF; - } - i += 3; - } - - - ret = i2c_master_send(st->client, tx_buf, len); - if (ret < 0) - goto error_ret; - if (ret != len) { - ret = -EIO; - goto error_ret; - } - - /* - * Now that we hopefully have sensible thresholds in place it is - * time to turn the interrupts on. - * It is unclear from the data sheet if this should be necessary - * (i.e. whether monitor mode setup is atomic) but it appears to - * be in practice. - */ - tx_buf[0] = st->setupbyte; - tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0; - ret = i2c_master_send(st->client, tx_buf, 2); - if (ret < 0) - goto error_ret; - if (ret != 2) { - ret = -EIO; - goto error_ret; - } - ret = 0; - st->monitor_on = true; -error_ret: - - kfree(tx_buf); - - return ret; -} - -/* - * To keep this manageable we always use one of 3 scan modes. - * Scan 0...3, 0-1,2-3 and 1-0,3-2 - */ - -static inline int __max1363_check_event_mask(int thismask, int checkmask) -{ - int ret = 0; - /* Is it unipolar */ - if (thismask < 4) { - if (checkmask & ~0x0F) { - ret = -EBUSY; - goto error_ret; - } - } else if (thismask < 6) { - if (checkmask & ~0x30) { - ret = -EBUSY; - goto error_ret; - } - } else if (checkmask & ~0xC0) - ret = -EBUSY; -error_ret: - return ret; -} - -static int max1363_write_event_config(struct iio_dev *indio_dev, - u64 event_code, - int state) -{ - int ret = 0; - struct max1363_state *st = iio_priv(indio_dev); - u16 unifiedmask; - int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); - - mutex_lock(&indio_dev->mlock); - unifiedmask = st->mask_low | st->mask_high; - if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) { - - if (state == 0) - st->mask_low &= ~(1 << number); - else { - ret = __max1363_check_event_mask((1 << number), - unifiedmask); - if (ret) - goto error_ret; - st->mask_low |= (1 << number); - } - } else { - if (state == 0) - st->mask_high &= ~(1 << number); - else { - ret = __max1363_check_event_mask((1 << number), - unifiedmask); - if (ret) - goto error_ret; - st->mask_high |= (1 << number); - } - } - - max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); -error_ret: - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -/* - * As with scan_elements, only certain sets of these can - * be combined. - */ -static struct attribute *max1363_event_attributes[] = { - &iio_dev_attr_sampling_frequency.dev_attr.attr, - &iio_const_attr_sampling_frequency_available.dev_attr.attr, - NULL, -}; - -static struct attribute_group max1363_event_attribute_group = { - .attrs = max1363_event_attributes, - .name = "events", -}; - -static int max1363_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *scan_mask) -{ - struct max1363_state *st = iio_priv(indio_dev); - - /* - * Need to figure out the current mode based upon the requested - * scan mask in iio_dev - */ - st->current_mode = max1363_match_mode(scan_mask, st->chip_info); - if (!st->current_mode) - return -EINVAL; - max1363_set_scan_mode(st); - return 0; -} - -static const struct iio_info max1238_info = { - .read_raw = &max1363_read_raw, - .driver_module = THIS_MODULE, - .update_scan_mode = &max1363_update_scan_mode, -}; - -static const struct iio_info max1363_info = { - .read_event_value = &max1363_read_thresh, - .write_event_value = &max1363_write_thresh, - .read_event_config = &max1363_read_event_config, - .write_event_config = &max1363_write_event_config, - .read_raw = &max1363_read_raw, - .update_scan_mode = &max1363_update_scan_mode, - .driver_module = THIS_MODULE, - .event_attrs = &max1363_event_attribute_group, -}; - -/* max1363 and max1368 tested - rest from data sheet */ -static const struct max1363_chip_info max1363_chip_info_tbl[] = { - [max1361] = { - .bits = 10, - .int_vref_mv = 2048, - .mode_list = max1363_mode_list, - .num_modes = ARRAY_SIZE(max1363_mode_list), - .default_mode = s0to3, - .channels = max1361_channels, - .num_channels = ARRAY_SIZE(max1361_channels), - .info = &max1363_info, - }, - [max1362] = { - .bits = 10, - .int_vref_mv = 4096, - .mode_list = max1363_mode_list, - .num_modes = ARRAY_SIZE(max1363_mode_list), - .default_mode = s0to3, - .channels = max1361_channels, - .num_channels = ARRAY_SIZE(max1361_channels), - .info = &max1363_info, - }, - [max1363] = { - .bits = 12, - .int_vref_mv = 2048, - .mode_list = max1363_mode_list, - .num_modes = ARRAY_SIZE(max1363_mode_list), - .default_mode = s0to3, - .channels = max1363_channels, - .num_channels = ARRAY_SIZE(max1363_channels), - .info = &max1363_info, - }, - [max1364] = { - .bits = 12, - .int_vref_mv = 4096, - .mode_list = max1363_mode_list, - .num_modes = ARRAY_SIZE(max1363_mode_list), - .default_mode = s0to3, - .channels = max1363_channels, - .num_channels = ARRAY_SIZE(max1363_channels), - .info = &max1363_info, - }, - [max1036] = { - .bits = 8, - .int_vref_mv = 4096, - .mode_list = max1236_mode_list, - .num_modes = ARRAY_SIZE(max1236_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1036_channels, - .num_channels = ARRAY_SIZE(max1036_channels), - }, - [max1037] = { - .bits = 8, - .int_vref_mv = 2048, - .mode_list = max1236_mode_list, - .num_modes = ARRAY_SIZE(max1236_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1036_channels, - .num_channels = ARRAY_SIZE(max1036_channels), - }, - [max1038] = { - .bits = 8, - .int_vref_mv = 4096, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1038_channels, - .num_channels = ARRAY_SIZE(max1038_channels), - }, - [max1039] = { - .bits = 8, - .int_vref_mv = 2048, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1038_channels, - .num_channels = ARRAY_SIZE(max1038_channels), - }, - [max1136] = { - .bits = 10, - .int_vref_mv = 4096, - .mode_list = max1236_mode_list, - .num_modes = ARRAY_SIZE(max1236_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1136_channels, - .num_channels = ARRAY_SIZE(max1136_channels), - }, - [max1137] = { - .bits = 10, - .int_vref_mv = 2048, - .mode_list = max1236_mode_list, - .num_modes = ARRAY_SIZE(max1236_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1136_channels, - .num_channels = ARRAY_SIZE(max1136_channels), - }, - [max1138] = { - .bits = 10, - .int_vref_mv = 4096, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1138_channels, - .num_channels = ARRAY_SIZE(max1138_channels), - }, - [max1139] = { - .bits = 10, - .int_vref_mv = 2048, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1138_channels, - .num_channels = ARRAY_SIZE(max1138_channels), - }, - [max1236] = { - .bits = 12, - .int_vref_mv = 4096, - .mode_list = max1236_mode_list, - .num_modes = ARRAY_SIZE(max1236_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1236_channels, - .num_channels = ARRAY_SIZE(max1236_channels), - }, - [max1237] = { - .bits = 12, - .int_vref_mv = 2048, - .mode_list = max1236_mode_list, - .num_modes = ARRAY_SIZE(max1236_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1236_channels, - .num_channels = ARRAY_SIZE(max1236_channels), - }, - [max1238] = { - .bits = 12, - .int_vref_mv = 4096, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), - }, - [max1239] = { - .bits = 12, - .int_vref_mv = 2048, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), - }, - [max11600] = { - .bits = 8, - .int_vref_mv = 4096, - .mode_list = max11607_mode_list, - .num_modes = ARRAY_SIZE(max11607_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1036_channels, - .num_channels = ARRAY_SIZE(max1036_channels), - }, - [max11601] = { - .bits = 8, - .int_vref_mv = 2048, - .mode_list = max11607_mode_list, - .num_modes = ARRAY_SIZE(max11607_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1036_channels, - .num_channels = ARRAY_SIZE(max1036_channels), - }, - [max11602] = { - .bits = 8, - .int_vref_mv = 4096, - .mode_list = max11608_mode_list, - .num_modes = ARRAY_SIZE(max11608_mode_list), - .default_mode = s0to7, - .info = &max1238_info, - .channels = max11602_channels, - .num_channels = ARRAY_SIZE(max11602_channels), - }, - [max11603] = { - .bits = 8, - .int_vref_mv = 2048, - .mode_list = max11608_mode_list, - .num_modes = ARRAY_SIZE(max11608_mode_list), - .default_mode = s0to7, - .info = &max1238_info, - .channels = max11602_channels, - .num_channels = ARRAY_SIZE(max11602_channels), - }, - [max11604] = { - .bits = 8, - .int_vref_mv = 4098, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), - }, - [max11605] = { - .bits = 8, - .int_vref_mv = 2048, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), - }, - [max11606] = { - .bits = 10, - .int_vref_mv = 4096, - .mode_list = max11607_mode_list, - .num_modes = ARRAY_SIZE(max11607_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1136_channels, - .num_channels = ARRAY_SIZE(max1136_channels), - }, - [max11607] = { - .bits = 10, - .int_vref_mv = 2048, - .mode_list = max11607_mode_list, - .num_modes = ARRAY_SIZE(max11607_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1136_channels, - .num_channels = ARRAY_SIZE(max1136_channels), - }, - [max11608] = { - .bits = 10, - .int_vref_mv = 4096, - .mode_list = max11608_mode_list, - .num_modes = ARRAY_SIZE(max11608_mode_list), - .default_mode = s0to7, - .info = &max1238_info, - .channels = max11608_channels, - .num_channels = ARRAY_SIZE(max11608_channels), - }, - [max11609] = { - .bits = 10, - .int_vref_mv = 2048, - .mode_list = max11608_mode_list, - .num_modes = ARRAY_SIZE(max11608_mode_list), - .default_mode = s0to7, - .info = &max1238_info, - .channels = max11608_channels, - .num_channels = ARRAY_SIZE(max11608_channels), - }, - [max11610] = { - .bits = 10, - .int_vref_mv = 4098, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), - }, - [max11611] = { - .bits = 10, - .int_vref_mv = 2048, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), - }, - [max11612] = { - .bits = 12, - .int_vref_mv = 4096, - .mode_list = max11607_mode_list, - .num_modes = ARRAY_SIZE(max11607_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1363_channels, - .num_channels = ARRAY_SIZE(max1363_channels), - }, - [max11613] = { - .bits = 12, - .int_vref_mv = 2048, - .mode_list = max11607_mode_list, - .num_modes = ARRAY_SIZE(max11607_mode_list), - .default_mode = s0to3, - .info = &max1238_info, - .channels = max1363_channels, - .num_channels = ARRAY_SIZE(max1363_channels), - }, - [max11614] = { - .bits = 12, - .int_vref_mv = 4096, - .mode_list = max11608_mode_list, - .num_modes = ARRAY_SIZE(max11608_mode_list), - .default_mode = s0to7, - .info = &max1238_info, - .channels = max11614_channels, - .num_channels = ARRAY_SIZE(max11614_channels), - }, - [max11615] = { - .bits = 12, - .int_vref_mv = 2048, - .mode_list = max11608_mode_list, - .num_modes = ARRAY_SIZE(max11608_mode_list), - .default_mode = s0to7, - .info = &max1238_info, - .channels = max11614_channels, - .num_channels = ARRAY_SIZE(max11614_channels), - }, - [max11616] = { - .bits = 12, - .int_vref_mv = 4098, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), - }, - [max11617] = { - .bits = 12, - .int_vref_mv = 2048, - .mode_list = max1238_mode_list, - .num_modes = ARRAY_SIZE(max1238_mode_list), - .default_mode = s0to11, - .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), - }, - [max11644] = { - .bits = 12, - .int_vref_mv = 2048, - .mode_list = max11644_mode_list, - .num_modes = ARRAY_SIZE(max11644_mode_list), - .default_mode = s0to1, - .info = &max1238_info, - .channels = max11644_channels, - .num_channels = ARRAY_SIZE(max11644_channels), - }, - [max11645] = { - .bits = 12, - .int_vref_mv = 4096, - .mode_list = max11644_mode_list, - .num_modes = ARRAY_SIZE(max11644_mode_list), - .default_mode = s0to1, - .info = &max1238_info, - .channels = max11644_channels, - .num_channels = ARRAY_SIZE(max11644_channels), - }, - [max11646] = { - .bits = 10, - .int_vref_mv = 2048, - .mode_list = max11644_mode_list, - .num_modes = ARRAY_SIZE(max11644_mode_list), - .default_mode = s0to1, - .info = &max1238_info, - .channels = max11646_channels, - .num_channels = ARRAY_SIZE(max11646_channels), - }, - [max11647] = { - .bits = 10, - .int_vref_mv = 4096, - .mode_list = max11644_mode_list, - .num_modes = ARRAY_SIZE(max11644_mode_list), - .default_mode = s0to1, - .info = &max1238_info, - .channels = max11646_channels, - .num_channels = ARRAY_SIZE(max11646_channels), - }, -}; - -static int max1363_initial_setup(struct max1363_state *st) -{ - st->setupbyte = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD - | MAX1363_SETUP_POWER_UP_INT_REF - | MAX1363_SETUP_INT_CLOCK - | MAX1363_SETUP_UNIPOLAR - | MAX1363_SETUP_NORESET; - - /* Set scan mode writes the config anyway so wait until then*/ - st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte); - st->current_mode = &max1363_mode_table[st->chip_info->default_mode]; - st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte); - - return max1363_set_scan_mode(st); -} - -static int __devinit max1363_alloc_scan_masks(struct iio_dev *indio_dev) -{ - struct max1363_state *st = iio_priv(indio_dev); - unsigned long *masks; - int i; - - masks = kzalloc(BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*sizeof(long)* - (st->chip_info->num_modes + 1), GFP_KERNEL); - if (!masks) - return -ENOMEM; - - for (i = 0; i < st->chip_info->num_modes; i++) - bitmap_copy(masks + BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i, - max1363_mode_table[st->chip_info->mode_list[i]] - .modemask, MAX1363_MAX_CHANNELS); - - indio_dev->available_scan_masks = masks; - - return 0; -} - - -static irqreturn_t max1363_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct max1363_state *st = iio_priv(indio_dev); - s64 time_ns; - __u8 *rxbuf; - int b_sent; - size_t d_size; - unsigned long numvals = bitmap_weight(st->current_mode->modemask, - MAX1363_MAX_CHANNELS); - - /* Ensure the timestamp is 8 byte aligned */ - if (st->chip_info->bits != 8) - d_size = numvals*2; - else - d_size = numvals; - if (indio_dev->scan_timestamp) { - d_size += sizeof(s64); - if (d_size % sizeof(s64)) - d_size += sizeof(s64) - (d_size % sizeof(s64)); - } - /* Monitor mode prevents reading. Whilst not currently implemented - * might as well have this test in here in the meantime as it does - * no harm. - */ - if (numvals == 0) - goto done; - - rxbuf = kmalloc(d_size, GFP_KERNEL); - if (rxbuf == NULL) - goto done; - if (st->chip_info->bits != 8) - b_sent = i2c_master_recv(st->client, rxbuf, numvals*2); - else - b_sent = i2c_master_recv(st->client, rxbuf, numvals); - if (b_sent < 0) - goto done_free; - - time_ns = iio_get_time_ns(); - - if (indio_dev->scan_timestamp) - memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); - iio_push_to_buffers(indio_dev, rxbuf); - -done_free: - kfree(rxbuf); -done: - iio_trigger_notify_done(indio_dev->trig); - - return IRQ_HANDLED; -} - -static const struct iio_buffer_setup_ops max1363_buffered_setup_ops = { - .postenable = &iio_triggered_buffer_postenable, - .preenable = &iio_sw_buffer_preenable, - .predisable = &iio_triggered_buffer_predisable, -}; - -static int max1363_register_buffered_funcs_and_init(struct iio_dev *indio_dev) -{ - struct max1363_state *st = iio_priv(indio_dev); - int ret = 0; - - indio_dev->buffer = iio_kfifo_allocate(indio_dev); - if (!indio_dev->buffer) { - ret = -ENOMEM; - goto error_ret; - } - indio_dev->pollfunc = iio_alloc_pollfunc(NULL, - &max1363_trigger_handler, - IRQF_ONESHOT, - indio_dev, - "%s_consumer%d", - st->client->name, - indio_dev->id); - if (indio_dev->pollfunc == NULL) { - ret = -ENOMEM; - goto error_deallocate_sw_rb; - } - /* Buffer functions - here trigger setup related */ - indio_dev->setup_ops = &max1363_buffered_setup_ops; - - /* Flag that polled buffering is possible */ - indio_dev->modes |= INDIO_BUFFER_TRIGGERED; - - return 0; - -error_deallocate_sw_rb: - iio_kfifo_free(indio_dev->buffer); -error_ret: - return ret; -} - -static void max1363_buffer_cleanup(struct iio_dev *indio_dev) -{ - /* ensure that the trigger has been detached */ - iio_dealloc_pollfunc(indio_dev->pollfunc); - iio_kfifo_free(indio_dev->buffer); -} - -static int __devinit max1363_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int ret; - struct max1363_state *st; - struct iio_dev *indio_dev; - - indio_dev = iio_device_alloc(sizeof(struct max1363_state)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_out; - } - - ret = iio_map_array_register(indio_dev, client->dev.platform_data); - if (ret < 0) - goto error_free_device; - - st = iio_priv(indio_dev); - - st->reg = regulator_get(&client->dev, "vcc"); - if (IS_ERR(st->reg)) { - ret = PTR_ERR(st->reg); - goto error_unregister_map; - } - - ret = regulator_enable(st->reg); - if (ret) - goto error_put_reg; - - /* this is only used for device removal purposes */ - i2c_set_clientdata(client, indio_dev); - - st->chip_info = &max1363_chip_info_tbl[id->driver_data]; - st->client = client; - - ret = max1363_alloc_scan_masks(indio_dev); - if (ret) - goto error_disable_reg; - - /* Estabilish that the iio_dev is a child of the i2c device */ - indio_dev->dev.parent = &client->dev; - indio_dev->name = id->name; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = st->chip_info->num_channels; - indio_dev->info = st->chip_info->info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = st->chip_info->num_channels; - ret = max1363_initial_setup(st); - if (ret < 0) - goto error_free_available_scan_masks; - - ret = max1363_register_buffered_funcs_and_init(indio_dev); - if (ret) - goto error_free_available_scan_masks; - - ret = iio_buffer_register(indio_dev, - st->chip_info->channels, - st->chip_info->num_channels); - if (ret) - goto error_cleanup_buffer; - - if (client->irq) { - ret = request_threaded_irq(st->client->irq, - NULL, - &max1363_event_handler, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "max1363_event", - indio_dev); - - if (ret) - goto error_uninit_buffer; - } - - ret = iio_device_register(indio_dev); - if (ret < 0) - goto error_free_irq; - - return 0; -error_free_irq: - free_irq(st->client->irq, indio_dev); -error_uninit_buffer: - iio_buffer_unregister(indio_dev); -error_cleanup_buffer: - max1363_buffer_cleanup(indio_dev); -error_free_available_scan_masks: - kfree(indio_dev->available_scan_masks); -error_unregister_map: - iio_map_array_unregister(indio_dev, client->dev.platform_data); -error_disable_reg: - regulator_disable(st->reg); -error_put_reg: - regulator_put(st->reg); -error_free_device: - iio_device_free(indio_dev); -error_out: - return ret; -} - -static int __devexit max1363_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct max1363_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (client->irq) - free_irq(st->client->irq, indio_dev); - iio_buffer_unregister(indio_dev); - max1363_buffer_cleanup(indio_dev); - kfree(indio_dev->available_scan_masks); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_map_array_unregister(indio_dev, client->dev.platform_data); - iio_device_free(indio_dev); - - return 0; -} - -static const struct i2c_device_id max1363_id[] = { - { "max1361", max1361 }, - { "max1362", max1362 }, - { "max1363", max1363 }, - { "max1364", max1364 }, - { "max1036", max1036 }, - { "max1037", max1037 }, - { "max1038", max1038 }, - { "max1039", max1039 }, - { "max1136", max1136 }, - { "max1137", max1137 }, - { "max1138", max1138 }, - { "max1139", max1139 }, - { "max1236", max1236 }, - { "max1237", max1237 }, - { "max1238", max1238 }, - { "max1239", max1239 }, - { "max11600", max11600 }, - { "max11601", max11601 }, - { "max11602", max11602 }, - { "max11603", max11603 }, - { "max11604", max11604 }, - { "max11605", max11605 }, - { "max11606", max11606 }, - { "max11607", max11607 }, - { "max11608", max11608 }, - { "max11609", max11609 }, - { "max11610", max11610 }, - { "max11611", max11611 }, - { "max11612", max11612 }, - { "max11613", max11613 }, - { "max11614", max11614 }, - { "max11615", max11615 }, - { "max11616", max11616 }, - { "max11617", max11617 }, - {} -}; - -MODULE_DEVICE_TABLE(i2c, max1363_id); - -static struct i2c_driver max1363_driver = { - .driver = { - .name = "max1363", - }, - .probe = max1363_probe, - .remove = __devexit_p(max1363_remove), - .id_table = max1363_id, -}; -module_i2c_driver(max1363_driver); - -MODULE_AUTHOR("Jonathan Cameron "); -MODULE_DESCRIPTION("Maxim 1363 ADC"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 69bcd3bf40228239db91b075f56f9ba511f0bfe1 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Mon, 22 Oct 2012 08:57:00 +0100 Subject: iio: hid-sensors: convert HID_SENSOR_ENUM_BASE_QUIRKS to bool It's non-sense to use tristate for the option, it's bool. Signed-off-by: Kirill A. Shutemov Signed-off-by: Jonathan Cameron --- drivers/iio/common/hid-sensors/Kconfig | 2 +- drivers/iio/common/hid-sensors/hid-sensor-trigger.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig index 8e63d81d652a..ae10778da7aa 100644 --- a/drivers/iio/common/hid-sensors/Kconfig +++ b/drivers/iio/common/hid-sensors/Kconfig @@ -15,7 +15,7 @@ config HID_SENSOR_IIO_COMMON attributes. config HID_SENSOR_ENUM_BASE_QUIRKS - tristate "ENUM base quirks for HID Sensor IIO drivers" + bool "ENUM base quirks for HID Sensor IIO drivers" depends on HID_SENSOR_IIO_COMMON help Say yes here to build support for sensor hub FW using diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index d4b790d18efb..d60198a6ca29 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -36,10 +36,8 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, int state_val; state_val = state ? 1 : 0; -#if (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS) || \ - (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS_MODULE) - ++state_val; -#endif + if (IS_ENABLED(CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS)) + ++state_val; st->data_ready = state; sensor_hub_set_feature(st->hsdev, st->power_state.report_id, st->power_state.index, -- cgit v1.2.3 From e3db9ef6eb39ac6d969787bc15756778c2c5ca66 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 21 Oct 2012 11:52:00 +0100 Subject: drivers/iio/industrialio-event.c: eliminate possible double free The function __iio_add_event_config_attrs is only called once, by the function iio_device_register_eventset. If the call fails, iio_device_register_eventset calls __iio_remove_event_config_attrs. There is thus no need for __iio_add_event_config_attrs to also call __iio_remove_event_config_attrs on failure. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r@ identifier f,free,a; parameter list[n] ps; type T; expression e; @@ f(ps,T a,...) { ... when any when != a = e if(...) { ... free(a); ... return ...; } ... when any } @@ identifier r.f,r.free; expression x,a; expression list[r.n] xs; @@ * x = f(xs,a,...); if (...) { ... free(a); ... return ...; } // Signed-off-by: Julia Lawall Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-event.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 857e6306c5c3..261cae00557e 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -350,15 +350,10 @@ static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev) ret = iio_device_add_event_sysfs(indio_dev, &indio_dev->channels[j]); if (ret < 0) - goto error_clear_attrs; + return ret; attrcount += ret; } return attrcount; - -error_clear_attrs: - __iio_remove_event_config_attrs(indio_dev); - - return ret; } static bool iio_check_for_dynamic_events(struct iio_dev *indio_dev) -- cgit v1.2.3 From 64ebe955f9447fcecf65360ec3bc332f4ec411b6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 27 Oct 2012 16:03:00 +0100 Subject: iio: hid-sensor: Return proper error if kmemdup fails Return -ENOMEM instead of 0 if kmemdup fails. Signed-off-by: Axel Lin Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 6 +++--- drivers/iio/gyro/hid-sensor-gyro-3d.c | 6 +++--- drivers/iio/light/hid-sensor-als.c | 5 ++--- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 6 +++--- 4 files changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index a95cda0e387f..e67bb912bd19 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -306,10 +306,10 @@ static int __devinit hid_accel_3d_probe(struct platform_device *pdev) goto error_free_dev; } - channels = kmemdup(accel_3d_channels, - sizeof(accel_3d_channels), - GFP_KERNEL); + channels = kmemdup(accel_3d_channels, sizeof(accel_3d_channels), + GFP_KERNEL); if (!channels) { + ret = -ENOMEM; dev_err(&pdev->dev, "failed to duplicate channels\n"); goto error_free_dev; } diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 02ef989b830d..4c8b158e40e1 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -306,10 +306,10 @@ static int __devinit hid_gyro_3d_probe(struct platform_device *pdev) goto error_free_dev; } - channels = kmemdup(gyro_3d_channels, - sizeof(gyro_3d_channels), - GFP_KERNEL); + channels = kmemdup(gyro_3d_channels, sizeof(gyro_3d_channels), + GFP_KERNEL); if (!channels) { + ret = -ENOMEM; dev_err(&pdev->dev, "failed to duplicate channels\n"); goto error_free_dev; } diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 8e1f69844eea..23eeeef64e84 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -272,10 +272,9 @@ static int __devinit hid_als_probe(struct platform_device *pdev) goto error_free_dev; } - channels = kmemdup(als_channels, - sizeof(als_channels), - GFP_KERNEL); + channels = kmemdup(als_channels, sizeof(als_channels), GFP_KERNEL); if (!channels) { + ret = -ENOMEM; dev_err(&pdev->dev, "failed to duplicate channels\n"); goto error_free_dev; } diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index d1b5fb74b9bf..8e75eb76ccd9 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -307,10 +307,10 @@ static int __devinit hid_magn_3d_probe(struct platform_device *pdev) goto error_free_dev; } - channels = kmemdup(magn_3d_channels, - sizeof(magn_3d_channels), - GFP_KERNEL); + channels = kmemdup(magn_3d_channels, sizeof(magn_3d_channels), + GFP_KERNEL); if (!channels) { + ret = -ENOMEM; dev_err(&pdev->dev, "failed to duplicate channels\n"); goto error_free_dev; } -- cgit v1.2.3 From ec04cb048d79cd778c06e28f34395a46d774800d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 13 Nov 2012 13:28:00 +0000 Subject: staging:iio: Move adis library out of staging Now that the adis library no longer depends on the sw_ring buffer implementation we can move it out of staging. While we are at it also sort the entries in the iio Kconfig and Makefile to be in alphabetical order. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 7 +- drivers/iio/Makefile | 7 +- drivers/iio/imu/Kconfig | 11 + drivers/iio/imu/Makefile | 8 + drivers/iio/imu/adis.c | 337 ++++++++++++++++++++++++++++ drivers/iio/imu/adis_buffer.c | 159 ++++++++++++++ drivers/iio/imu/adis_trigger.c | 89 ++++++++ drivers/staging/iio/accel/adis16201_core.c | 2 +- drivers/staging/iio/accel/adis16203_core.c | 2 +- drivers/staging/iio/accel/adis16204_core.c | 2 +- drivers/staging/iio/accel/adis16209_core.c | 2 +- drivers/staging/iio/accel/adis16220.h | 2 +- drivers/staging/iio/accel/adis16240_core.c | 2 +- drivers/staging/iio/gyro/adis16260.h | 2 +- drivers/staging/iio/imu/Kconfig | 12 - drivers/staging/iio/imu/Makefile | 5 - drivers/staging/iio/imu/adis.c | 338 ----------------------------- drivers/staging/iio/imu/adis.h | 186 ---------------- drivers/staging/iio/imu/adis_buffer.c | 160 -------------- drivers/staging/iio/imu/adis_trigger.c | 90 -------- include/linux/iio/imu/adis.h | 186 ++++++++++++++++ 21 files changed, 805 insertions(+), 804 deletions(-) create mode 100644 drivers/iio/imu/Kconfig create mode 100644 drivers/iio/imu/Makefile create mode 100644 drivers/iio/imu/adis.c create mode 100644 drivers/iio/imu/adis_buffer.c create mode 100644 drivers/iio/imu/adis_trigger.c delete mode 100644 drivers/staging/iio/imu/adis.c delete mode 100644 drivers/staging/iio/imu/adis.h delete mode 100644 drivers/staging/iio/imu/adis_buffer.c delete mode 100644 drivers/staging/iio/imu/adis_trigger.c create mode 100644 include/linux/iio/imu/adis.h (limited to 'drivers/iio') diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 65ae734c607d..b2f963be3993 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -63,11 +63,12 @@ config IIO_CONSUMERS_PER_TRIGGER source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" source "drivers/iio/amplifiers/Kconfig" -source "drivers/iio/light/Kconfig" -source "drivers/iio/frequency/Kconfig" -source "drivers/iio/dac/Kconfig" source "drivers/iio/common/Kconfig" +source "drivers/iio/dac/Kconfig" +source "drivers/iio/frequency/Kconfig" source "drivers/iio/gyro/Kconfig" +source "drivers/iio/imu/Kconfig" +source "drivers/iio/light/Kconfig" source "drivers/iio/magnetometer/Kconfig" endif # IIO diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 31d76a07ec65..a0e8cdd67e4d 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -14,9 +14,10 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o obj-y += accel/ obj-y += adc/ obj-y += amplifiers/ -obj-y += light/ -obj-y += frequency/ -obj-y += dac/ obj-y += common/ +obj-y += dac/ obj-y += gyro/ +obj-y += frequency/ +obj-y += imu/ +obj-y += light/ obj-y += magnetometer/ diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig new file mode 100644 index 000000000000..c24410c873c7 --- /dev/null +++ b/drivers/iio/imu/Kconfig @@ -0,0 +1,11 @@ +config IIO_ADIS_LIB + tristate + help + A set of IO helper functions for the Analog Devices ADIS* device family. + +config IIO_ADIS_LIB_BUFFER + bool + select IIO_TRIGGERED_BUFFER + help + A set of buffer helper functions for the Analog Devices ADIS* device + family. diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile new file mode 100644 index 000000000000..97676ab5723d --- /dev/null +++ b/drivers/iio/imu/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for Inertial Measurement Units +# + +adis_lib-y += adis.o +adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o +adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o +obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c new file mode 100644 index 000000000000..8259b774078f --- /dev/null +++ b/drivers/iio/imu/adis.c @@ -0,0 +1,337 @@ +/* + * Common library for ADIS16XXX devices + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define ADIS_MSC_CTRL_DATA_RDY_EN BIT(2) +#define ADIS_MSC_CTRL_DATA_RDY_POL_HIGH BIT(1) +#define ADIS_MSC_CTRL_DATA_RDY_DIO2 BIT(0) +#define ADIS_GLOB_CMD_SW_RESET BIT(7) + +/** + * adis_write_reg_8() - Write single byte to a register + * @adis: The adis device + * @reg: The address of the register to be written + * @val: The value to write + */ +int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val) +{ + int ret; + + mutex_lock(&adis->txrx_lock); + adis->tx[0] = ADIS_WRITE_REG(reg); + adis->tx[1] = val; + + ret = spi_write(adis->spi, adis->tx, 2); + mutex_unlock(&adis->txrx_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(adis_write_reg_8); + +/** + * adis_write_reg_16() - Write 2 bytes to a pair of registers + * @adis: The adis device + * @reg: The address of the lower of the two registers + * @val: Value to be written + */ +int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t value) +{ + int ret; + struct spi_message msg; + struct spi_transfer xfers[] = { + { + .tx_buf = adis->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = adis->data->write_delay, + }, { + .tx_buf = adis->tx + 2, + .bits_per_word = 8, + .len = 2, + .delay_usecs = adis->data->write_delay, + }, + }; + + mutex_lock(&adis->txrx_lock); + adis->tx[0] = ADIS_WRITE_REG(reg); + adis->tx[1] = value & 0xff; + adis->tx[2] = ADIS_WRITE_REG(reg + 1); + adis->tx[3] = (value >> 8) & 0xff; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(adis->spi, &msg); + mutex_unlock(&adis->txrx_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(adis_write_reg_16); + +/** + * adis_read_reg_16() - read 2 bytes from a 16-bit register + * @adis: The adis device + * @reg: The address of the lower of the two registers + * @val: The value read back from the device + */ +int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val) +{ + struct spi_message msg; + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = adis->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = adis->data->read_delay, + }, { + .rx_buf = adis->rx, + .bits_per_word = 8, + .len = 2, + .delay_usecs = adis->data->read_delay, + }, + }; + + mutex_lock(&adis->txrx_lock); + adis->tx[0] = ADIS_READ_REG(reg); + adis->tx[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(adis->spi, &msg); + if (ret) { + dev_err(&adis->spi->dev, "Failed to read 16 bit register 0x%02X: %d\n", + reg, ret); + goto error_ret; + } + *val = get_unaligned_be16(adis->rx); + +error_ret: + mutex_unlock(&adis->txrx_lock); + return ret; +} +EXPORT_SYMBOL_GPL(adis_read_reg_16); + +/** + * adis_enable_irq() - Enable or disable data ready IRQ + * @adis: The adis device + * @enable: Whether to enable the IRQ + * + * Returns 0 on success, negative error code otherwise + */ +int adis_enable_irq(struct adis *adis, bool enable) +{ + int ret = 0; + uint16_t msc; + + ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); + if (ret) + goto error_ret; + + msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH; + msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2; + if (enable) + msc |= ADIS_MSC_CTRL_DATA_RDY_EN; + else + msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN; + + ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc); + +error_ret: + return ret; +} +EXPORT_SYMBOL(adis_enable_irq); + +/** + * adis_check_status() - Check the device for error conditions + * @adis: The adis device + * + * Returns 0 on success, a negative error code otherwise + */ +int adis_check_status(struct adis *adis) +{ + uint16_t status; + int ret; + int i; + + ret = adis_read_reg_16(adis, adis->data->diag_stat_reg, &status); + if (ret < 0) + return ret; + + status &= adis->data->status_error_mask; + + if (status == 0) + return 0; + + for (i = 0; i < 16; ++i) { + if (status & BIT(i)) { + dev_err(&adis->spi->dev, "%s.\n", + adis->data->status_error_msgs[i]); + } + } + + return -EIO; +} +EXPORT_SYMBOL_GPL(adis_check_status); + +/** + * adis_reset() - Reset the device + * @adis: The adis device + * + * Returns 0 on success, a negative error code otherwise + */ +int adis_reset(struct adis *adis) +{ + int ret; + + ret = adis_write_reg_8(adis, adis->data->glob_cmd_reg, + ADIS_GLOB_CMD_SW_RESET); + if (ret) + dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(adis_reset); + +static int adis_self_test(struct adis *adis) +{ + int ret; + + ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, + adis->data->self_test_mask); + if (ret) { + dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n", + ret); + return ret; + } + + msleep(adis->data->startup_delay); + + return adis_check_status(adis); +} + +/** + * adis_inital_startup() - Performs device self-test + * @adis: The adis device + * + * Returns 0 if the device is operational, a negative error code otherwise. + * + * This function should be called early on in the device initialization sequence + * to ensure that the device is in a sane and known state and that it is usable. + */ +int adis_initial_startup(struct adis *adis) +{ + int ret; + + ret = adis_self_test(adis); + if (ret) { + dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n"); + adis_reset(adis); + msleep(adis->data->startup_delay); + ret = adis_self_test(adis); + if (ret) { + dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(adis_initial_startup); + +/** + * adis_single_conversion() - Performs a single sample conversion + * @indio_dev: The IIO device + * @chan: The IIO channel + * @error_mask: Mask for the error bit + * @val: Result of the conversion + * + * Returns IIO_VAL_INT on success, a negative error code otherwise. + * + * The function performs a single conversion on a given channel and post + * processes the value accordingly to the channel spec. If a error_mask is given + * the function will check if the mask is set in the returned raw value. If it + * is set the function will perform a self-check. If the device does not report + * a error bit in the channels raw value set error_mask to 0. + */ +int adis_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int error_mask, int *val) +{ + struct adis *adis = iio_device_get_drvdata(indio_dev); + uint16_t val16; + int ret; + + mutex_lock(&indio_dev->mlock); + + ret = adis_read_reg_16(adis, chan->address, &val16); + if (ret) + goto err_unlock; + + if (val16 & error_mask) { + ret = adis_check_status(adis); + if (ret) + goto err_unlock; + } + + if (chan->scan_type.sign == 's') + *val = sign_extend32(val16, chan->scan_type.realbits - 1); + else + *val = val16 & ((1 << chan->scan_type.realbits) - 1); + + ret = IIO_VAL_INT; +err_unlock: + mutex_unlock(&indio_dev->mlock); + return ret; +} +EXPORT_SYMBOL_GPL(adis_single_conversion); + +/** + * adis_init() - Initialize adis device structure + * @adis: The adis device + * @indio_dev: The iio device + * @spi: The spi device + * @data: Chip specific data + * + * Returns 0 on success, a negative error code otherwise. + * + * This function must be called, before any other adis helper function may be + * called. + */ +int adis_init(struct adis *adis, struct iio_dev *indio_dev, + struct spi_device *spi, const struct adis_data *data) +{ + mutex_init(&adis->txrx_lock); + adis->spi = spi; + adis->data = data; + iio_device_set_drvdata(indio_dev, adis); + + return adis_enable_irq(adis, false); +} +EXPORT_SYMBOL_GPL(adis_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Common library code for ADIS16XXX devices"); diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c new file mode 100644 index 000000000000..a91b4cbdc73a --- /dev/null +++ b/drivers/iio/imu/adis_buffer.c @@ -0,0 +1,159 @@ +/* + * Common library for ADIS16XXX devices + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int adis_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct adis *adis = iio_device_get_drvdata(indio_dev); + const struct iio_chan_spec *chan; + unsigned int scan_count; + unsigned int i, j; + __be16 *tx, *rx; + + kfree(adis->xfer); + kfree(adis->buffer); + + scan_count = indio_dev->scan_bytes / 2; + + adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL); + if (!adis->xfer) + return -ENOMEM; + + adis->buffer = kzalloc(indio_dev->scan_bytes * 2, GFP_KERNEL); + if (!adis->buffer) + return -ENOMEM; + + rx = adis->buffer; + tx = rx + indio_dev->scan_bytes; + + spi_message_init(&adis->msg); + + for (j = 0; j <= scan_count; j++) { + adis->xfer[j].bits_per_word = 8; + if (j != scan_count) + adis->xfer[j].cs_change = 1; + adis->xfer[j].len = 2; + adis->xfer[j].delay_usecs = adis->data->read_delay; + if (j < scan_count) + adis->xfer[j].tx_buf = &tx[j]; + if (j >= 1) + adis->xfer[j].rx_buf = &rx[j - 1]; + spi_message_add_tail(&adis->xfer[j], &adis->msg); + } + + chan = indio_dev->channels; + for (i = 0; i < indio_dev->num_channels; i++, chan++) { + if (!test_bit(chan->scan_index, scan_mask)) + continue; + *tx++ = cpu_to_be16(chan->address << 8); + } + + return 0; +} +EXPORT_SYMBOL_GPL(adis_update_scan_mode); + +static irqreturn_t adis_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adis *adis = iio_device_get_drvdata(indio_dev); + int ret; + + if (!adis->buffer) + return -ENOMEM; + + ret = spi_sync(adis->spi, &adis->msg); + if (ret) + dev_err(&adis->spi->dev, "Failed to read data: %d", ret); + + /* Guaranteed to be aligned with 8 byte boundary */ + if (indio_dev->scan_timestamp) { + void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64); + *(s64 *)b = pf->timestamp; + } + + iio_push_to_buffers(indio_dev, adis->buffer); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +/** + * adis_setup_buffer_and_trigger() - Sets up buffer and trigger for the adis device + * @adis: The adis device. + * @indio_dev: The IIO device. + * @trigger_handler: Optional trigger handler, may be NULL. + * + * Returns 0 on success, a negative error code otherwise. + * + * This function sets up the buffer and trigger for a adis devices. If + * 'trigger_handler' is NULL the default trigger handler will be used. The + * default trigger handler will simply read the registers assigned to the + * currently active channels. + * + * adis_cleanup_buffer_and_trigger() should be called to free the resources + * allocated by this function. + */ +int adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev, + irqreturn_t (*trigger_handler)(int, void *)) +{ + int ret; + + if (!trigger_handler) + trigger_handler = adis_trigger_handler; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + trigger_handler, NULL); + if (ret) + return ret; + + if (adis->spi->irq) { + ret = adis_probe_trigger(adis, indio_dev); + if (ret) + goto error_buffer_cleanup; + } + return 0; + +error_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); + return ret; +} +EXPORT_SYMBOL_GPL(adis_setup_buffer_and_trigger); + +/** + * adis_cleanup_buffer_and_trigger() - Free buffer and trigger resources + * @adis: The adis device. + * @indio_dev: The IIO device. + * + * Frees resources allocated by adis_setup_buffer_and_trigger() + */ +void adis_cleanup_buffer_and_trigger(struct adis *adis, + struct iio_dev *indio_dev) +{ + if (adis->spi->irq) + adis_remove_trigger(adis); + kfree(adis->buffer); + kfree(adis->xfer); + iio_triggered_buffer_cleanup(indio_dev); +} +EXPORT_SYMBOL_GPL(adis_cleanup_buffer_and_trigger); diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c new file mode 100644 index 000000000000..5a24c9cac343 --- /dev/null +++ b/drivers/iio/imu/adis_trigger.c @@ -0,0 +1,89 @@ +/* + * Common library for ADIS16XXX devices + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include + +#include +#include +#include + +static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct adis *adis = trig->private_data; + + return adis_enable_irq(adis, state); +} + +static const struct iio_trigger_ops adis_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = &adis_data_rdy_trigger_set_state, +}; + +/** + * adis_probe_trigger() - Sets up trigger for a adis device + * @adis: The adis device + * @indio_dev: The IIO device + * + * Returns 0 on success or a negative error code + * + * adis_remove_trigger() should be used to free the trigger. + */ +int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) +{ + int ret; + + adis->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, + indio_dev->id); + if (adis->trig == NULL) + return -ENOMEM; + + ret = request_irq(adis->spi->irq, + &iio_trigger_generic_data_rdy_poll, + IRQF_TRIGGER_RISING, + indio_dev->name, + adis->trig); + if (ret) + goto error_free_trig; + + adis->trig->dev.parent = &adis->spi->dev; + adis->trig->ops = &adis_trigger_ops; + adis->trig->private_data = adis; + ret = iio_trigger_register(adis->trig); + + indio_dev->trig = adis->trig; + if (ret) + goto error_free_irq; + + return 0; + +error_free_irq: + free_irq(adis->spi->irq, adis->trig); +error_free_trig: + iio_trigger_free(adis->trig); + return ret; +} +EXPORT_SYMBOL_GPL(adis_probe_trigger); + +/** + * adis_remove_trigger() - Remove trigger for a adis devices + * @adis: The adis device + * + * Removes the trigger previously registered with adis_probe_trigger(). + */ +void adis_remove_trigger(struct adis *adis) +{ + iio_trigger_unregister(adis->trig); + free_irq(adis->spi->irq, adis->trig); + iio_trigger_free(adis->trig); +} +EXPORT_SYMBOL_GPL(adis_remove_trigger); diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c index 833dd6b73bc3..ccdc8d23f6a3 100644 --- a/drivers/staging/iio/accel/adis16201_core.c +++ b/drivers/staging/iio/accel/adis16201_core.c @@ -18,9 +18,9 @@ #include #include #include +#include #include "adis16201.h" -#include "../imu/adis.h" static const u8 adis16201_addresses[] = { [ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS, diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c index f631e578fbd1..202985ea3531 100644 --- a/drivers/staging/iio/accel/adis16203_core.c +++ b/drivers/staging/iio/accel/adis16203_core.c @@ -18,9 +18,9 @@ #include #include #include +#include #include "adis16203.h" -#include "../imu/adis.h" #define DRIVER_NAME "adis16203" diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c index dbec841ce30c..6dafad67cd23 100644 --- a/drivers/staging/iio/accel/adis16204_core.c +++ b/drivers/staging/iio/accel/adis16204_core.c @@ -21,9 +21,9 @@ #include #include #include +#include #include "adis16204.h" -#include "../imu/adis.h" /* Unique to this driver currently */ diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c index f9f9d582b32d..d2921c30a8bb 100644 --- a/drivers/staging/iio/accel/adis16209_core.c +++ b/drivers/staging/iio/accel/adis16209_core.c @@ -19,9 +19,9 @@ #include #include #include +#include #include "adis16209.h" -#include "../imu/adis.h" static const u8 adis16209_addresses[8][1] = { [ADIS16209_SCAN_SUPPLY] = { }, diff --git a/drivers/staging/iio/accel/adis16220.h b/drivers/staging/iio/accel/adis16220.h index 7cc4d2f3ec28..a894ad7fb26d 100644 --- a/drivers/staging/iio/accel/adis16220.h +++ b/drivers/staging/iio/accel/adis16220.h @@ -1,7 +1,7 @@ #ifndef SPI_ADIS16220_H_ #define SPI_ADIS16220_H_ -#include "../imu/adis.h" +#include #define ADIS16220_STARTUP_DELAY 220 /* ms */ diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c index 3d1a8a9921ad..d098b49cc18b 100644 --- a/drivers/staging/iio/accel/adis16240_core.c +++ b/drivers/staging/iio/accel/adis16240_core.c @@ -22,9 +22,9 @@ #include #include #include +#include #include "adis16240.h" -#include "../imu/adis.h" static ssize_t adis16240_spi_read_signed(struct device *dev, struct device_attribute *attr, diff --git a/drivers/staging/iio/gyro/adis16260.h b/drivers/staging/iio/gyro/adis16260.h index ea5eba205bbf..df3c0b7e954a 100644 --- a/drivers/staging/iio/gyro/adis16260.h +++ b/drivers/staging/iio/gyro/adis16260.h @@ -2,7 +2,7 @@ #define SPI_ADIS16260_H_ #include "adis16260_platform_data.h" -#include "../imu/adis.h" +#include #define ADIS16260_STARTUP_DELAY 220 /* ms */ diff --git a/drivers/staging/iio/imu/Kconfig b/drivers/staging/iio/imu/Kconfig index 2c564edeb172..2c2f47de2630 100644 --- a/drivers/staging/iio/imu/Kconfig +++ b/drivers/staging/iio/imu/Kconfig @@ -15,15 +15,3 @@ config ADIS16400 (adis16400 series also have magnetometers). endmenu - -config IIO_ADIS_LIB - tristate - help - A set of IO helper functions for the Analog Devices ADIS* device family. - -config IIO_ADIS_LIB_BUFFER - bool - select IIO_TRIGGERED_BUFFER - help - A set of buffer helper functions for the Analog Devices ADIS* device - family. diff --git a/drivers/staging/iio/imu/Makefile b/drivers/staging/iio/imu/Makefile index 65dafba1e5df..3400a13d1522 100644 --- a/drivers/staging/iio/imu/Makefile +++ b/drivers/staging/iio/imu/Makefile @@ -5,8 +5,3 @@ adis16400-y := adis16400_core.o adis16400-$(CONFIG_IIO_BUFFER) += adis16400_ring.o adis16400_trigger.o obj-$(CONFIG_ADIS16400) += adis16400.o - -adis_lib-y += adis.o -adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o -adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o -obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o diff --git a/drivers/staging/iio/imu/adis.c b/drivers/staging/iio/imu/adis.c deleted file mode 100644 index 0bd21022e4c0..000000000000 --- a/drivers/staging/iio/imu/adis.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Common library for ADIS16XXX devices - * - * Copyright 2012 Analog Devices Inc. - * Author: Lars-Peter Clausen - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "adis.h" - -#define ADIS_MSC_CTRL_DATA_RDY_EN BIT(2) -#define ADIS_MSC_CTRL_DATA_RDY_POL_HIGH BIT(1) -#define ADIS_MSC_CTRL_DATA_RDY_DIO2 BIT(0) -#define ADIS_GLOB_CMD_SW_RESET BIT(7) - -/** - * adis_write_reg_8() - Write single byte to a register - * @adis: The adis device - * @reg: The address of the register to be written - * @val: The value to write - */ -int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val) -{ - int ret; - - mutex_lock(&adis->txrx_lock); - adis->tx[0] = ADIS_WRITE_REG(reg); - adis->tx[1] = val; - - ret = spi_write(adis->spi, adis->tx, 2); - mutex_unlock(&adis->txrx_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(adis_write_reg_8); - -/** - * adis_write_reg_16() - Write 2 bytes to a pair of registers - * @adis: The adis device - * @reg: The address of the lower of the two registers - * @val: Value to be written - */ -int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t value) -{ - int ret; - struct spi_message msg; - struct spi_transfer xfers[] = { - { - .tx_buf = adis->tx, - .bits_per_word = 8, - .len = 2, - .cs_change = 1, - .delay_usecs = adis->data->write_delay, - }, { - .tx_buf = adis->tx + 2, - .bits_per_word = 8, - .len = 2, - .delay_usecs = adis->data->write_delay, - }, - }; - - mutex_lock(&adis->txrx_lock); - adis->tx[0] = ADIS_WRITE_REG(reg); - adis->tx[1] = value & 0xff; - adis->tx[2] = ADIS_WRITE_REG(reg + 1); - adis->tx[3] = (value >> 8) & 0xff; - - spi_message_init(&msg); - spi_message_add_tail(&xfers[0], &msg); - spi_message_add_tail(&xfers[1], &msg); - ret = spi_sync(adis->spi, &msg); - mutex_unlock(&adis->txrx_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(adis_write_reg_16); - -/** - * adis_read_reg_16() - read 2 bytes from a 16-bit register - * @adis: The adis device - * @reg: The address of the lower of the two registers - * @val: The value read back from the device - */ -int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val) -{ - struct spi_message msg; - int ret; - struct spi_transfer xfers[] = { - { - .tx_buf = adis->tx, - .bits_per_word = 8, - .len = 2, - .cs_change = 1, - .delay_usecs = adis->data->read_delay, - }, { - .rx_buf = adis->rx, - .bits_per_word = 8, - .len = 2, - .delay_usecs = adis->data->read_delay, - }, - }; - - mutex_lock(&adis->txrx_lock); - adis->tx[0] = ADIS_READ_REG(reg); - adis->tx[1] = 0; - - spi_message_init(&msg); - spi_message_add_tail(&xfers[0], &msg); - spi_message_add_tail(&xfers[1], &msg); - ret = spi_sync(adis->spi, &msg); - if (ret) { - dev_err(&adis->spi->dev, "Failed to read 16 bit register 0x%02X: %d\n", - reg, ret); - goto error_ret; - } - *val = get_unaligned_be16(adis->rx); - -error_ret: - mutex_unlock(&adis->txrx_lock); - return ret; -} -EXPORT_SYMBOL_GPL(adis_read_reg_16); - -/** - * adis_enable_irq() - Enable or disable data ready IRQ - * @adis: The adis device - * @enable: Whether to enable the IRQ - * - * Returns 0 on success, negative error code otherwise - */ -int adis_enable_irq(struct adis *adis, bool enable) -{ - int ret = 0; - uint16_t msc; - - ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); - if (ret) - goto error_ret; - - msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH; - msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2; - if (enable) - msc |= ADIS_MSC_CTRL_DATA_RDY_EN; - else - msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN; - - ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc); - -error_ret: - return ret; -} -EXPORT_SYMBOL(adis_enable_irq); - -/** - * adis_check_status() - Check the device for error conditions - * @adis: The adis device - * - * Returns 0 on success, a negative error code otherwise - */ -int adis_check_status(struct adis *adis) -{ - uint16_t status; - int ret; - int i; - - ret = adis_read_reg_16(adis, adis->data->diag_stat_reg, &status); - if (ret < 0) - return ret; - - status &= adis->data->status_error_mask; - - if (status == 0) - return 0; - - for (i = 0; i < 16; ++i) { - if (status & BIT(i)) { - dev_err(&adis->spi->dev, "%s.\n", - adis->data->status_error_msgs[i]); - } - } - - return -EIO; -} -EXPORT_SYMBOL_GPL(adis_check_status); - -/** - * adis_reset() - Reset the device - * @adis: The adis device - * - * Returns 0 on success, a negative error code otherwise - */ -int adis_reset(struct adis *adis) -{ - int ret; - - ret = adis_write_reg_8(adis, adis->data->glob_cmd_reg, - ADIS_GLOB_CMD_SW_RESET); - if (ret) - dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret); - - return ret; -} -EXPORT_SYMBOL_GPL(adis_reset); - -static int adis_self_test(struct adis *adis) -{ - int ret; - - ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, - adis->data->self_test_mask); - if (ret) { - dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n", - ret); - return ret; - } - - msleep(adis->data->startup_delay); - - return adis_check_status(adis); -} - -/** - * adis_inital_startup() - Performs device self-test - * @adis: The adis device - * - * Returns 0 if the device is operational, a negative error code otherwise. - * - * This function should be called early on in the device initialization sequence - * to ensure that the device is in a sane and known state and that it is usable. - */ -int adis_initial_startup(struct adis *adis) -{ - int ret; - - ret = adis_self_test(adis); - if (ret) { - dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n"); - adis_reset(adis); - msleep(adis->data->startup_delay); - ret = adis_self_test(adis); - if (ret) { - dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n"); - return ret; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(adis_initial_startup); - -/** - * adis_single_conversion() - Performs a single sample conversion - * @indio_dev: The IIO device - * @chan: The IIO channel - * @error_mask: Mask for the error bit - * @val: Result of the conversion - * - * Returns IIO_VAL_INT on success, a negative error code otherwise. - * - * The function performs a single conversion on a given channel and post - * processes the value accordingly to the channel spec. If a error_mask is given - * the function will check if the mask is set in the returned raw value. If it - * is set the function will perform a self-check. If the device does not report - * a error bit in the channels raw value set error_mask to 0. - */ -int adis_single_conversion(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int error_mask, int *val) -{ - struct adis *adis = iio_device_get_drvdata(indio_dev); - uint16_t val16; - int ret; - - mutex_lock(&indio_dev->mlock); - - ret = adis_read_reg_16(adis, chan->address, &val16); - if (ret) - goto err_unlock; - - if (val16 & error_mask) { - ret = adis_check_status(adis); - if (ret) - goto err_unlock; - } - - if (chan->scan_type.sign == 's') - *val = sign_extend32(val16, chan->scan_type.realbits - 1); - else - *val = val16 & ((1 << chan->scan_type.realbits) - 1); - - ret = IIO_VAL_INT; -err_unlock: - mutex_unlock(&indio_dev->mlock); - return ret; -} -EXPORT_SYMBOL_GPL(adis_single_conversion); - -/** - * adis_init() - Initialize adis device structure - * @adis: The adis device - * @indio_dev: The iio device - * @spi: The spi device - * @data: Chip specific data - * - * Returns 0 on success, a negative error code otherwise. - * - * This function must be called, before any other adis helper function may be - * called. - */ -int adis_init(struct adis *adis, struct iio_dev *indio_dev, - struct spi_device *spi, const struct adis_data *data) -{ - mutex_init(&adis->txrx_lock); - adis->spi = spi; - adis->data = data; - iio_device_set_drvdata(indio_dev, adis); - - return adis_enable_irq(adis, false); -} -EXPORT_SYMBOL_GPL(adis_init); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Common library code for ADIS16XXX devices"); diff --git a/drivers/staging/iio/imu/adis.h b/drivers/staging/iio/imu/adis.h deleted file mode 100644 index 8c3304d44b97..000000000000 --- a/drivers/staging/iio/imu/adis.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Common library for ADIS16XXX devices - * - * Copyright 2012 Analog Devices Inc. - * Author: Lars-Peter Clausen - * - * Licensed under the GPL-2 or later. - */ - -#ifndef __IIO_ADIS_H__ -#define __IIO_ADIS_H__ - -#include -#include -#include - -#define ADIS_WRITE_REG(reg) (0x80 | (reg)) -#define ADIS_READ_REG(reg) (reg) - -/** - * struct adis_data - ADIS chip variant specific data - * @read_delay: SPI delay for read operations in us - * @write_delay: SPI delay for write operations in us - * @glob_cmd_reg: Register address of the GLOB_CMD register - * @msc_ctrl_reg: Register address of the MSC_CTRL register - * @diag_stat_reg: Register address of the DIAG_STAT register - * @status_error_msgs: Array of error messgaes - * @status_error_mask: - */ -struct adis_data { - unsigned int read_delay; - unsigned int write_delay; - - unsigned int glob_cmd_reg; - unsigned int msc_ctrl_reg; - unsigned int diag_stat_reg; - - unsigned int self_test_mask; - unsigned int startup_delay; - - const char * const *status_error_msgs; - unsigned int status_error_mask; -}; - -struct adis { - struct spi_device *spi; - struct iio_trigger *trig; - - const struct adis_data *data; - - struct mutex txrx_lock; - struct spi_message msg; - struct spi_transfer *xfer; - void *buffer; - - uint8_t tx[8] ____cacheline_aligned; - uint8_t rx[4]; -}; - -int adis_init(struct adis *adis, struct iio_dev *indio_dev, - struct spi_device *spi, const struct adis_data *data); -int adis_reset(struct adis *adis); - -int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val); -int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t val); -int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val); - -int adis_enable_irq(struct adis *adis, bool enable); -int adis_check_status(struct adis *adis); - -int adis_initial_startup(struct adis *adis); - -int adis_single_conversion(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int error_mask, - int *val); - -#define ADIS_VOLTAGE_CHAN(addr, si, chan, name, bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .channel = (chan), \ - .extend_name = name, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ - .address = (addr), \ - .scan_index = (si), \ - .scan_type = { \ - .sign = 'u', \ - .realbits = (bits), \ - .storagebits = 16, \ - .endianness = IIO_BE, \ - }, \ -} - -#define ADIS_SUPPLY_CHAN(addr, si, bits) \ - ADIS_VOLTAGE_CHAN(addr, si, 0, "supply", bits) - -#define ADIS_AUX_ADC_CHAN(addr, si, bits) \ - ADIS_VOLTAGE_CHAN(addr, si, 1, NULL, bits) - -#define ADIS_TEMP_CHAN(addr, si, bits) { \ - .type = IIO_TEMP, \ - .indexed = 1, \ - .channel = 0, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \ - .address = (addr), \ - .scan_index = (si), \ - .scan_type = { \ - .sign = 'u', \ - .realbits = (bits), \ - .storagebits = 16, \ - .endianness = IIO_BE, \ - }, \ -} - -#define ADIS_MOD_CHAN(_type, mod, addr, si, info, bits) { \ - .type = (_type), \ - .modified = 1, \ - .channel2 = IIO_MOD_ ## mod, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT | \ - info, \ - .address = (addr), \ - .scan_index = (si), \ - .scan_type = { \ - .sign = 's', \ - .realbits = (bits), \ - .storagebits = 16, \ - .endianness = IIO_BE, \ - }, \ -} - -#define ADIS_ACCEL_CHAN(mod, addr, si, info, bits) \ - ADIS_MOD_CHAN(IIO_ACCEL, mod, addr, si, info, bits) - -#define ADIS_GYRO_CHAN(mod, addr, si, info, bits) \ - ADIS_MOD_CHAN(IIO_ANGL_VEL, mod, addr, si, info, bits) - -#define ADIS_INCLI_CHAN(mod, addr, si, info, bits) \ - ADIS_MOD_CHAN(IIO_INCLI, mod, addr, si, info, bits) - -#define ADIS_ROT_CHAN(mod, addr, si, info, bits) \ - ADIS_MOD_CHAN(IIO_ROT, mod, addr, si, info, bits) - -#ifdef CONFIG_IIO_ADIS_LIB_BUFFER - -int adis_setup_buffer_and_trigger(struct adis *adis, - struct iio_dev *indio_dev, irqreturn_t (*trigger_handler)(int, void *)); -void adis_cleanup_buffer_and_trigger(struct adis *adis, - struct iio_dev *indio_dev); - -int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev); -void adis_remove_trigger(struct adis *adis); - -int adis_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *scan_mask); - -#else /* CONFIG_IIO_BUFFER */ - -static inline int adis_setup_buffer_and_trigger(struct adis *adis, - struct iio_dev *indio_dev, irqreturn_t (*trigger_handler)(int, void *)) -{ - return 0; -} - -static inline void adis_cleanup_buffer_and_trigger(struct adis *adis, - struct iio_dev *indio_dev) -{ -} - -static inline int adis_probe_trigger(struct adis *adis, - struct iio_dev *indio_dev) -{ - return 0; -} - -static inline void adis_remove_trigger(struct adis *adis) -{ -} - -#define adis_update_scan_mode NULL - -#endif /* CONFIG_IIO_BUFFER */ - -#endif diff --git a/drivers/staging/iio/imu/adis_buffer.c b/drivers/staging/iio/imu/adis_buffer.c deleted file mode 100644 index 342758c14d2b..000000000000 --- a/drivers/staging/iio/imu/adis_buffer.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Common library for ADIS16XXX devices - * - * Copyright 2012 Analog Devices Inc. - * Author: Lars-Peter Clausen - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "adis.h" - -int adis_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *scan_mask) -{ - struct adis *adis = iio_device_get_drvdata(indio_dev); - const struct iio_chan_spec *chan; - unsigned int scan_count; - unsigned int i, j; - __be16 *tx, *rx; - - kfree(adis->xfer); - kfree(adis->buffer); - - scan_count = indio_dev->scan_bytes / 2; - - adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL); - if (!adis->xfer) - return -ENOMEM; - - adis->buffer = kzalloc(indio_dev->scan_bytes * 2, GFP_KERNEL); - if (!adis->buffer) - return -ENOMEM; - - rx = adis->buffer; - tx = rx + indio_dev->scan_bytes; - - spi_message_init(&adis->msg); - - for (j = 0; j <= scan_count; j++) { - adis->xfer[j].bits_per_word = 8; - if (j != scan_count) - adis->xfer[j].cs_change = 1; - adis->xfer[j].len = 2; - adis->xfer[j].delay_usecs = adis->data->read_delay; - if (j < scan_count) - adis->xfer[j].tx_buf = &tx[j]; - if (j >= 1) - adis->xfer[j].rx_buf = &rx[j - 1]; - spi_message_add_tail(&adis->xfer[j], &adis->msg); - } - - chan = indio_dev->channels; - for (i = 0; i < indio_dev->num_channels; i++, chan++) { - if (!test_bit(chan->scan_index, scan_mask)) - continue; - *tx++ = cpu_to_be16(chan->address << 8); - } - - return 0; -} -EXPORT_SYMBOL_GPL(adis_update_scan_mode); - -static irqreturn_t adis_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct adis *adis = iio_device_get_drvdata(indio_dev); - int ret; - - if (!adis->buffer) - return -ENOMEM; - - ret = spi_sync(adis->spi, &adis->msg); - if (ret) - dev_err(&adis->spi->dev, "Failed to read data: %d", ret); - - /* Guaranteed to be aligned with 8 byte boundary */ - if (indio_dev->scan_timestamp) { - void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64); - *(s64 *)b = pf->timestamp; - } - - iio_push_to_buffers(indio_dev, adis->buffer); - - iio_trigger_notify_done(indio_dev->trig); - - return IRQ_HANDLED; -} - -/** - * adis_setup_buffer_and_trigger() - Sets up buffer and trigger for the adis device - * @adis: The adis device. - * @indio_dev: The IIO device. - * @trigger_handler: Optional trigger handler, may be NULL. - * - * Returns 0 on success, a negative error code otherwise. - * - * This function sets up the buffer and trigger for a adis devices. If - * 'trigger_handler' is NULL the default trigger handler will be used. The - * default trigger handler will simply read the registers assigned to the - * currently active channels. - * - * adis_cleanup_buffer_and_trigger() should be called to free the resources - * allocated by this function. - */ -int adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev, - irqreturn_t (*trigger_handler)(int, void *)) -{ - int ret; - - if (!trigger_handler) - trigger_handler = adis_trigger_handler; - - ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, - trigger_handler, NULL); - if (ret) - return ret; - - if (adis->spi->irq) { - ret = adis_probe_trigger(adis, indio_dev); - if (ret) - goto error_buffer_cleanup; - } - return 0; - -error_buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); - return ret; -} -EXPORT_SYMBOL_GPL(adis_setup_buffer_and_trigger); - -/** - * adis_cleanup_buffer_and_trigger() - Free buffer and trigger resources - * @adis: The adis device. - * @indio_dev: The IIO device. - * - * Frees resources allocated by adis_setup_buffer_and_trigger() - */ -void adis_cleanup_buffer_and_trigger(struct adis *adis, - struct iio_dev *indio_dev) -{ - if (adis->spi->irq) - adis_remove_trigger(adis); - kfree(adis->buffer); - kfree(adis->xfer); - iio_triggered_buffer_cleanup(indio_dev); -} -EXPORT_SYMBOL_GPL(adis_cleanup_buffer_and_trigger); diff --git a/drivers/staging/iio/imu/adis_trigger.c b/drivers/staging/iio/imu/adis_trigger.c deleted file mode 100644 index 3e89b2e83708..000000000000 --- a/drivers/staging/iio/imu/adis_trigger.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Common library for ADIS16XXX devices - * - * Copyright 2012 Analog Devices Inc. - * Author: Lars-Peter Clausen - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include - -#include -#include - -#include "adis.h" - -static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig, - bool state) -{ - struct adis *adis = trig->private_data; - - return adis_enable_irq(adis, state); -} - -static const struct iio_trigger_ops adis_trigger_ops = { - .owner = THIS_MODULE, - .set_trigger_state = &adis_data_rdy_trigger_set_state, -}; - -/** - * adis_probe_trigger() - Sets up trigger for a adis device - * @adis: The adis device - * @indio_dev: The IIO device - * - * Returns 0 on success or a negative error code - * - * adis_remove_trigger() should be used to free the trigger. - */ -int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) -{ - int ret; - - adis->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, - indio_dev->id); - if (adis->trig == NULL) - return -ENOMEM; - - ret = request_irq(adis->spi->irq, - &iio_trigger_generic_data_rdy_poll, - IRQF_TRIGGER_RISING, - indio_dev->name, - adis->trig); - if (ret) - goto error_free_trig; - - adis->trig->dev.parent = &adis->spi->dev; - adis->trig->ops = &adis_trigger_ops; - adis->trig->private_data = adis; - ret = iio_trigger_register(adis->trig); - - indio_dev->trig = adis->trig; - if (ret) - goto error_free_irq; - - return 0; - -error_free_irq: - free_irq(adis->spi->irq, adis->trig); -error_free_trig: - iio_trigger_free(adis->trig); - return ret; -} -EXPORT_SYMBOL_GPL(adis_probe_trigger); - -/** - * adis_remove_trigger() - Remove trigger for a adis devices - * @adis: The adis device - * - * Removes the trigger previously registered with adis_probe_trigger(). - */ -void adis_remove_trigger(struct adis *adis) -{ - iio_trigger_unregister(adis->trig); - free_irq(adis->spi->irq, adis->trig); - iio_trigger_free(adis->trig); -} -EXPORT_SYMBOL_GPL(adis_remove_trigger); diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h new file mode 100644 index 000000000000..8c3304d44b97 --- /dev/null +++ b/include/linux/iio/imu/adis.h @@ -0,0 +1,186 @@ +/* + * Common library for ADIS16XXX devices + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __IIO_ADIS_H__ +#define __IIO_ADIS_H__ + +#include +#include +#include + +#define ADIS_WRITE_REG(reg) (0x80 | (reg)) +#define ADIS_READ_REG(reg) (reg) + +/** + * struct adis_data - ADIS chip variant specific data + * @read_delay: SPI delay for read operations in us + * @write_delay: SPI delay for write operations in us + * @glob_cmd_reg: Register address of the GLOB_CMD register + * @msc_ctrl_reg: Register address of the MSC_CTRL register + * @diag_stat_reg: Register address of the DIAG_STAT register + * @status_error_msgs: Array of error messgaes + * @status_error_mask: + */ +struct adis_data { + unsigned int read_delay; + unsigned int write_delay; + + unsigned int glob_cmd_reg; + unsigned int msc_ctrl_reg; + unsigned int diag_stat_reg; + + unsigned int self_test_mask; + unsigned int startup_delay; + + const char * const *status_error_msgs; + unsigned int status_error_mask; +}; + +struct adis { + struct spi_device *spi; + struct iio_trigger *trig; + + const struct adis_data *data; + + struct mutex txrx_lock; + struct spi_message msg; + struct spi_transfer *xfer; + void *buffer; + + uint8_t tx[8] ____cacheline_aligned; + uint8_t rx[4]; +}; + +int adis_init(struct adis *adis, struct iio_dev *indio_dev, + struct spi_device *spi, const struct adis_data *data); +int adis_reset(struct adis *adis); + +int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val); +int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t val); +int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val); + +int adis_enable_irq(struct adis *adis, bool enable); +int adis_check_status(struct adis *adis); + +int adis_initial_startup(struct adis *adis); + +int adis_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int error_mask, + int *val); + +#define ADIS_VOLTAGE_CHAN(addr, si, chan, name, bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan), \ + .extend_name = name, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .address = (addr), \ + .scan_index = (si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +#define ADIS_SUPPLY_CHAN(addr, si, bits) \ + ADIS_VOLTAGE_CHAN(addr, si, 0, "supply", bits) + +#define ADIS_AUX_ADC_CHAN(addr, si, bits) \ + ADIS_VOLTAGE_CHAN(addr, si, 1, NULL, bits) + +#define ADIS_TEMP_CHAN(addr, si, bits) { \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .channel = 0, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \ + .address = (addr), \ + .scan_index = (si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +#define ADIS_MOD_CHAN(_type, mod, addr, si, info, bits) { \ + .type = (_type), \ + .modified = 1, \ + .channel2 = IIO_MOD_ ## mod, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT | \ + info, \ + .address = (addr), \ + .scan_index = (si), \ + .scan_type = { \ + .sign = 's', \ + .realbits = (bits), \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +#define ADIS_ACCEL_CHAN(mod, addr, si, info, bits) \ + ADIS_MOD_CHAN(IIO_ACCEL, mod, addr, si, info, bits) + +#define ADIS_GYRO_CHAN(mod, addr, si, info, bits) \ + ADIS_MOD_CHAN(IIO_ANGL_VEL, mod, addr, si, info, bits) + +#define ADIS_INCLI_CHAN(mod, addr, si, info, bits) \ + ADIS_MOD_CHAN(IIO_INCLI, mod, addr, si, info, bits) + +#define ADIS_ROT_CHAN(mod, addr, si, info, bits) \ + ADIS_MOD_CHAN(IIO_ROT, mod, addr, si, info, bits) + +#ifdef CONFIG_IIO_ADIS_LIB_BUFFER + +int adis_setup_buffer_and_trigger(struct adis *adis, + struct iio_dev *indio_dev, irqreturn_t (*trigger_handler)(int, void *)); +void adis_cleanup_buffer_and_trigger(struct adis *adis, + struct iio_dev *indio_dev); + +int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev); +void adis_remove_trigger(struct adis *adis); + +int adis_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask); + +#else /* CONFIG_IIO_BUFFER */ + +static inline int adis_setup_buffer_and_trigger(struct adis *adis, + struct iio_dev *indio_dev, irqreturn_t (*trigger_handler)(int, void *)) +{ + return 0; +} + +static inline void adis_cleanup_buffer_and_trigger(struct adis *adis, + struct iio_dev *indio_dev) +{ +} + +static inline int adis_probe_trigger(struct adis *adis, + struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void adis_remove_trigger(struct adis *adis) +{ +} + +#define adis_update_scan_mode NULL + +#endif /* CONFIG_IIO_BUFFER */ + +#endif -- cgit v1.2.3 From 709ab36e9559ff5c7df6e6f2d9e3c4a4410f8d49 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 17 Nov 2012 11:42:59 +0000 Subject: staging:iio: Move the ad7298 driver out of staging The driver does not expose any custom API to userspace and none of the standard static code checker tools report any issues, so move it out of staging. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 12 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7298.c | 408 +++++++++++++++++++++++++++++++++++ drivers/staging/iio/adc/Kconfig | 12 -- drivers/staging/iio/adc/Makefile | 2 - drivers/staging/iio/adc/ad7298.c | 408 ----------------------------------- drivers/staging/iio/adc/ad7298.h | 20 -- include/linux/platform_data/ad7298.h | 20 ++ 8 files changed, 441 insertions(+), 442 deletions(-) create mode 100644 drivers/iio/adc/ad7298.c delete mode 100644 drivers/staging/iio/adc/ad7298.c delete mode 100644 drivers/staging/iio/adc/ad7298.h create mode 100644 include/linux/platform_data/ad7298.h (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index ef5200a6850e..cd5eed60be28 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -18,6 +18,18 @@ config AD7266 Say yes here to build support for Analog Devices AD7265 and AD7266 ADCs. +config AD7298 + tristate "Analog Devices AD7298 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7298 + 8 Channel ADC with temperature sensor. + + To compile this driver as a module, choose M here: the + module will be called ad7298. + config AD7791 tristate "Analog Devices AD7791 ADC driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 54ac7bbcd01b..3256dc64a466 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7266) += ad7266.o +obj-$(CONFIG_AD7298) += ad7298.o obj-$(CONFIG_AD7476) += ad7476.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7887) += ad7887.o diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c new file mode 100644 index 000000000000..441a9a265c12 --- /dev/null +++ b/drivers/iio/adc/ad7298.c @@ -0,0 +1,408 @@ +/* + * AD7298 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define AD7298_WRITE (1 << 15) /* write to the control register */ +#define AD7298_REPEAT (1 << 14) /* repeated conversion enable */ +#define AD7298_CH(x) (1 << (13 - (x))) /* channel select */ +#define AD7298_TSENSE (1 << 5) /* temperature conversion enable */ +#define AD7298_EXTREF (1 << 2) /* external reference enable */ +#define AD7298_TAVG (1 << 1) /* temperature sensor averaging enable */ +#define AD7298_PDD (1 << 0) /* partial power down enable */ + +#define AD7298_MAX_CHAN 8 +#define AD7298_BITS 12 +#define AD7298_STORAGE_BITS 16 +#define AD7298_INTREF_mV 2500 + +#define AD7298_CH_TEMP 9 + +#define RES_MASK(bits) ((1 << (bits)) - 1) + +struct ad7298_state { + struct spi_device *spi; + struct regulator *reg; + unsigned ext_ref; + struct spi_transfer ring_xfer[10]; + struct spi_transfer scan_single_xfer[3]; + struct spi_message ring_msg; + struct spi_message scan_single_msg; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + unsigned short rx_buf[12] ____cacheline_aligned; + unsigned short tx_buf[2]; +}; + +#define AD7298_V_CHAN(index) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec ad7298_channels[] = { + { + .type = IIO_TEMP, + .indexed = 1, + .channel = 0, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, + .address = AD7298_CH_TEMP, + .scan_index = -1, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + }, + }, + AD7298_V_CHAN(0), + AD7298_V_CHAN(1), + AD7298_V_CHAN(2), + AD7298_V_CHAN(3), + AD7298_V_CHAN(4), + AD7298_V_CHAN(5), + AD7298_V_CHAN(6), + AD7298_V_CHAN(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +/** + * ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask + **/ +static int ad7298_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad7298_state *st = iio_priv(indio_dev); + int i, m; + unsigned short command; + int scan_count; + + /* Now compute overall size */ + scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength); + + command = AD7298_WRITE | st->ext_ref; + + for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1) + if (test_bit(i, active_scan_mask)) + command |= m; + + st->tx_buf[0] = cpu_to_be16(command); + + /* build spi ring message */ + st->ring_xfer[0].tx_buf = &st->tx_buf[0]; + st->ring_xfer[0].len = 2; + st->ring_xfer[0].cs_change = 1; + st->ring_xfer[1].tx_buf = &st->tx_buf[1]; + st->ring_xfer[1].len = 2; + st->ring_xfer[1].cs_change = 1; + + spi_message_init(&st->ring_msg); + spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg); + spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg); + + for (i = 0; i < scan_count; i++) { + st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i]; + st->ring_xfer[i + 2].len = 2; + st->ring_xfer[i + 2].cs_change = 1; + spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg); + } + /* make sure last transfer cs_change is not set */ + st->ring_xfer[i + 1].cs_change = 0; + + return 0; +} + +/** + * ad7298_trigger_handler() bh of trigger launched polling to ring buffer + * + * Currently there is no option in this driver to disable the saving of + * timestamps within the ring. + **/ +static irqreturn_t ad7298_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7298_state *st = iio_priv(indio_dev); + s64 time_ns = 0; + int b_sent; + + b_sent = spi_sync(st->spi, &st->ring_msg); + if (b_sent) + goto done; + + if (indio_dev->scan_timestamp) { + time_ns = iio_get_time_ns(); + memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64), + &time_ns, sizeof(time_ns)); + } + + iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch) +{ + int ret; + st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref | + (AD7298_CH(0) >> ch)); + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + return be16_to_cpu(st->rx_buf[0]); +} + +static int ad7298_scan_temp(struct ad7298_state *st, int *val) +{ + int ret; + __be16 buf; + + buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE | + AD7298_TAVG | st->ext_ref); + + ret = spi_write(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + buf = cpu_to_be16(0); + + ret = spi_write(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + usleep_range(101, 1000); /* sleep > 100us */ + + ret = spi_read(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + *val = sign_extend32(be16_to_cpu(buf), 11); + + return 0; +} + +static int ad7298_get_ref_voltage(struct ad7298_state *st) +{ + int vref; + + if (st->ext_ref) { + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + return vref / 1000; + } else { + return AD7298_INTREF_mV; + } +} + +static int ad7298_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7298_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { + ret = -EBUSY; + } else { + if (chan->address == AD7298_CH_TEMP) + ret = ad7298_scan_temp(st, val); + else + ret = ad7298_scan_direct(st, chan->address); + } + mutex_unlock(&indio_dev->mlock); + + if (ret < 0) + return ret; + + if (chan->address != AD7298_CH_TEMP) + *val = ret & RES_MASK(AD7298_BITS); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = ad7298_get_ref_voltage(st); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *val = ad7298_get_ref_voltage(st); + *val2 = 10; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + *val = 1093 - 2732500 / ad7298_get_ref_voltage(st); + return IIO_VAL_INT; + } + return -EINVAL; +} + +static const struct iio_info ad7298_info = { + .read_raw = &ad7298_read_raw, + .update_scan_mode = ad7298_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad7298_probe(struct spi_device *spi) +{ + struct ad7298_platform_data *pdata = spi->dev.platform_data; + struct ad7298_state *st; + struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st)); + int ret; + + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + if (pdata && pdata->ext_ref) + st->ext_ref = AD7298_EXTREF; + + if (st->ext_ref) { + st->reg = regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto error_free; + } + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + } + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7298_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7298_channels); + indio_dev->info = &ad7298_info; + + /* Setup default message */ + + st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; + st->scan_single_xfer[0].len = 2; + st->scan_single_xfer[0].cs_change = 1; + st->scan_single_xfer[1].tx_buf = &st->tx_buf[1]; + st->scan_single_xfer[1].len = 2; + st->scan_single_xfer[1].cs_change = 1; + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[2].len = 2; + + spi_message_init(&st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &ad7298_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_ring; + + return 0; + +error_cleanup_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + if (st->ext_ref) + regulator_disable(st->reg); +error_put_reg: + if (st->ext_ref) + regulator_put(st->reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad7298_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7298_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (st->ext_ref) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7298_id[] = { + {"ad7298", 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7298_id); + +static struct spi_driver ad7298_driver = { + .driver = { + .name = "ad7298", + .owner = THIS_MODULE, + }, + .probe = ad7298_probe, + .remove = __devexit_p(ad7298_remove), + .id_table = ad7298_id, +}; +module_spi_driver(ad7298_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7298 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 5086a46b8e9a..dc8582b95b61 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -10,18 +10,6 @@ config AD7291 Say yes here to build support for Analog Devices AD7291 8 Channel ADC with temperature sensor. -config AD7298 - tristate "Analog Devices AD7298 ADC driver" - depends on SPI - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Say yes here to build support for Analog Devices AD7298 - 8 Channel ADC with temperature sensor. - - To compile this driver as a module, choose M here: the - module will be called ad7298. - config AD7606 tristate "Analog Devices AD7606 ADC driver" depends on GPIOLIB diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 4beaa588256b..7281451a613a 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -12,8 +12,6 @@ ad799x-y := ad799x_core.o ad799x-$(CONFIG_AD799X_RING_BUFFER) += ad799x_ring.o obj-$(CONFIG_AD799X) += ad799x.o -obj-$(CONFIG_AD7298) += ad7298.o - obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7780) += ad7780.o obj-$(CONFIG_AD7793) += ad7793.o diff --git a/drivers/staging/iio/adc/ad7298.c b/drivers/staging/iio/adc/ad7298.c deleted file mode 100644 index 2742a9de05db..000000000000 --- a/drivers/staging/iio/adc/ad7298.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * AD7298 SPI ADC driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "ad7298.h" - -#define AD7298_WRITE (1 << 15) /* write to the control register */ -#define AD7298_REPEAT (1 << 14) /* repeated conversion enable */ -#define AD7298_CH(x) (1 << (13 - (x))) /* channel select */ -#define AD7298_TSENSE (1 << 5) /* temperature conversion enable */ -#define AD7298_EXTREF (1 << 2) /* external reference enable */ -#define AD7298_TAVG (1 << 1) /* temperature sensor averaging enable */ -#define AD7298_PDD (1 << 0) /* partial power down enable */ - -#define AD7298_MAX_CHAN 8 -#define AD7298_BITS 12 -#define AD7298_STORAGE_BITS 16 -#define AD7298_INTREF_mV 2500 - -#define AD7298_CH_TEMP 9 - -#define RES_MASK(bits) ((1 << (bits)) - 1) - -struct ad7298_state { - struct spi_device *spi; - struct regulator *reg; - unsigned ext_ref; - struct spi_transfer ring_xfer[10]; - struct spi_transfer scan_single_xfer[3]; - struct spi_message ring_msg; - struct spi_message scan_single_msg; - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - unsigned short rx_buf[12] ____cacheline_aligned; - unsigned short tx_buf[2]; -}; - -#define AD7298_V_CHAN(index) \ - { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .channel = index, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT, \ - .address = index, \ - .scan_index = index, \ - .scan_type = { \ - .sign = 'u', \ - .realbits = 12, \ - .storagebits = 16, \ - .endianness = IIO_BE, \ - }, \ - } - -static const struct iio_chan_spec ad7298_channels[] = { - { - .type = IIO_TEMP, - .indexed = 1, - .channel = 0, - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | - IIO_CHAN_INFO_SCALE_SEPARATE_BIT | - IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, - .address = AD7298_CH_TEMP, - .scan_index = -1, - .scan_type = { - .sign = 's', - .realbits = 32, - .storagebits = 32, - }, - }, - AD7298_V_CHAN(0), - AD7298_V_CHAN(1), - AD7298_V_CHAN(2), - AD7298_V_CHAN(3), - AD7298_V_CHAN(4), - AD7298_V_CHAN(5), - AD7298_V_CHAN(6), - AD7298_V_CHAN(7), - IIO_CHAN_SOFT_TIMESTAMP(8), -}; - -/** - * ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask - **/ -static int ad7298_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *active_scan_mask) -{ - struct ad7298_state *st = iio_priv(indio_dev); - int i, m; - unsigned short command; - int scan_count; - - /* Now compute overall size */ - scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength); - - command = AD7298_WRITE | st->ext_ref; - - for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1) - if (test_bit(i, active_scan_mask)) - command |= m; - - st->tx_buf[0] = cpu_to_be16(command); - - /* build spi ring message */ - st->ring_xfer[0].tx_buf = &st->tx_buf[0]; - st->ring_xfer[0].len = 2; - st->ring_xfer[0].cs_change = 1; - st->ring_xfer[1].tx_buf = &st->tx_buf[1]; - st->ring_xfer[1].len = 2; - st->ring_xfer[1].cs_change = 1; - - spi_message_init(&st->ring_msg); - spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg); - spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg); - - for (i = 0; i < scan_count; i++) { - st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i]; - st->ring_xfer[i + 2].len = 2; - st->ring_xfer[i + 2].cs_change = 1; - spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg); - } - /* make sure last transfer cs_change is not set */ - st->ring_xfer[i + 1].cs_change = 0; - - return 0; -} - -/** - * ad7298_trigger_handler() bh of trigger launched polling to ring buffer - * - * Currently there is no option in this driver to disable the saving of - * timestamps within the ring. - **/ -static irqreturn_t ad7298_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct ad7298_state *st = iio_priv(indio_dev); - s64 time_ns = 0; - int b_sent; - - b_sent = spi_sync(st->spi, &st->ring_msg); - if (b_sent) - goto done; - - if (indio_dev->scan_timestamp) { - time_ns = iio_get_time_ns(); - memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64), - &time_ns, sizeof(time_ns)); - } - - iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf); - -done: - iio_trigger_notify_done(indio_dev->trig); - - return IRQ_HANDLED; -} - -static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch) -{ - int ret; - st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref | - (AD7298_CH(0) >> ch)); - - ret = spi_sync(st->spi, &st->scan_single_msg); - if (ret) - return ret; - - return be16_to_cpu(st->rx_buf[0]); -} - -static int ad7298_scan_temp(struct ad7298_state *st, int *val) -{ - int ret; - __be16 buf; - - buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE | - AD7298_TAVG | st->ext_ref); - - ret = spi_write(st->spi, (u8 *)&buf, 2); - if (ret) - return ret; - - buf = cpu_to_be16(0); - - ret = spi_write(st->spi, (u8 *)&buf, 2); - if (ret) - return ret; - - usleep_range(101, 1000); /* sleep > 100us */ - - ret = spi_read(st->spi, (u8 *)&buf, 2); - if (ret) - return ret; - - *val = sign_extend32(be16_to_cpu(buf), 11); - - return 0; -} - -static int ad7298_get_ref_voltage(struct ad7298_state *st) -{ - int vref; - - if (st->ext_ref) { - vref = regulator_get_voltage(st->reg); - if (vref < 0) - return vref; - - return vref / 1000; - } else { - return AD7298_INTREF_mV; - } -} - -static int ad7298_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - int ret; - struct ad7298_state *st = iio_priv(indio_dev); - - switch (m) { - case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { - ret = -EBUSY; - } else { - if (chan->address == AD7298_CH_TEMP) - ret = ad7298_scan_temp(st, val); - else - ret = ad7298_scan_direct(st, chan->address); - } - mutex_unlock(&indio_dev->mlock); - - if (ret < 0) - return ret; - - if (chan->address != AD7298_CH_TEMP) - *val = ret & RES_MASK(AD7298_BITS); - - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_VOLTAGE: - *val = ad7298_get_ref_voltage(st); - *val2 = chan->scan_type.realbits; - return IIO_VAL_FRACTIONAL_LOG2; - case IIO_TEMP: - *val = ad7298_get_ref_voltage(st); - *val2 = 10; - return IIO_VAL_FRACTIONAL; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_OFFSET: - *val = 1093 - 2732500 / ad7298_get_ref_voltage(st); - return IIO_VAL_INT; - } - return -EINVAL; -} - -static const struct iio_info ad7298_info = { - .read_raw = &ad7298_read_raw, - .update_scan_mode = ad7298_update_scan_mode, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad7298_probe(struct spi_device *spi) -{ - struct ad7298_platform_data *pdata = spi->dev.platform_data; - struct ad7298_state *st; - struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st)); - int ret; - - if (indio_dev == NULL) - return -ENOMEM; - - st = iio_priv(indio_dev); - - if (pdata && pdata->ext_ref) - st->ext_ref = AD7298_EXTREF; - - if (st->ext_ref) { - st->reg = regulator_get(&spi->dev, "vref"); - if (IS_ERR(st->reg)) { - ret = PTR_ERR(st->reg); - goto error_free; - } - ret = regulator_enable(st->reg); - if (ret) - goto error_put_reg; - } - - spi_set_drvdata(spi, indio_dev); - - st->spi = spi; - - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->dev.parent = &spi->dev; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ad7298_channels; - indio_dev->num_channels = ARRAY_SIZE(ad7298_channels); - indio_dev->info = &ad7298_info; - - /* Setup default message */ - - st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; - st->scan_single_xfer[0].len = 2; - st->scan_single_xfer[0].cs_change = 1; - st->scan_single_xfer[1].tx_buf = &st->tx_buf[1]; - st->scan_single_xfer[1].len = 2; - st->scan_single_xfer[1].cs_change = 1; - st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; - st->scan_single_xfer[2].len = 2; - - spi_message_init(&st->scan_single_msg); - spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); - spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg); - spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg); - - ret = iio_triggered_buffer_setup(indio_dev, NULL, - &ad7298_trigger_handler, NULL); - if (ret) - goto error_disable_reg; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_cleanup_ring; - - return 0; - -error_cleanup_ring: - iio_triggered_buffer_cleanup(indio_dev); -error_disable_reg: - if (st->ext_ref) - regulator_disable(st->reg); -error_put_reg: - if (st->ext_ref) - regulator_put(st->reg); -error_free: - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad7298_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7298_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - if (st->ext_ref) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad7298_id[] = { - {"ad7298", 0}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad7298_id); - -static struct spi_driver ad7298_driver = { - .driver = { - .name = "ad7298", - .owner = THIS_MODULE, - }, - .probe = ad7298_probe, - .remove = __devexit_p(ad7298_remove), - .id_table = ad7298_id, -}; -module_spi_driver(ad7298_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD7298 ADC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7298.h b/drivers/staging/iio/adc/ad7298.h deleted file mode 100644 index c8ac969ec016..000000000000 --- a/drivers/staging/iio/adc/ad7298.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * AD7298 SPI ADC driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#ifndef IIO_ADC_AD7298_H_ -#define IIO_ADC_AD7298_H_ - -/** - * struct ad7298_platform_data - Platform data for the ad7298 ADC driver - * @ext_ref: Whether to use an external reference voltage. - **/ -struct ad7298_platform_data { - bool ext_ref; -}; - -#endif /* IIO_ADC_AD7298_H_ */ diff --git a/include/linux/platform_data/ad7298.h b/include/linux/platform_data/ad7298.h new file mode 100644 index 000000000000..fbf8adf1363a --- /dev/null +++ b/include/linux/platform_data/ad7298.h @@ -0,0 +1,20 @@ +/* + * AD7298 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef __LINUX_PLATFORM_DATA_AD7298_H__ +#define __LINUX_PLATFORM_DATA_AD7298_H__ + +/** + * struct ad7298_platform_data - Platform data for the ad7298 ADC driver + * @ext_ref: Whether to use an external reference voltage. + **/ +struct ad7298_platform_data { + bool ext_ref; +}; + +#endif /* IIO_ADC_AD7298_H_ */ -- cgit v1.2.3 From ffd8a6e7a77802dd09559be78dea63956aa8f1d5 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Mon, 5 Nov 2012 15:48:26 +0100 Subject: iio: adc: Add viperboard adc driver This adds the mfd cell to use the adc part of the Nano River Technologies viperboard. Signed-off-by: Lars Poeschel Reviewed-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Samuel Ortiz --- drivers/iio/adc/Kconfig | 7 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/viperboard_adc.c | 181 +++++++++++++++++++++++++++++++++++++++ drivers/mfd/viperboard.c | 3 + include/linux/mfd/viperboard.h | 1 + 5 files changed, 193 insertions(+) create mode 100644 drivers/iio/adc/viperboard_adc.c (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 1401ed1af39f..bb3053739c80 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -67,4 +67,11 @@ config TI_AM335X_ADC Say yes here to build support for Texas Instruments ADC driver which is also a MFD client. +config VIPERBOARD_ADC + tristate "Viperboard ADC support" + depends on MFD_VIPERBOARD && USB + help + Say yes here to access the ADC part of the Nano River + Technologies Viperboard. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 4410a90fc84b..4268fa987fe6 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o +obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o \ No newline at end of file diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c new file mode 100644 index 000000000000..10136a8b20d4 --- /dev/null +++ b/drivers/iio/adc/viperboard_adc.c @@ -0,0 +1,181 @@ +/* + * Nano River Technologies viperboard IIO ADC driver + * + * (C) 2012 by Lemonage GmbH + * Author: Lars Poeschel + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define VPRBRD_ADC_CMD_GET 0x00 + +struct vprbrd_adc_msg { + u8 cmd; + u8 chan; + u8 val; +} __packed; + +struct vprbrd_adc { + struct vprbrd *vb; +}; + +#define VPRBRD_ADC_CHANNEL(_index) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ +} + +static struct iio_chan_spec const vprbrd_adc_iio_channels[] = { + VPRBRD_ADC_CHANNEL(0), + VPRBRD_ADC_CHANNEL(1), + VPRBRD_ADC_CHANNEL(2), + VPRBRD_ADC_CHANNEL(3), +}; + +static int vprbrd_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + int ret, error = 0; + struct vprbrd_adc *adc = iio_priv(iio_dev); + struct vprbrd *vb = adc->vb; + struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf; + + switch (info) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&vb->lock); + + admsg->cmd = VPRBRD_ADC_CMD_GET; + admsg->chan = chan->scan_index; + admsg->val = 0x00; + + ret = usb_control_msg(vb->usb_dev, + usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC, + VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg, + sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); + if (ret != sizeof(struct vprbrd_adc_msg)) { + dev_err(&iio_dev->dev, "usb send error on adc read\n"); + error = -EREMOTEIO; + } + + ret = usb_control_msg(vb->usb_dev, + usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg, + sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); + + *val = admsg->val; + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_adc_msg)) { + dev_err(&iio_dev->dev, "usb recv error on adc read\n"); + error = -EREMOTEIO; + } + + if (error) + goto error; + + return IIO_VAL_INT; + default: + error = -EINVAL; + break; + } +error: + return error; +} + +static const struct iio_info vprbrd_adc_iio_info = { + .read_raw = &vprbrd_iio_read_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit vprbrd_adc_probe(struct platform_device *pdev) +{ + struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); + struct vprbrd_adc *adc; + struct iio_dev *indio_dev; + int ret; + + /* registering iio */ + indio_dev = iio_device_alloc(sizeof(*adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + adc = iio_priv(indio_dev); + adc->vb = vb; + indio_dev->name = "viperboard adc"; + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &vprbrd_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = vprbrd_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "could not register iio (adc)"); + goto error; + } + + platform_set_drvdata(pdev, indio_dev); + + return 0; + +error: + iio_device_free(indio_dev); + return ret; +} + +static int __devexit vprbrd_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_device_free(indio_dev); + + return 0; +} + +static struct platform_driver vprbrd_adc_driver = { + .driver = { + .name = "viperboard-adc", + .owner = THIS_MODULE, + }, + .probe = vprbrd_adc_probe, + .remove = __devexit_p(vprbrd_adc_remove), +}; + +module_platform_driver(vprbrd_adc_driver); + +MODULE_AUTHOR("Lars Poeschel "); +MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:viperboard-adc"); diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index 4f74ec868a52..276122b37199 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -44,6 +44,9 @@ static struct mfd_cell vprbrd_devs[] = { { .name = "viperboard-i2c", }, + { + .name = "viperboard-adc", + }, }; static int vprbrd_probe(struct usb_interface *interface, diff --git a/include/linux/mfd/viperboard.h b/include/linux/mfd/viperboard.h index ef7851430d86..193452848c04 100644 --- a/include/linux/mfd/viperboard.h +++ b/include/linux/mfd/viperboard.h @@ -46,6 +46,7 @@ #define VPRBRD_USB_REQUEST_I2C 0xe9 #define VPRBRD_USB_REQUEST_MAJOR 0xea #define VPRBRD_USB_REQUEST_MINOR 0xeb +#define VPRBRD_USB_REQUEST_ADC 0xec #define VPRBRD_USB_REQUEST_GPIOA 0xed #define VPRBRD_USB_REQUEST_GPIOB 0xdd -- cgit v1.2.3 From 78026a6fde8f7b0ca77c059da11f476d69dfde3b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Nov 2012 13:36:00 +0000 Subject: iio:imu:adis: Add debugfs register access support Provide a IIO debugfs register access function for the ADIS library. This function can be used by individual drivers to allow raw register access via debugfs. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 23 +++++++++++++++++++++++ include/linux/iio/imu/adis.h | 11 +++++++++++ 2 files changed, 34 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 8259b774078f..28d4df2fe76a 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -135,6 +135,29 @@ error_ret: } EXPORT_SYMBOL_GPL(adis_read_reg_16); +#ifdef CONFIG_DEBUG_FS + +int adis_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, unsigned int *readval) +{ + struct adis *adis = iio_device_get_drvdata(indio_dev); + + if (readval) { + uint16_t val16; + int ret; + + ret = adis_read_reg_16(adis, reg, &val16); + *readval = val16; + + return ret; + } else { + return adis_write_reg_16(adis, reg, writeval); + } +} +EXPORT_SYMBOL(adis_debugfs_reg_access); + +#endif + /** * adis_enable_irq() - Enable or disable data ready IRQ * @adis: The adis device diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 8c3304d44b97..fce7bc3ba0b5 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -183,4 +183,15 @@ static inline void adis_remove_trigger(struct adis *adis) #endif /* CONFIG_IIO_BUFFER */ +#ifdef CONFIG_DEBUG_FS + +int adis_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, unsigned int *readval); + +#else + +#define adis_debugfs_reg_access NULL + +#endif + #endif -- cgit v1.2.3 From 57a1228a06b7a5939a8b0078a92b44fa30855bcb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Nov 2012 13:36:00 +0000 Subject: iio:imu:adis: Add support for 32bit registers Some of the newer generation devices from the ADIS16XXX family have 32bit wide register which spans two 16bit wide registers. This patch adds support for reading and writing a 32bit wide register. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 145 +++++++++++++++++++++++++++--------------- drivers/iio/imu/adis_buffer.c | 2 + include/linux/iio/imu/adis.h | 81 +++++++++++++++++++++-- 3 files changed, 171 insertions(+), 57 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 28d4df2fe76a..280a49584adf 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -27,36 +27,10 @@ #define ADIS_MSC_CTRL_DATA_RDY_DIO2 BIT(0) #define ADIS_GLOB_CMD_SW_RESET BIT(7) -/** - * adis_write_reg_8() - Write single byte to a register - * @adis: The adis device - * @reg: The address of the register to be written - * @val: The value to write - */ -int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val) -{ - int ret; - - mutex_lock(&adis->txrx_lock); - adis->tx[0] = ADIS_WRITE_REG(reg); - adis->tx[1] = val; - - ret = spi_write(adis->spi, adis->tx, 2); - mutex_unlock(&adis->txrx_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(adis_write_reg_8); - -/** - * adis_write_reg_16() - Write 2 bytes to a pair of registers - * @adis: The adis device - * @reg: The address of the lower of the two registers - * @val: Value to be written - */ -int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t value) +int adis_write_reg(struct adis *adis, unsigned int reg, + unsigned int value, unsigned int size) { - int ret; + int ret, i; struct spi_message msg; struct spi_transfer xfers[] = { { @@ -69,33 +43,69 @@ int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t value) .tx_buf = adis->tx + 2, .bits_per_word = 8, .len = 2, + .cs_change = 1, + .delay_usecs = adis->data->write_delay, + }, { + .tx_buf = adis->tx + 4, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = adis->data->write_delay, + }, { + .tx_buf = adis->tx + 6, + .bits_per_word = 8, + .len = 2, .delay_usecs = adis->data->write_delay, }, }; mutex_lock(&adis->txrx_lock); - adis->tx[0] = ADIS_WRITE_REG(reg); - adis->tx[1] = value & 0xff; - adis->tx[2] = ADIS_WRITE_REG(reg + 1); - adis->tx[3] = (value >> 8) & 0xff; spi_message_init(&msg); - spi_message_add_tail(&xfers[0], &msg); - spi_message_add_tail(&xfers[1], &msg); + switch (size) { + case 4: + adis->tx[6] = ADIS_WRITE_REG(reg + 3); + adis->tx[7] = (value >> 24) & 0xff; + adis->tx[4] = ADIS_WRITE_REG(reg + 2); + adis->tx[5] = (value >> 16) & 0xff; + case 2: + adis->tx[2] = ADIS_WRITE_REG(reg + 1); + adis->tx[3] = (value >> 8) & 0xff; + case 1: + adis->tx[0] = ADIS_WRITE_REG(reg); + adis->tx[1] = value & 0xff; + break; + default: + ret = -EINVAL; + goto out_unlock; + } + + xfers[size - 1].cs_change = 0; + + for (i = 0; i < size; i++) + spi_message_add_tail(&xfers[i], &msg); + ret = spi_sync(adis->spi, &msg); + if (ret) { + dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n", + reg, ret); + } + +out_unlock: mutex_unlock(&adis->txrx_lock); return ret; } -EXPORT_SYMBOL_GPL(adis_write_reg_16); +EXPORT_SYMBOL_GPL(adis_write_reg); /** - * adis_read_reg_16() - read 2 bytes from a 16-bit register + * adis_read_reg() - read 2 bytes from a 16-bit register * @adis: The adis device * @reg: The address of the lower of the two registers * @val: The value read back from the device */ -int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val) +int adis_read_reg(struct adis *adis, unsigned int reg, + unsigned int *val, unsigned int size) { struct spi_message msg; int ret; @@ -107,33 +117,61 @@ int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val) .cs_change = 1, .delay_usecs = adis->data->read_delay, }, { + .tx_buf = adis->tx + 2, .rx_buf = adis->rx, .bits_per_word = 8, .len = 2, + .cs_change = 1, + .delay_usecs = adis->data->read_delay, + }, { + .rx_buf = adis->rx + 2, + .bits_per_word = 8, + .len = 2, .delay_usecs = adis->data->read_delay, }, }; mutex_lock(&adis->txrx_lock); - adis->tx[0] = ADIS_READ_REG(reg); - adis->tx[1] = 0; - spi_message_init(&msg); - spi_message_add_tail(&xfers[0], &msg); - spi_message_add_tail(&xfers[1], &msg); + + switch (size) { + case 4: + adis->tx[0] = ADIS_READ_REG(reg + 2); + adis->tx[1] = 0; + spi_message_add_tail(&xfers[0], &msg); + case 2: + adis->tx[2] = ADIS_READ_REG(reg); + adis->tx[3] = 0; + spi_message_add_tail(&xfers[1], &msg); + spi_message_add_tail(&xfers[2], &msg); + break; + default: + ret = -EINVAL; + goto out_unlock; + } + ret = spi_sync(adis->spi, &msg); if (ret) { - dev_err(&adis->spi->dev, "Failed to read 16 bit register 0x%02X: %d\n", + dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n", reg, ret); - goto error_ret; + goto out_unlock; } - *val = get_unaligned_be16(adis->rx); -error_ret: + switch (size) { + case 4: + *val = get_unaligned_be32(adis->rx); + break; + case 2: + *val = get_unaligned_be16(adis->rx + 2); + break; + } + +out_unlock: mutex_unlock(&adis->txrx_lock); + return ret; } -EXPORT_SYMBOL_GPL(adis_read_reg_16); +EXPORT_SYMBOL_GPL(adis_read_reg); #ifdef CONFIG_DEBUG_FS @@ -304,25 +342,26 @@ int adis_single_conversion(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int error_mask, int *val) { struct adis *adis = iio_device_get_drvdata(indio_dev); - uint16_t val16; + unsigned int uval; int ret; mutex_lock(&indio_dev->mlock); - ret = adis_read_reg_16(adis, chan->address, &val16); + ret = adis_read_reg(adis, chan->address, &uval, + chan->scan_type.storagebits / 8); if (ret) goto err_unlock; - if (val16 & error_mask) { + if (uval & error_mask) { ret = adis_check_status(adis); if (ret) goto err_unlock; } if (chan->scan_type.sign == 's') - *val = sign_extend32(val16, chan->scan_type.realbits - 1); + *val = sign_extend32(uval, chan->scan_type.realbits - 1); else - *val = val16 & ((1 << chan->scan_type.realbits) - 1); + *val = uval & ((1 << chan->scan_type.realbits) - 1); ret = IIO_VAL_INT; err_unlock: diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index a91b4cbdc73a..785713349e9f 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -64,6 +64,8 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, for (i = 0; i < indio_dev->num_channels; i++, chan++) { if (!test_bit(chan->scan_index, scan_mask)) continue; + if (chan->scan_type.storagebits == 32) + *tx++ = cpu_to_be16((chan->address + 2) << 8); *tx++ = cpu_to_be16(chan->address << 8); } diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index fce7bc3ba0b5..6402a08c0e65 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -53,7 +53,7 @@ struct adis { struct spi_transfer *xfer; void *buffer; - uint8_t tx[8] ____cacheline_aligned; + uint8_t tx[10] ____cacheline_aligned; uint8_t rx[4]; }; @@ -61,9 +61,82 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev, struct spi_device *spi, const struct adis_data *data); int adis_reset(struct adis *adis); -int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val); -int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t val); -int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val); +int adis_write_reg(struct adis *adis, unsigned int reg, + unsigned int val, unsigned int size); +int adis_read_reg(struct adis *adis, unsigned int reg, + unsigned int *val, unsigned int size); + +/** + * adis_write_reg_8() - Write single byte to a register + * @adis: The adis device + * @reg: The address of the register to be written + * @value: The value to write + */ +static inline int adis_write_reg_8(struct adis *adis, unsigned int reg, + uint8_t val) +{ + return adis_write_reg(adis, reg, val, 1); +} + +/** + * adis_write_reg_16() - Write 2 bytes to a pair of registers + * @adis: The adis device + * @reg: The address of the lower of the two registers + * @value: Value to be written + */ +static inline int adis_write_reg_16(struct adis *adis, unsigned int reg, + uint16_t val) +{ + return adis_write_reg(adis, reg, val, 2); +} + +/** + * adis_write_reg_32() - write 4 bytes to four registers + * @adis: The adis device + * @reg: The address of the lower of the four register + * @value: Value to be written + */ +static inline int adis_write_reg_32(struct adis *adis, unsigned int reg, + uint32_t val) +{ + return adis_write_reg(adis, reg, val, 4); +} + +/** + * adis_read_reg_16() - read 2 bytes from a 16-bit register + * @adis: The adis device + * @reg: The address of the lower of the two registers + * @val: The value read back from the device + */ +static inline int adis_read_reg_16(struct adis *adis, unsigned int reg, + uint16_t *val) +{ + unsigned int tmp; + int ret; + + ret = adis_read_reg(adis, reg, &tmp, 2); + *val = tmp; + + return ret; +} + +/** + * adis_read_reg_32() - read 4 bytes from a 32-bit register + * @adis: The adis device + * @reg: The address of the lower of the two registers + * @val: The value read back from the device + */ +static inline int adis_read_reg_32(struct adis *adis, unsigned int reg, + uint32_t *val) +{ + unsigned int tmp; + int ret; + + ret = adis_read_reg(adis, reg, &tmp, 4); + *val = tmp; + + return ret; +} int adis_enable_irq(struct adis *adis, bool enable); int adis_check_status(struct adis *adis); -- cgit v1.2.3 From 9caed0d9d6db12cb6d81ba68d5bc98432d6b4711 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Nov 2012 13:36:00 +0000 Subject: iio:gyro: Add support for the ADIS16136 gyroscope This patch adds support for the ADIS16133, ADIS16135, ADIS16136 single channel gyroscopes. The main difference between them is the sensor precision. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/Kconfig | 9 + drivers/iio/gyro/Makefile | 1 + drivers/iio/gyro/adis16136.c | 581 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 591 insertions(+) create mode 100644 drivers/iio/gyro/adis16136.c (limited to 'drivers/iio') diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 21e27e2fc68c..48ed1483ff27 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -3,6 +3,15 @@ # menu "Digital gyroscope sensors" +config ADIS16136 + tristate "Analog devices ADIS16136 and similar gyroscopes driver" + depends on SPI_MASTER + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say yes here to build support for the Analog Devices ADIS16133, ADIS16135, + ADIS16136 gyroscope devices. + config HID_SENSOR_GYRO_3D depends on HID_SENSOR_HUB select IIO_BUFFER diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile index 8a895d9fcbce..702a058907e3 100644 --- a/drivers/iio/gyro/Makefile +++ b/drivers/iio/gyro/Makefile @@ -2,4 +2,5 @@ # Makefile for industrial I/O gyroscope sensor drivers # +obj-$(CONFIG_ADIS16136) += adis16136.o obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c new file mode 100644 index 000000000000..05486dfd721b --- /dev/null +++ b/drivers/iio/gyro/adis16136.c @@ -0,0 +1,581 @@ +/* + * ADIS16133/ADIS16135/ADIS16136 gyroscope driver + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define ADIS16136_REG_FLASH_CNT 0x00 +#define ADIS16136_REG_TEMP_OUT 0x02 +#define ADIS16136_REG_GYRO_OUT2 0x04 +#define ADIS16136_REG_GYRO_OUT 0x06 +#define ADIS16136_REG_GYRO_OFF2 0x08 +#define ADIS16136_REG_GYRO_OFF 0x0A +#define ADIS16136_REG_ALM_MAG1 0x10 +#define ADIS16136_REG_ALM_MAG2 0x12 +#define ADIS16136_REG_ALM_SAMPL1 0x14 +#define ADIS16136_REG_ALM_SAMPL2 0x16 +#define ADIS16136_REG_ALM_CTRL 0x18 +#define ADIS16136_REG_GPIO_CTRL 0x1A +#define ADIS16136_REG_MSC_CTRL 0x1C +#define ADIS16136_REG_SMPL_PRD 0x1E +#define ADIS16136_REG_AVG_CNT 0x20 +#define ADIS16136_REG_DEC_RATE 0x22 +#define ADIS16136_REG_SLP_CTRL 0x24 +#define ADIS16136_REG_DIAG_STAT 0x26 +#define ADIS16136_REG_GLOB_CMD 0x28 +#define ADIS16136_REG_LOT1 0x32 +#define ADIS16136_REG_LOT2 0x34 +#define ADIS16136_REG_LOT3 0x36 +#define ADIS16136_REG_PROD_ID 0x38 +#define ADIS16136_REG_SERIAL_NUM 0x3A + +#define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL 2 +#define ADIS16136_DIAG_STAT_SPI_FAIL 3 +#define ADIS16136_DIAG_STAT_SELF_TEST_FAIL 5 +#define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL 6 + +#define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11) +#define ADIS16136_MSC_CTRL_SELF_TEST BIT(10) + +struct adis16136_chip_info { + unsigned int precision; + unsigned int fullscale; +}; + +struct adis16136 { + const struct adis16136_chip_info *chip_info; + + struct adis adis; +}; + +#ifdef CONFIG_DEBUG_FS + +static ssize_t adis16136_show_serial(struct file *file, + char __user *userbuf, size_t count, loff_t *ppos) +{ + struct adis16136 *adis16136 = file->private_data; + uint16_t lot1, lot2, lot3, serial; + char buf[20]; + size_t len; + int ret; + + ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM, + &serial); + if (ret < 0) + return ret; + + ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1); + if (ret < 0) + return ret; + + ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2); + if (ret < 0) + return ret; + + ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3); + if (ret < 0) + return ret; + + len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2, + lot3, serial); + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations adis16136_serial_fops = { + .open = simple_open, + .read = adis16136_show_serial, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static int adis16136_show_product_id(void *arg, u64 *val) +{ + struct adis16136 *adis16136 = arg; + u16 prod_id; + int ret; + + ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID, + &prod_id); + if (ret < 0) + return ret; + + *val = prod_id; + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(adis16136_product_id_fops, + adis16136_show_product_id, NULL, "%llu\n"); + +static int adis16136_show_flash_count(void *arg, u64 *val) +{ + struct adis16136 *adis16136 = arg; + uint16_t flash_count; + int ret; + + ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT, + &flash_count); + if (ret < 0) + return ret; + + *val = flash_count; + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(adis16136_flash_count_fops, + adis16136_show_flash_count, NULL, "%lld\n"); + +static int adis16136_debugfs_init(struct iio_dev *indio_dev) +{ + struct adis16136 *adis16136 = iio_priv(indio_dev); + + debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry, + adis16136, &adis16136_serial_fops); + debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry, + adis16136, &adis16136_product_id_fops); + debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry, + adis16136, &adis16136_flash_count_fops); + + return 0; +} + +#else + +static int adis16136_debugfs_init(struct iio_dev *indio_dev) +{ + return 0; +} + +#endif + +static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq) +{ + unsigned int t; + + t = 32768 / freq; + if (t < 0xf) + t = 0xf; + else if (t > 0xffff) + t = 0xffff; + else + t--; + + return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t); +} + +static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq) +{ + uint16_t t; + int ret; + + ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t); + if (ret < 0) + return ret; + + *freq = 32768 / (t + 1); + + return 0; +} + +static ssize_t adis16136_write_frequency(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adis16136 *adis16136 = iio_priv(indio_dev); + long val; + int ret; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + + if (val == 0) + return -EINVAL; + + ret = adis16136_set_freq(adis16136, val); + + return ret ? ret : len; +} + +static ssize_t adis16136_read_frequency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adis16136 *adis16136 = iio_priv(indio_dev); + unsigned int freq; + int ret; + + ret = adis16136_get_freq(adis16136, &freq); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", freq); +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16136_read_frequency, + adis16136_write_frequency); + +static const unsigned adis16136_3db_divisors[] = { + [0] = 2, /* Special case */ + [1] = 6, + [2] = 12, + [3] = 25, + [4] = 50, + [5] = 100, + [6] = 200, + [7] = 200, /* Not a valid setting */ +}; + +static int adis16136_set_filter(struct iio_dev *indio_dev, int val) +{ + struct adis16136 *adis16136 = iio_priv(indio_dev); + unsigned int freq; + int i, ret; + + ret = adis16136_get_freq(adis16136, &freq); + if (ret < 0) + return ret; + + for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) { + if (freq / adis16136_3db_divisors[i] >= val) + break; + } + + return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i); +} + +static int adis16136_get_filter(struct iio_dev *indio_dev, int *val) +{ + struct adis16136 *adis16136 = iio_priv(indio_dev); + unsigned int freq; + uint16_t val16; + int ret; + + mutex_lock(&indio_dev->mlock); + + ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16); + if (ret < 0) + goto err_unlock; + + ret = adis16136_get_freq(adis16136, &freq); + if (ret < 0) + goto err_unlock; + + *val = freq / adis16136_3db_divisors[val16 & 0x07]; + +err_unlock: + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : IIO_VAL_INT; +} + +static int adis16136_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + struct adis16136 *adis16136 = iio_priv(indio_dev); + uint32_t val32; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, 0, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL_VEL: + *val = adis16136->chip_info->precision; + *val2 = (adis16136->chip_info->fullscale << 16); + return IIO_VAL_FRACTIONAL; + case IIO_TEMP: + *val = 10; + *val2 = 697000; /* 0.010697 degree Celsius */ + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + ret = adis_read_reg_32(&adis16136->adis, + ADIS16136_REG_GYRO_OFF2, &val32); + if (ret < 0) + return ret; + + *val = sign_extend32(val32, 31); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return adis16136_get_filter(indio_dev, val); + default: + return -EINVAL; + } +} + +static int adis16136_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int val, int val2, long info) +{ + struct adis16136 *adis16136 = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_CALIBBIAS: + return adis_write_reg_32(&adis16136->adis, + ADIS16136_REG_GYRO_OFF2, val); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return adis16136_set_filter(indio_dev, val); + default: + break; + } + + return -EINVAL; +} + +enum { + ADIS16136_SCAN_GYRO, + ADIS16136_SCAN_TEMP, +}; + +static const struct iio_chan_spec adis16136_channels[] = { + { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT, + .address = ADIS16136_REG_GYRO_OUT2, + .scan_index = ADIS16136_SCAN_GYRO, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, { + .type = IIO_TEMP, + .indexed = 1, + .channel = 0, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, + .address = ADIS16136_REG_TEMP_OUT, + .scan_index = ADIS16136_SCAN_TEMP, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static struct attribute *adis16136_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16136_attribute_group = { + .attrs = adis16136_attributes, +}; + +static const struct iio_info adis16136_info = { + .driver_module = THIS_MODULE, + .attrs = &adis16136_attribute_group, + .read_raw = &adis16136_read_raw, + .write_raw = &adis16136_write_raw, + .update_scan_mode = adis_update_scan_mode, + .debugfs_reg_access = adis_debugfs_reg_access, +}; + +static int adis16136_stop_device(struct iio_dev *indio_dev) +{ + struct adis16136 *adis16136 = iio_priv(indio_dev); + int ret; + + ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff); + if (ret) + dev_err(&indio_dev->dev, + "Could not power down device: %d\n", ret); + + return ret; +} + +static int adis16136_initial_setup(struct iio_dev *indio_dev) +{ + struct adis16136 *adis16136 = iio_priv(indio_dev); + unsigned int device_id; + uint16_t prod_id; + int ret; + + ret = adis_initial_startup(&adis16136->adis); + if (ret) + return ret; + + ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID, + &prod_id); + if (ret) + return ret; + + sscanf(indio_dev->name, "adis%u\n", &device_id); + + if (prod_id != device_id) + dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", + device_id, prod_id); + + return 0; +} + +static const char * const adis16136_status_error_msgs[] = { + [ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed", + [ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure", + [ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error", + [ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error", +}; + +static const struct adis_data adis16136_data = { + .diag_stat_reg = ADIS16136_REG_DIAG_STAT, + .glob_cmd_reg = ADIS16136_REG_GLOB_CMD, + .msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, + + .self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, + .startup_delay = 80, + + .read_delay = 10, + .write_delay = 10, + + .status_error_msgs = adis16136_status_error_msgs, + .status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) | + BIT(ADIS16136_DIAG_STAT_SPI_FAIL) | + BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) | + BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL), +}; + +enum adis16136_id { + ID_ADIS16133, + ID_ADIS16135, + ID_ADIS16136, +}; + +static const struct adis16136_chip_info adis16136_chip_info[] = { + [ID_ADIS16133] = { + .precision = IIO_DEGREE_TO_RAD(1200), + .fullscale = 24000, + }, + [ID_ADIS16135] = { + .precision = IIO_DEGREE_TO_RAD(300), + .fullscale = 24000, + }, + [ID_ADIS16136] = { + .precision = IIO_DEGREE_TO_RAD(450), + .fullscale = 24623, + }, +}; + +static int adis16136_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct adis16136 *adis16136; + struct iio_dev *indio_dev; + int ret; + + indio_dev = iio_device_alloc(sizeof(*adis16136)); + if (indio_dev == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, indio_dev); + + adis16136 = iio_priv(indio_dev); + + adis16136->chip_info = &adis16136_chip_info[id->driver_data]; + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->channels = adis16136_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16136_channels); + indio_dev->info = &adis16136_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data); + if (ret) + goto error_free_dev; + + ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL); + if (ret) + goto error_free_dev; + + ret = adis16136_initial_setup(indio_dev); + if (ret) + goto error_cleanup_buffer; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_stop_device; + + adis16136_debugfs_init(indio_dev); + + return 0; + +error_stop_device: + adis16136_stop_device(indio_dev); +error_cleanup_buffer: + adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev); +error_free_dev: + iio_device_free(indio_dev); + return ret; +} + +static int adis16136_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adis16136 *adis16136 = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + adis16136_stop_device(indio_dev); + + adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id adis16136_ids[] = { + { "adis16133", ID_ADIS16133 }, + { "adis16135", ID_ADIS16135 }, + { "adis16136", ID_ADIS16136 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adis16136_ids); + +static struct spi_driver adis16136_driver = { + .driver = { + .name = "adis16136", + .owner = THIS_MODULE, + }, + .id_table = adis16136_ids, + .probe = adis16136_probe, + .remove = adis16136_remove, +}; +module_spi_driver(adis16136_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 484a0bf091c93c379e6524a17bb037c33c898e01 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Nov 2012 13:36:00 +0000 Subject: iio:imu:adis: Add paging support Some of the newer generation devices from the ADIS16XXX series have more registers than what can be supported with the current register addressing scheme. These devices implement register paging to support a larger register range. Each page is 128 registers large and the currently active page can be selected via register 0x00 in each page. This patch implements transparent paging inside the common adis library. The register read/write interface stays the same and when a register is accessed the library automatically switches to the correct page if it is not already selected. The page number is encoded in the upper bits of the register number, e.g. register 0x5 of page 1 is 0x85. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 70 +++++++++++++++++++++++++++++++++---------- drivers/iio/imu/adis_buffer.c | 15 ++++++++++ include/linux/iio/imu/adis.h | 10 +++++-- 3 files changed, 77 insertions(+), 18 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 280a49584adf..c4ea04ffa60e 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -30,6 +30,7 @@ int adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value, unsigned int size) { + unsigned int page = reg / ADIS_PAGE_SIZE; int ret, i; struct spi_message msg; struct spi_transfer xfers[] = { @@ -56,39 +57,53 @@ int adis_write_reg(struct adis *adis, unsigned int reg, .bits_per_word = 8, .len = 2, .delay_usecs = adis->data->write_delay, + }, { + .tx_buf = adis->tx + 8, + .bits_per_word = 8, + .len = 2, + .delay_usecs = adis->data->write_delay, }, }; mutex_lock(&adis->txrx_lock); spi_message_init(&msg); + + if (adis->current_page != page) { + adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); + adis->tx[1] = page; + spi_message_add_tail(&xfers[0], &msg); + } + switch (size) { case 4: - adis->tx[6] = ADIS_WRITE_REG(reg + 3); - adis->tx[7] = (value >> 24) & 0xff; - adis->tx[4] = ADIS_WRITE_REG(reg + 2); - adis->tx[5] = (value >> 16) & 0xff; + adis->tx[8] = ADIS_WRITE_REG(reg + 3); + adis->tx[9] = (value >> 24) & 0xff; + adis->tx[6] = ADIS_WRITE_REG(reg + 2); + adis->tx[7] = (value >> 16) & 0xff; case 2: - adis->tx[2] = ADIS_WRITE_REG(reg + 1); - adis->tx[3] = (value >> 8) & 0xff; + adis->tx[4] = ADIS_WRITE_REG(reg + 1); + adis->tx[5] = (value >> 8) & 0xff; case 1: - adis->tx[0] = ADIS_WRITE_REG(reg); - adis->tx[1] = value & 0xff; + adis->tx[2] = ADIS_WRITE_REG(reg); + adis->tx[3] = value & 0xff; break; default: ret = -EINVAL; goto out_unlock; } - xfers[size - 1].cs_change = 0; + xfers[size].cs_change = 0; - for (i = 0; i < size; i++) + for (i = 1; i <= size; i++) spi_message_add_tail(&xfers[i], &msg); ret = spi_sync(adis->spi, &msg); if (ret) { dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n", reg, ret); + } else { + adis->current_page = page; } out_unlock: @@ -107,6 +122,7 @@ EXPORT_SYMBOL_GPL(adis_write_reg); int adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val, unsigned int size) { + unsigned int page = reg / ADIS_PAGE_SIZE; struct spi_message msg; int ret; struct spi_transfer xfers[] = { @@ -115,9 +131,15 @@ int adis_read_reg(struct adis *adis, unsigned int reg, .bits_per_word = 8, .len = 2, .cs_change = 1, - .delay_usecs = adis->data->read_delay, + .delay_usecs = adis->data->write_delay, }, { .tx_buf = adis->tx + 2, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = adis->data->read_delay, + }, { + .tx_buf = adis->tx + 4, .rx_buf = adis->rx, .bits_per_word = 8, .len = 2, @@ -134,16 +156,22 @@ int adis_read_reg(struct adis *adis, unsigned int reg, mutex_lock(&adis->txrx_lock); spi_message_init(&msg); + if (adis->current_page != page) { + adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); + adis->tx[1] = page; + spi_message_add_tail(&xfers[0], &msg); + } + switch (size) { case 4: - adis->tx[0] = ADIS_READ_REG(reg + 2); - adis->tx[1] = 0; - spi_message_add_tail(&xfers[0], &msg); - case 2: - adis->tx[2] = ADIS_READ_REG(reg); + adis->tx[2] = ADIS_READ_REG(reg + 2); adis->tx[3] = 0; spi_message_add_tail(&xfers[1], &msg); + case 2: + adis->tx[4] = ADIS_READ_REG(reg); + adis->tx[5] = 0; spi_message_add_tail(&xfers[2], &msg); + spi_message_add_tail(&xfers[3], &msg); break; default: ret = -EINVAL; @@ -155,6 +183,8 @@ int adis_read_reg(struct adis *adis, unsigned int reg, dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n", reg, ret); goto out_unlock; + } else { + adis->current_page = page; } switch (size) { @@ -390,6 +420,14 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev, adis->data = data; iio_device_set_drvdata(indio_dev, adis); + if (data->has_paging) { + /* Need to set the page before first read/write */ + adis->current_page = -1; + } else { + /* Page will always be 0 */ + adis->current_page = 0; + } + return adis_enable_irq(adis, false); } EXPORT_SYMBOL_GPL(adis_init); diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index 785713349e9f..99d8e0b0dd34 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -83,10 +83,25 @@ static irqreturn_t adis_trigger_handler(int irq, void *p) if (!adis->buffer) return -ENOMEM; + if (adis->data->has_paging) { + mutex_lock(&adis->txrx_lock); + if (adis->current_page != 0) { + adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); + adis->tx[1] = 0; + spi_write(adis->spi, adis->tx, 2); + } + } + ret = spi_sync(adis->spi, &adis->msg); if (ret) dev_err(&adis->spi->dev, "Failed to read data: %d", ret); + + if (adis->data->has_paging) { + adis->current_page = 0; + mutex_unlock(&adis->txrx_lock); + } + /* Guaranteed to be aligned with 8 byte boundary */ if (indio_dev->scan_timestamp) { void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64); diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 6402a08c0e65..e82cd0827e91 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -14,8 +14,11 @@ #include #include -#define ADIS_WRITE_REG(reg) (0x80 | (reg)) -#define ADIS_READ_REG(reg) (reg) +#define ADIS_WRITE_REG(reg) ((0x80 | (reg))) +#define ADIS_READ_REG(reg) ((reg) & 0x7f) + +#define ADIS_PAGE_SIZE 0x80 +#define ADIS_REG_PAGE_ID 0x00 /** * struct adis_data - ADIS chip variant specific data @@ -40,6 +43,8 @@ struct adis_data { const char * const *status_error_msgs; unsigned int status_error_mask; + + bool has_paging; }; struct adis { @@ -51,6 +56,7 @@ struct adis { struct mutex txrx_lock; struct spi_message msg; struct spi_transfer *xfer; + unsigned int current_page; void *buffer; uint8_t tx[10] ____cacheline_aligned; -- cgit v1.2.3 From c4f0c6936762ecd6b453275611a785dfdee0d417 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Nov 2012 13:36:00 +0000 Subject: iio: Add pressure channel type This patch adds support for a new IIO channel type for pressure measurements. This can for example be used for barometric pressure sensors. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 24 ++++++++++++++++++++++++ drivers/iio/industrialio-core.c | 1 + include/linux/iio/types.h | 1 + 3 files changed, 26 insertions(+) (limited to 'drivers/iio') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 2f06d40fe07d..2e33dc6b2346 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -189,6 +189,14 @@ Description: A computed peak value based on the sum squared magnitude of the underlying value in the specified directions. +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_raw +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_raw +KernelVersion: 3.8 +Contact: linux-iio@vger.kernel.org +Description: + Raw pressure measurement from channel Y. Units after + application of scale and offset are kilopascal. + What: /sys/bus/iio/devices/iio:deviceX/in_accel_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset @@ -197,6 +205,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltage_offset What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_offset KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -226,6 +236,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_magn_scale What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_scale What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_scale What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_scale +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -245,6 +257,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibbias What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibbias +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibbias KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -262,6 +276,8 @@ What /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale What /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale what /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale what /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -275,6 +291,8 @@ What: /sys/.../iio:deviceX/in_voltage-voltage_scale_available What: /sys/.../iio:deviceX/out_voltageX_scale_available What: /sys/.../iio:deviceX/out_altvoltageX_scale_available What: /sys/.../iio:deviceX/in_capacitance_scale_available +What: /sys/.../iio:deviceX/in_pressure_scale_available +What: /sys/.../iio:deviceX/in_pressureY_scale_available KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -694,6 +712,8 @@ What: /sys/.../buffer/scan_elements/in_voltageY_en What: /sys/.../buffer/scan_elements/in_voltageY-voltageZ_en What: /sys/.../buffer/scan_elements/in_incli_x_en What: /sys/.../buffer/scan_elements/in_incli_y_en +What: /sys/.../buffer/scan_elements/in_pressureY_en +What: /sys/.../buffer/scan_elements/in_pressure_en KernelVersion: 2.6.37 Contact: linux-iio@vger.kernel.org Description: @@ -707,6 +727,8 @@ What: /sys/.../buffer/scan_elements/in_voltageY_type What: /sys/.../buffer/scan_elements/in_voltage_type What: /sys/.../buffer/scan_elements/in_voltageY_supply_type What: /sys/.../buffer/scan_elements/in_timestamp_type +What: /sys/.../buffer/scan_elements/in_pressureY_type +What: /sys/.../buffer/scan_elements/in_pressure_type KernelVersion: 2.6.37 Contact: linux-iio@vger.kernel.org Description: @@ -751,6 +773,8 @@ What: /sys/.../buffer/scan_elements/in_magn_z_index What: /sys/.../buffer/scan_elements/in_incli_x_index What: /sys/.../buffer/scan_elements/in_incli_y_index What: /sys/.../buffer/scan_elements/in_timestamp_index +What: /sys/.../buffer/scan_elements/in_pressureY_index +What: /sys/.../buffer/scan_elements/in_pressure_index KernelVersion: 2.6.37 Contact: linux-iio@vger.kernel.org Description: diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 060a4045be85..3dccd6c3a889 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -65,6 +65,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_CAPACITANCE] = "capacitance", [IIO_ALTVOLTAGE] = "altvoltage", [IIO_CCT] = "cct", + [IIO_PRESSURE] = "pressure", }; static const char * const iio_modifier_names[] = { diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 87b196a2d698..88bf0f0d27b4 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -28,6 +28,7 @@ enum iio_chan_type { IIO_CAPACITANCE, IIO_ALTVOLTAGE, IIO_CCT, + IIO_PRESSURE, }; enum iio_modifier { -- cgit v1.2.3 From 6807d7211327dbdd8df3692f3d26ca711514ba71 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Nov 2012 13:36:00 +0000 Subject: iio: Factor out fixed point number parsing into its own function Factor out the code for parsing fixed point numbers into its own function and make this function globally available. This allows us to reuse the code to parse fixed point numbers in individual IIO drivers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 98 ++++++++++++++++++++++++++--------------- include/linux/iio/iio.h | 3 ++ 2 files changed, 66 insertions(+), 35 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 3dccd6c3a889..8848f16c547b 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -408,6 +408,64 @@ static ssize_t iio_read_channel_info(struct device *dev, } } +/** + * iio_str_to_fixpoint() - Parse a fixed-point number from a string + * @str: The string to parse + * @fract_mult: Multiplier for the first decimal place, should be a power of 10 + * @integer: The integer part of the number + * @fract: The fractional part of the number + * + * Returns 0 on success, or a negative error code if the string could not be + * parsed. + */ +int iio_str_to_fixpoint(const char *str, int fract_mult, + int *integer, int *fract) +{ + int i = 0, f = 0; + bool integer_part = true, negative = false; + + if (str[0] == '-') { + negative = true; + str++; + } else if (str[0] == '+') { + str++; + } + + while (*str) { + if ('0' <= *str && *str <= '9') { + if (integer_part) { + i = i * 10 + *str - '0'; + } else { + f += fract_mult * (*str - '0'); + fract_mult /= 10; + } + } else if (*str == '\n') { + if (*(str + 1) == '\0') + break; + else + return -EINVAL; + } else if (*str == '.' && integer_part) { + integer_part = false; + } else { + return -EINVAL; + } + str++; + } + + if (negative) { + if (i) + i = -i; + else + f = -f; + } + + *integer = i; + *fract = f; + + return 0; +} +EXPORT_SYMBOL_GPL(iio_str_to_fixpoint); + static ssize_t iio_write_channel_info(struct device *dev, struct device_attribute *attr, const char *buf, @@ -415,8 +473,8 @@ static ssize_t iio_write_channel_info(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int ret, integer = 0, fract = 0, fract_mult = 100000; - bool integer_part = true, negative = false; + int ret, fract_mult = 100000; + int integer, fract; /* Assumes decimal - precision based on number of digits */ if (!indio_dev->info->write_raw) @@ -435,39 +493,9 @@ static ssize_t iio_write_channel_info(struct device *dev, return -EINVAL; } - if (buf[0] == '-') { - negative = true; - buf++; - } else if (buf[0] == '+') { - buf++; - } - - while (*buf) { - if ('0' <= *buf && *buf <= '9') { - if (integer_part) - integer = integer*10 + *buf - '0'; - else { - fract += fract_mult*(*buf - '0'); - fract_mult /= 10; - } - } else if (*buf == '\n') { - if (*(buf + 1) == '\0') - break; - else - return -EINVAL; - } else if (*buf == '.' && integer_part) { - integer_part = false; - } else { - return -EINVAL; - } - buf++; - } - if (negative) { - if (integer) - integer = -integer; - else - fract = -fract; - } + ret = iio_str_to_fixpoint(buf, fract_mult, &integer, &fract); + if (ret) + return ret; ret = indio_dev->info->write_raw(indio_dev, this_attr->c, integer, fract, this_attr->address); diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index adca93a999a7..da8c776ba0bd 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -620,6 +620,9 @@ static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev) }; #endif +int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer, + int *fract); + /** * IIO_DEGREE_TO_RAD() - Convert degree to rad * @deg: A value in degree -- cgit v1.2.3 From 2f3abe6cbb6c963ac790b40936b6761c9f0497b4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Nov 2012 13:36:00 +0000 Subject: iio:imu: Add support for the ADIS16480 and similar IMUs This patch adds support for the ADIS16375, ADIS16480, ADIS16485, ADIS16488 6 degree to 10 degree of freedom IMUs. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/imu/Kconfig | 16 + drivers/iio/imu/Makefile | 2 + drivers/iio/imu/adis.c | 3 + drivers/iio/imu/adis16480.c | 925 +++++++++++++++++++++++++++++++++++++++++++ include/linux/iio/imu/adis.h | 4 + 5 files changed, 950 insertions(+) create mode 100644 drivers/iio/imu/adis16480.c (limited to 'drivers/iio') diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index c24410c873c7..3d79a40e916b 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -1,3 +1,19 @@ +# +# IIO imu drivers configuration +# +menu "Inertial measurement units" + +config ADIS16480 + tristate "Analog Devices ADIS16480 and similar IMU driver" + depends on SPI + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say yes here to build support for Analog Devices ADIS16375, ADIS16480, + ADIS16485, ADIS16488 inertial sensors. + +endmenu + config IIO_ADIS_LIB tristate help diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 97676ab5723d..cfe57638f6f9 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -2,6 +2,8 @@ # Makefile for Inertial Measurement Units # +obj-$(CONFIG_ADIS16480) += adis16480.o + adis_lib-y += adis.o adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index c4ea04ffa60e..911255d41c1a 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -238,6 +238,9 @@ int adis_enable_irq(struct adis *adis, bool enable) int ret = 0; uint16_t msc; + if (adis->data->enable_irq) + return adis->data->enable_irq(adis, enable); + ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); if (ret) goto error_ret; diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c new file mode 100644 index 000000000000..a080b3515015 --- /dev/null +++ b/drivers/iio/imu/adis16480.c @@ -0,0 +1,925 @@ +/* + * ADIS16480 and similar IMUs driver + * + * Copyright 2012 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define ADIS16480_PAGE_SIZE 0x80 + +#define ADIS16480_REG(page, reg) ((page) * ADIS16480_PAGE_SIZE + (reg)) + +#define ADIS16480_REG_PAGE_ID 0x00 /* Same address on each page */ +#define ADIS16480_REG_SEQ_CNT ADIS16480_REG(0x00, 0x06) +#define ADIS16480_REG_SYS_E_FLA ADIS16480_REG(0x00, 0x08) +#define ADIS16480_REG_DIAG_STS ADIS16480_REG(0x00, 0x0A) +#define ADIS16480_REG_ALM_STS ADIS16480_REG(0x00, 0x0C) +#define ADIS16480_REG_TEMP_OUT ADIS16480_REG(0x00, 0x0E) +#define ADIS16480_REG_X_GYRO_OUT ADIS16480_REG(0x00, 0x10) +#define ADIS16480_REG_Y_GYRO_OUT ADIS16480_REG(0x00, 0x14) +#define ADIS16480_REG_Z_GYRO_OUT ADIS16480_REG(0x00, 0x18) +#define ADIS16480_REG_X_ACCEL_OUT ADIS16480_REG(0x00, 0x1C) +#define ADIS16480_REG_Y_ACCEL_OUT ADIS16480_REG(0x00, 0x20) +#define ADIS16480_REG_Z_ACCEL_OUT ADIS16480_REG(0x00, 0x24) +#define ADIS16480_REG_X_MAGN_OUT ADIS16480_REG(0x00, 0x28) +#define ADIS16480_REG_Y_MAGN_OUT ADIS16480_REG(0x00, 0x2A) +#define ADIS16480_REG_Z_MAGN_OUT ADIS16480_REG(0x00, 0x2C) +#define ADIS16480_REG_BAROM_OUT ADIS16480_REG(0x00, 0x2E) +#define ADIS16480_REG_X_DELTAANG_OUT ADIS16480_REG(0x00, 0x40) +#define ADIS16480_REG_Y_DELTAANG_OUT ADIS16480_REG(0x00, 0x44) +#define ADIS16480_REG_Z_DELTAANG_OUT ADIS16480_REG(0x00, 0x48) +#define ADIS16480_REG_X_DELTAVEL_OUT ADIS16480_REG(0x00, 0x4C) +#define ADIS16480_REG_Y_DELTAVEL_OUT ADIS16480_REG(0x00, 0x50) +#define ADIS16480_REG_Z_DELTAVEL_OUT ADIS16480_REG(0x00, 0x54) +#define ADIS16480_REG_PROD_ID ADIS16480_REG(0x00, 0x7E) + +#define ADIS16480_REG_X_GYRO_SCALE ADIS16480_REG(0x02, 0x04) +#define ADIS16480_REG_Y_GYRO_SCALE ADIS16480_REG(0x02, 0x06) +#define ADIS16480_REG_Z_GYRO_SCALE ADIS16480_REG(0x02, 0x08) +#define ADIS16480_REG_X_ACCEL_SCALE ADIS16480_REG(0x02, 0x0A) +#define ADIS16480_REG_Y_ACCEL_SCALE ADIS16480_REG(0x02, 0x0C) +#define ADIS16480_REG_Z_ACCEL_SCALE ADIS16480_REG(0x02, 0x0E) +#define ADIS16480_REG_X_GYRO_BIAS ADIS16480_REG(0x02, 0x10) +#define ADIS16480_REG_Y_GYRO_BIAS ADIS16480_REG(0x02, 0x14) +#define ADIS16480_REG_Z_GYRO_BIAS ADIS16480_REG(0x02, 0x18) +#define ADIS16480_REG_X_ACCEL_BIAS ADIS16480_REG(0x02, 0x1C) +#define ADIS16480_REG_Y_ACCEL_BIAS ADIS16480_REG(0x02, 0x20) +#define ADIS16480_REG_Z_ACCEL_BIAS ADIS16480_REG(0x02, 0x24) +#define ADIS16480_REG_X_HARD_IRON ADIS16480_REG(0x02, 0x28) +#define ADIS16480_REG_Y_HARD_IRON ADIS16480_REG(0x02, 0x2A) +#define ADIS16480_REG_Z_HARD_IRON ADIS16480_REG(0x02, 0x2C) +#define ADIS16480_REG_BAROM_BIAS ADIS16480_REG(0x02, 0x40) +#define ADIS16480_REG_FLASH_CNT ADIS16480_REG(0x02, 0x7C) + +#define ADIS16480_REG_GLOB_CMD ADIS16480_REG(0x03, 0x02) +#define ADIS16480_REG_FNCTIO_CTRL ADIS16480_REG(0x03, 0x06) +#define ADIS16480_REG_GPIO_CTRL ADIS16480_REG(0x03, 0x08) +#define ADIS16480_REG_CONFIG ADIS16480_REG(0x03, 0x0A) +#define ADIS16480_REG_DEC_RATE ADIS16480_REG(0x03, 0x0C) +#define ADIS16480_REG_SLP_CNT ADIS16480_REG(0x03, 0x10) +#define ADIS16480_REG_FILTER_BNK0 ADIS16480_REG(0x03, 0x16) +#define ADIS16480_REG_FILTER_BNK1 ADIS16480_REG(0x03, 0x18) +#define ADIS16480_REG_ALM_CNFG0 ADIS16480_REG(0x03, 0x20) +#define ADIS16480_REG_ALM_CNFG1 ADIS16480_REG(0x03, 0x22) +#define ADIS16480_REG_ALM_CNFG2 ADIS16480_REG(0x03, 0x24) +#define ADIS16480_REG_XG_ALM_MAGN ADIS16480_REG(0x03, 0x28) +#define ADIS16480_REG_YG_ALM_MAGN ADIS16480_REG(0x03, 0x2A) +#define ADIS16480_REG_ZG_ALM_MAGN ADIS16480_REG(0x03, 0x2C) +#define ADIS16480_REG_XA_ALM_MAGN ADIS16480_REG(0x03, 0x2E) +#define ADIS16480_REG_YA_ALM_MAGN ADIS16480_REG(0x03, 0x30) +#define ADIS16480_REG_ZA_ALM_MAGN ADIS16480_REG(0x03, 0x32) +#define ADIS16480_REG_XM_ALM_MAGN ADIS16480_REG(0x03, 0x34) +#define ADIS16480_REG_YM_ALM_MAGN ADIS16480_REG(0x03, 0x36) +#define ADIS16480_REG_ZM_ALM_MAGN ADIS16480_REG(0x03, 0x38) +#define ADIS16480_REG_BR_ALM_MAGN ADIS16480_REG(0x03, 0x3A) +#define ADIS16480_REG_FIRM_REV ADIS16480_REG(0x03, 0x78) +#define ADIS16480_REG_FIRM_DM ADIS16480_REG(0x03, 0x7A) +#define ADIS16480_REG_FIRM_Y ADIS16480_REG(0x03, 0x7C) + +#define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20) + +/* Each filter coefficent bank spans two pages */ +#define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \ + ADIS16480_REG((page) + 1, (x) - 60 + 8)) +#define ADIS16480_FIR_COEF_A(x) ADIS16480_FIR_COEF(0x05, (x)) +#define ADIS16480_FIR_COEF_B(x) ADIS16480_FIR_COEF(0x07, (x)) +#define ADIS16480_FIR_COEF_C(x) ADIS16480_FIR_COEF(0x09, (x)) +#define ADIS16480_FIR_COEF_D(x) ADIS16480_FIR_COEF(0x0B, (x)) + +struct adis16480_chip_info { + unsigned int num_channels; + const struct iio_chan_spec *channels; +}; + +struct adis16480 { + const struct adis16480_chip_info *chip_info; + + struct adis adis; +}; + +#ifdef CONFIG_DEBUG_FS + +static ssize_t adis16480_show_firmware_revision(struct file *file, + char __user *userbuf, size_t count, loff_t *ppos) +{ + struct adis16480 *adis16480 = file->private_data; + char buf[6]; + size_t len; + u16 rev; + int ret; + + ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_REV, &rev); + if (ret < 0) + return ret; + + len = snprintf(buf, sizeof(buf), "%x.%x\n", rev >> 8, rev & 0xff); + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations adis16480_firmware_revision_fops = { + .open = simple_open, + .read = adis16480_show_firmware_revision, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static ssize_t adis16480_show_firmware_date(struct file *file, + char __user *userbuf, size_t count, loff_t *ppos) +{ + struct adis16480 *adis16480 = file->private_data; + u16 md, year; + char buf[12]; + size_t len; + int ret; + + ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_Y, &year); + if (ret < 0) + return ret; + + ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_DM, &md); + if (ret < 0) + return ret; + + len = snprintf(buf, sizeof(buf), "%.2x-%.2x-%.4x\n", + md >> 8, md & 0xff, year); + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations adis16480_firmware_date_fops = { + .open = simple_open, + .read = adis16480_show_firmware_date, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static int adis16480_show_serial_number(void *arg, u64 *val) +{ + struct adis16480 *adis16480 = arg; + u16 serial; + int ret; + + ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_SERIAL_NUM, + &serial); + if (ret < 0) + return ret; + + *val = serial; + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(adis16480_serial_number_fops, + adis16480_show_serial_number, NULL, "0x%.4llx\n"); + +static int adis16480_show_product_id(void *arg, u64 *val) +{ + struct adis16480 *adis16480 = arg; + u16 prod_id; + int ret; + + ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_PROD_ID, + &prod_id); + if (ret < 0) + return ret; + + *val = prod_id; + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(adis16480_product_id_fops, + adis16480_show_product_id, NULL, "%llu\n"); + +static int adis16480_show_flash_count(void *arg, u64 *val) +{ + struct adis16480 *adis16480 = arg; + u32 flash_count; + int ret; + + ret = adis_read_reg_32(&adis16480->adis, ADIS16480_REG_FLASH_CNT, + &flash_count); + if (ret < 0) + return ret; + + *val = flash_count; + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(adis16480_flash_count_fops, + adis16480_show_flash_count, NULL, "%lld\n"); + +static int adis16480_debugfs_init(struct iio_dev *indio_dev) +{ + struct adis16480 *adis16480 = iio_priv(indio_dev); + + debugfs_create_file("firmware_revision", 0400, + indio_dev->debugfs_dentry, adis16480, + &adis16480_firmware_revision_fops); + debugfs_create_file("firmware_date", 0400, indio_dev->debugfs_dentry, + adis16480, &adis16480_firmware_date_fops); + debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry, + adis16480, &adis16480_serial_number_fops); + debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry, + adis16480, &adis16480_product_id_fops); + debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry, + adis16480, &adis16480_flash_count_fops); + + return 0; +} + +#else + +static int adis16480_debugfs_init(struct iio_dev *indio_dev) +{ + return 0; +} + +#endif + +static int adis16480_set_freq(struct adis16480 *st, unsigned int freq) +{ + unsigned int t; + + t = 2460000 / freq; + if (t > 2048) + t = 2048; + + if (t != 0) + t--; + + return adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t); +} + +static int adis16480_get_freq(struct adis16480 *st, unsigned int *freq) +{ + uint16_t t; + int ret; + + ret = adis_read_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, &t); + if (ret < 0) + return ret; + + *freq = 2460000 / (t + 1); + + return 0; +} + +static ssize_t adis16480_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adis16480 *st = iio_priv(indio_dev); + unsigned int freq; + int ret; + + ret = adis16480_get_freq(st, &freq); + if (ret < 0) + return ret; + + return sprintf(buf, "%d.%.3d\n", freq / 1000, freq % 1000); +} + +static ssize_t adis16480_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adis16480 *st = iio_priv(indio_dev); + int freq_int, freq_fract; + long val; + int ret; + + ret = iio_str_to_fixpoint(buf, 100, &freq_int, &freq_fract); + if (ret) + return ret; + + val = freq_int * 1000 + freq_fract; + + if (val <= 0) + return -EINVAL; + + ret = adis16480_set_freq(st, val); + + return ret ? ret : len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + adis16480_read_frequency, + adis16480_write_frequency); + +enum { + ADIS16480_SCAN_GYRO_X, + ADIS16480_SCAN_GYRO_Y, + ADIS16480_SCAN_GYRO_Z, + ADIS16480_SCAN_ACCEL_X, + ADIS16480_SCAN_ACCEL_Y, + ADIS16480_SCAN_ACCEL_Z, + ADIS16480_SCAN_MAGN_X, + ADIS16480_SCAN_MAGN_Y, + ADIS16480_SCAN_MAGN_Z, + ADIS16480_SCAN_BARO, + ADIS16480_SCAN_TEMP, +}; + +static const unsigned int adis16480_calibbias_regs[] = { + [ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_BIAS, + [ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_BIAS, + [ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_BIAS, + [ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_BIAS, + [ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_BIAS, + [ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_BIAS, + [ADIS16480_SCAN_MAGN_X] = ADIS16480_REG_X_HARD_IRON, + [ADIS16480_SCAN_MAGN_Y] = ADIS16480_REG_Y_HARD_IRON, + [ADIS16480_SCAN_MAGN_Z] = ADIS16480_REG_Z_HARD_IRON, + [ADIS16480_SCAN_BARO] = ADIS16480_REG_BAROM_BIAS, +}; + +static const unsigned int adis16480_calibscale_regs[] = { + [ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_SCALE, + [ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_SCALE, + [ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_SCALE, + [ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_SCALE, + [ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_SCALE, + [ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_SCALE, +}; + +static int adis16480_set_calibbias(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int bias) +{ + unsigned int reg = adis16480_calibbias_regs[chan->scan_index]; + struct adis16480 *st = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_MAGN: + case IIO_PRESSURE: + if (bias < -0x8000 || bias >= 0x8000) + return -EINVAL; + return adis_write_reg_16(&st->adis, reg, bias); + case IIO_ANGL_VEL: + case IIO_ACCEL: + return adis_write_reg_32(&st->adis, reg, bias); + default: + break; + } + + return -EINVAL; +} + +static int adis16480_get_calibbias(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *bias) +{ + unsigned int reg = adis16480_calibbias_regs[chan->scan_index]; + struct adis16480 *st = iio_priv(indio_dev); + uint16_t val16; + uint32_t val32; + int ret; + + switch (chan->type) { + case IIO_MAGN: + case IIO_PRESSURE: + ret = adis_read_reg_16(&st->adis, reg, &val16); + *bias = sign_extend32(val16, 15); + break; + case IIO_ANGL_VEL: + case IIO_ACCEL: + ret = adis_read_reg_32(&st->adis, reg, &val32); + *bias = sign_extend32(val32, 31); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + return IIO_VAL_INT; +} + +static int adis16480_set_calibscale(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int scale) +{ + unsigned int reg = adis16480_calibscale_regs[chan->scan_index]; + struct adis16480 *st = iio_priv(indio_dev); + + if (scale < -0x8000 || scale >= 0x8000) + return -EINVAL; + + return adis_write_reg_16(&st->adis, reg, scale); +} + +static int adis16480_get_calibscale(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *scale) +{ + unsigned int reg = adis16480_calibscale_regs[chan->scan_index]; + struct adis16480 *st = iio_priv(indio_dev); + uint16_t val16; + int ret; + + ret = adis_read_reg_16(&st->adis, reg, &val16); + if (ret < 0) + return ret; + + *scale = sign_extend32(val16, 15); + return IIO_VAL_INT; +} + +static const unsigned int adis16480_def_filter_freqs[] = { + 310, + 55, + 275, + 63, +}; + +static const unsigned int ad16480_filter_data[][2] = { + [ADIS16480_SCAN_GYRO_X] = { ADIS16480_REG_FILTER_BNK0, 0 }, + [ADIS16480_SCAN_GYRO_Y] = { ADIS16480_REG_FILTER_BNK0, 3 }, + [ADIS16480_SCAN_GYRO_Z] = { ADIS16480_REG_FILTER_BNK0, 6 }, + [ADIS16480_SCAN_ACCEL_X] = { ADIS16480_REG_FILTER_BNK0, 9 }, + [ADIS16480_SCAN_ACCEL_Y] = { ADIS16480_REG_FILTER_BNK0, 12 }, + [ADIS16480_SCAN_ACCEL_Z] = { ADIS16480_REG_FILTER_BNK1, 0 }, + [ADIS16480_SCAN_MAGN_X] = { ADIS16480_REG_FILTER_BNK1, 3 }, + [ADIS16480_SCAN_MAGN_Y] = { ADIS16480_REG_FILTER_BNK1, 6 }, + [ADIS16480_SCAN_MAGN_Z] = { ADIS16480_REG_FILTER_BNK1, 9 }, +}; + +static int adis16480_get_filter_freq(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *freq) +{ + struct adis16480 *st = iio_priv(indio_dev); + unsigned int enable_mask, offset, reg; + uint16_t val; + int ret; + + reg = ad16480_filter_data[chan->scan_index][0]; + offset = ad16480_filter_data[chan->scan_index][1]; + enable_mask = BIT(offset + 2); + + ret = adis_read_reg_16(&st->adis, reg, &val); + if (ret < 0) + return ret; + + if (!(val & enable_mask)) + *freq = 0; + else + *freq = adis16480_def_filter_freqs[(val >> offset) & 0x3]; + + return IIO_VAL_INT; +} + +static int adis16480_set_filter_freq(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int freq) +{ + struct adis16480 *st = iio_priv(indio_dev); + unsigned int enable_mask, offset, reg; + unsigned int diff, best_diff; + unsigned int i, best_freq; + uint16_t val; + int ret; + + reg = ad16480_filter_data[chan->scan_index][0]; + offset = ad16480_filter_data[chan->scan_index][1]; + enable_mask = BIT(offset + 2); + + ret = adis_read_reg_16(&st->adis, reg, &val); + if (ret < 0) + return ret; + + if (freq == 0) { + val &= ~enable_mask; + } else { + best_freq = 0; + best_diff = 310; + for (i = 0; i < ARRAY_SIZE(adis16480_def_filter_freqs); i++) { + if (adis16480_def_filter_freqs[i] >= freq) { + diff = adis16480_def_filter_freqs[i] - freq; + if (diff < best_diff) { + best_diff = diff; + best_freq = i; + } + } + } + + val &= ~(0x3 << offset); + val |= best_freq << offset; + val |= enable_mask; + } + + return adis_write_reg_16(&st->adis, reg, val); +} + +static int adis16480_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + switch (info) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, 0, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL_VEL: + *val = 0; + *val2 = IIO_DEGREE_TO_RAD(20000); /* 0.02 degree/sec */ + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + *val = 0; + *val2 = IIO_G_TO_M_S_2(800); /* 0.8 mg */ + return IIO_VAL_INT_PLUS_MICRO; + case IIO_MAGN: + *val = 0; + *val2 = 100; /* 0.0001 gauss */ + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + *val = 5; + *val2 = 650000; /* 5.65 milli degree Celsius */ + return IIO_VAL_INT_PLUS_MICRO; + case IIO_PRESSURE: + *val = 0; + *val2 = 4000; /* 40ubar = 0.004 kPa */ + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has a offset */ + *val = 4425; /* 25 degree Celsius = 0x0000 */ + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + return adis16480_get_calibbias(indio_dev, chan, val); + case IIO_CHAN_INFO_CALIBSCALE: + return adis16480_get_calibscale(indio_dev, chan, val); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return adis16480_get_filter_freq(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +static int adis16480_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int val, int val2, long info) +{ + switch (info) { + case IIO_CHAN_INFO_CALIBBIAS: + return adis16480_set_calibbias(indio_dev, chan, val); + case IIO_CHAN_INFO_CALIBSCALE: + return adis16480_set_calibscale(indio_dev, chan, val); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return adis16480_set_filter_freq(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +#define ADIS16480_MOD_CHANNEL(_type, _mod, _address, _si, _info, _bits) \ + { \ + .type = (_type), \ + .modified = 1, \ + .channel2 = (_mod), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT | \ + _info, \ + .address = (_address), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 's', \ + .realbits = (_bits), \ + .storagebits = (_bits), \ + .endianness = IIO_BE, \ + }, \ + } + +#define ADIS16480_GYRO_CHANNEL(_mod) \ + ADIS16480_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \ + ADIS16480_REG_ ## _mod ## _GYRO_OUT, ADIS16480_SCAN_GYRO_ ## _mod, \ + IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \ + 32) + +#define ADIS16480_ACCEL_CHANNEL(_mod) \ + ADIS16480_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \ + ADIS16480_REG_ ## _mod ## _ACCEL_OUT, ADIS16480_SCAN_ACCEL_ ## _mod, \ + IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \ + 32) + +#define ADIS16480_MAGN_CHANNEL(_mod) \ + ADIS16480_MOD_CHANNEL(IIO_MAGN, IIO_MOD_ ## _mod, \ + ADIS16480_REG_ ## _mod ## _MAGN_OUT, ADIS16480_SCAN_MAGN_ ## _mod, \ + IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT, \ + 16) + +#define ADIS16480_PRESSURE_CHANNEL() \ + { \ + .type = IIO_PRESSURE, \ + .indexed = 1, \ + .channel = 0, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .address = ADIS16480_REG_BAROM_OUT, \ + .scan_index = ADIS16480_SCAN_BARO, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +#define ADIS16480_TEMP_CHANNEL() { \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .channel = 0, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \ + .address = ADIS16480_REG_TEMP_OUT, \ + .scan_index = ADIS16480_SCAN_TEMP, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec adis16480_channels[] = { + ADIS16480_GYRO_CHANNEL(X), + ADIS16480_GYRO_CHANNEL(Y), + ADIS16480_GYRO_CHANNEL(Z), + ADIS16480_ACCEL_CHANNEL(X), + ADIS16480_ACCEL_CHANNEL(Y), + ADIS16480_ACCEL_CHANNEL(Z), + ADIS16480_MAGN_CHANNEL(X), + ADIS16480_MAGN_CHANNEL(Y), + ADIS16480_MAGN_CHANNEL(Z), + ADIS16480_PRESSURE_CHANNEL(), + ADIS16480_TEMP_CHANNEL(), + IIO_CHAN_SOFT_TIMESTAMP(11) +}; + +static const struct iio_chan_spec adis16485_channels[] = { + ADIS16480_GYRO_CHANNEL(X), + ADIS16480_GYRO_CHANNEL(Y), + ADIS16480_GYRO_CHANNEL(Z), + ADIS16480_ACCEL_CHANNEL(X), + ADIS16480_ACCEL_CHANNEL(Y), + ADIS16480_ACCEL_CHANNEL(Z), + ADIS16480_TEMP_CHANNEL(), + IIO_CHAN_SOFT_TIMESTAMP(7) +}; + +enum adis16480_variant { + ADIS16375, + ADIS16480, + ADIS16485, + ADIS16488, +}; + +static const struct adis16480_chip_info adis16480_chip_info[] = { + [ADIS16375] = { + .channels = adis16485_channels, + .num_channels = ARRAY_SIZE(adis16485_channels), + }, + [ADIS16480] = { + .channels = adis16480_channels, + .num_channels = ARRAY_SIZE(adis16480_channels), + }, + [ADIS16485] = { + .channels = adis16485_channels, + .num_channels = ARRAY_SIZE(adis16485_channels), + }, + [ADIS16488] = { + .channels = adis16480_channels, + .num_channels = ARRAY_SIZE(adis16480_channels), + }, +}; + +static struct attribute *adis16480_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16480_attribute_group = { + .attrs = adis16480_attributes, +}; + +static const struct iio_info adis16480_info = { + .attrs = &adis16480_attribute_group, + .read_raw = &adis16480_read_raw, + .write_raw = &adis16480_write_raw, + .update_scan_mode = adis_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static int adis16480_stop_device(struct iio_dev *indio_dev) +{ + struct adis16480 *st = iio_priv(indio_dev); + int ret; + + ret = adis_write_reg_16(&st->adis, ADIS16480_REG_SLP_CNT, BIT(9)); + if (ret) + dev_err(&indio_dev->dev, + "Could not power down device: %d\n", ret); + + return ret; +} + +static int adis16480_enable_irq(struct adis *adis, bool enable) +{ + return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, + enable ? BIT(3) : 0); +} + +static int adis16480_initial_setup(struct iio_dev *indio_dev) +{ + struct adis16480 *st = iio_priv(indio_dev); + uint16_t prod_id; + unsigned int device_id; + int ret; + + adis_reset(&st->adis); + msleep(70); + + ret = adis_write_reg_16(&st->adis, ADIS16480_REG_GLOB_CMD, BIT(1)); + if (ret) + return ret; + msleep(30); + + ret = adis_check_status(&st->adis); + if (ret) + return ret; + + ret = adis_read_reg_16(&st->adis, ADIS16480_REG_PROD_ID, &prod_id); + if (ret) + return ret; + + sscanf(indio_dev->name, "adis%u\n", &device_id); + + if (prod_id != device_id) + dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", + device_id, prod_id); + + return 0; +} + +#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0 +#define ADIS16480_DIAG_STAT_YGYRO_FAIL 1 +#define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2 +#define ADIS16480_DIAG_STAT_XACCL_FAIL 3 +#define ADIS16480_DIAG_STAT_YACCL_FAIL 4 +#define ADIS16480_DIAG_STAT_ZACCL_FAIL 5 +#define ADIS16480_DIAG_STAT_XMAGN_FAIL 8 +#define ADIS16480_DIAG_STAT_YMAGN_FAIL 9 +#define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10 +#define ADIS16480_DIAG_STAT_BARO_FAIL 11 + +static const char * const adis16480_status_error_msgs[] = { + [ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure", + [ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure", + [ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure", + [ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure", + [ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure", + [ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure", + [ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure", + [ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure", + [ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure", + [ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure", +}; + +static const struct adis_data adis16480_data = { + .diag_stat_reg = ADIS16480_REG_DIAG_STS, + .glob_cmd_reg = ADIS16480_REG_GLOB_CMD, + .has_paging = true, + + .read_delay = 5, + .write_delay = 5, + + .status_error_msgs = adis16480_status_error_msgs, + .status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) | + BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) | + BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) | + BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) | + BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) | + BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) | + BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) | + BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) | + BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) | + BIT(ADIS16480_DIAG_STAT_BARO_FAIL), + + .enable_irq = adis16480_enable_irq, +}; + +static int adis16480_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct adis16480 *st; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, indio_dev); + + st = iio_priv(indio_dev); + + st->chip_info = &adis16480_chip_info[id->driver_data]; + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = &adis16480_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis_init(&st->adis, indio_dev, spi, &adis16480_data); + if (ret) + goto error_free_dev; + + ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL); + if (ret) + goto error_free_dev; + + ret = adis16480_initial_setup(indio_dev); + if (ret) + goto error_cleanup_buffer; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_stop_device; + + adis16480_debugfs_init(indio_dev); + + return 0; + +error_stop_device: + adis16480_stop_device(indio_dev); +error_cleanup_buffer: + adis_cleanup_buffer_and_trigger(&st->adis, indio_dev); +error_free_dev: + iio_device_free(indio_dev); + return ret; +} + +static int adis16480_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adis16480 *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + adis16480_stop_device(indio_dev); + + adis_cleanup_buffer_and_trigger(&st->adis, indio_dev); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id adis16480_ids[] = { + { "adis16375", ADIS16375 }, + { "adis16480", ADIS16480 }, + { "adis16485", ADIS16485 }, + { "adis16488", ADIS16488 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adis16480_ids); + +static struct spi_driver adis16480_driver = { + .driver = { + .name = "adis16480", + .owner = THIS_MODULE, + }, + .id_table = adis16480_ids, + .probe = adis16480_probe, + .remove = adis16480_remove, +}; +module_spi_driver(adis16480_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices ADIS16480 IMU driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index e82cd0827e91..ff781dca2e9a 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -20,6 +20,8 @@ #define ADIS_PAGE_SIZE 0x80 #define ADIS_REG_PAGE_ID 0x00 +struct adis; + /** * struct adis_data - ADIS chip variant specific data * @read_delay: SPI delay for read operations in us @@ -44,6 +46,8 @@ struct adis_data { const char * const *status_error_msgs; unsigned int status_error_mask; + int (*enable_irq)(struct adis *adis, bool enable); + bool has_paging; }; -- cgit v1.2.3 From be7fd3b86ad2f2a8db58decc15d2274b0c89c23b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Wed, 21 Nov 2012 18:24:26 +0000 Subject: iio:adc:ad7298 make the tx and rx buffers __be16 These buffers are a little interesting in that their content may have variable endianness, but all but one element will definitely be big endian. Reported-by: kbuild test robot Signed-off-by: Jonathan Cameron Acked-by: Lars-Peter Clausen --- drivers/iio/adc/ad7298.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 441a9a265c12..2364807a5d6c 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -54,8 +54,8 @@ struct ad7298_state { * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. */ - unsigned short rx_buf[12] ____cacheline_aligned; - unsigned short tx_buf[2]; + __be16 rx_buf[12] ____cacheline_aligned; + __be16 tx_buf[2]; }; #define AD7298_V_CHAN(index) \ -- cgit v1.2.3 From f87f1a2375a51ef8c5048bfce42587dbea1ca627 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 21 Nov 2012 16:27:00 +0000 Subject: staging:iio: Move ad7793 driver out of staging The driver does not expose any custom API to userspace and none of the standard static code checker tools report any issues, so move it out of staging. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7793.c | 691 ++++++++++++++++++++++++++++++++++ drivers/staging/iio/adc/Kconfig | 12 - drivers/staging/iio/adc/Makefile | 1 - drivers/staging/iio/adc/ad7793.c | 692 ----------------------------------- drivers/staging/iio/adc/ad7793.h | 116 ------ include/linux/platform_data/ad7793.h | 112 ++++++ 8 files changed, 816 insertions(+), 821 deletions(-) create mode 100644 drivers/iio/adc/ad7793.c delete mode 100644 drivers/staging/iio/adc/ad7793.c delete mode 100644 drivers/staging/iio/adc/ad7793.h create mode 100644 include/linux/platform_data/ad7793.h (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index cd5eed60be28..408557b02441 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -42,6 +42,18 @@ config AD7791 To compile this driver as a module, choose M here: the module will be called ad7791. +config AD7793 + tristate "Analog Devices AD7793 and similar ADCs driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7785, AD7792, AD7793, + AD7794 and AD7795 SPI analog to digital converters (ADC). + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called AD7793. + config AD7476 tristate "Analog Devices AD7476 and similar 1-channel ADCs driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 3256dc64a466..78202d9eb961 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AD7298) += ad7298.o obj-$(CONFIG_AD7476) += ad7476.o obj-$(CONFIG_AD7791) += ad7791.o +obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c new file mode 100644 index 000000000000..a109e686b9f7 --- /dev/null +++ b/drivers/iio/adc/ad7793.c @@ -0,0 +1,691 @@ +/* + * AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver + * + * Copyright 2011-2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define AD7793_REG_COMM 0 /* Communications Register (WO, 8-bit) */ +#define AD7793_REG_STAT 0 /* Status Register (RO, 8-bit) */ +#define AD7793_REG_MODE 1 /* Mode Register (RW, 16-bit */ +#define AD7793_REG_CONF 2 /* Configuration Register (RW, 16-bit) */ +#define AD7793_REG_DATA 3 /* Data Register (RO, 16-/24-bit) */ +#define AD7793_REG_ID 4 /* ID Register (RO, 8-bit) */ +#define AD7793_REG_IO 5 /* IO Register (RO, 8-bit) */ +#define AD7793_REG_OFFSET 6 /* Offset Register (RW, 16-bit + * (AD7792)/24-bit (AD7793)) */ +#define AD7793_REG_FULLSALE 7 /* Full-Scale Register + * (RW, 16-bit (AD7792)/24-bit (AD7793)) */ + +/* Communications Register Bit Designations (AD7793_REG_COMM) */ +#define AD7793_COMM_WEN (1 << 7) /* Write Enable */ +#define AD7793_COMM_WRITE (0 << 6) /* Write Operation */ +#define AD7793_COMM_READ (1 << 6) /* Read Operation */ +#define AD7793_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */ +#define AD7793_COMM_CREAD (1 << 2) /* Continuous Read of Data Register */ + +/* Status Register Bit Designations (AD7793_REG_STAT) */ +#define AD7793_STAT_RDY (1 << 7) /* Ready */ +#define AD7793_STAT_ERR (1 << 6) /* Error (Overrange, Underrange) */ +#define AD7793_STAT_CH3 (1 << 2) /* Channel 3 */ +#define AD7793_STAT_CH2 (1 << 1) /* Channel 2 */ +#define AD7793_STAT_CH1 (1 << 0) /* Channel 1 */ + +/* Mode Register Bit Designations (AD7793_REG_MODE) */ +#define AD7793_MODE_SEL(x) (((x) & 0x7) << 13) /* Operation Mode Select */ +#define AD7793_MODE_SEL_MASK (0x7 << 13) /* Operation Mode Select mask */ +#define AD7793_MODE_CLKSRC(x) (((x) & 0x3) << 6) /* ADC Clock Source Select */ +#define AD7793_MODE_RATE(x) ((x) & 0xF) /* Filter Update Rate Select */ + +#define AD7793_MODE_CONT 0 /* Continuous Conversion Mode */ +#define AD7793_MODE_SINGLE 1 /* Single Conversion Mode */ +#define AD7793_MODE_IDLE 2 /* Idle Mode */ +#define AD7793_MODE_PWRDN 3 /* Power-Down Mode */ +#define AD7793_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */ +#define AD7793_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */ +#define AD7793_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */ +#define AD7793_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */ + +#define AD7793_CLK_INT 0 /* Internal 64 kHz Clock not + * available at the CLK pin */ +#define AD7793_CLK_INT_CO 1 /* Internal 64 kHz Clock available + * at the CLK pin */ +#define AD7793_CLK_EXT 2 /* External 64 kHz Clock */ +#define AD7793_CLK_EXT_DIV2 3 /* External Clock divided by 2 */ + +/* Configuration Register Bit Designations (AD7793_REG_CONF) */ +#define AD7793_CONF_VBIAS(x) (((x) & 0x3) << 14) /* Bias Voltage + * Generator Enable */ +#define AD7793_CONF_BO_EN (1 << 13) /* Burnout Current Enable */ +#define AD7793_CONF_UNIPOLAR (1 << 12) /* Unipolar/Bipolar Enable */ +#define AD7793_CONF_BOOST (1 << 11) /* Boost Enable */ +#define AD7793_CONF_GAIN(x) (((x) & 0x7) << 8) /* Gain Select */ +#define AD7793_CONF_REFSEL(x) ((x) << 6) /* INT/EXT Reference Select */ +#define AD7793_CONF_BUF (1 << 4) /* Buffered Mode Enable */ +#define AD7793_CONF_CHAN(x) ((x) & 0xf) /* Channel select */ +#define AD7793_CONF_CHAN_MASK 0xf /* Channel select mask */ + +#define AD7793_CH_AIN1P_AIN1M 0 /* AIN1(+) - AIN1(-) */ +#define AD7793_CH_AIN2P_AIN2M 1 /* AIN2(+) - AIN2(-) */ +#define AD7793_CH_AIN3P_AIN3M 2 /* AIN3(+) - AIN3(-) */ +#define AD7793_CH_AIN1M_AIN1M 3 /* AIN1(-) - AIN1(-) */ +#define AD7793_CH_TEMP 6 /* Temp Sensor */ +#define AD7793_CH_AVDD_MONITOR 7 /* AVDD Monitor */ + +#define AD7795_CH_AIN4P_AIN4M 4 /* AIN4(+) - AIN4(-) */ +#define AD7795_CH_AIN5P_AIN5M 5 /* AIN5(+) - AIN5(-) */ +#define AD7795_CH_AIN6P_AIN6M 6 /* AIN6(+) - AIN6(-) */ +#define AD7795_CH_AIN1M_AIN1M 8 /* AIN1(-) - AIN1(-) */ + +/* ID Register Bit Designations (AD7793_REG_ID) */ +#define AD7785_ID 0xB +#define AD7792_ID 0xA +#define AD7793_ID 0xB +#define AD7794_ID 0xF +#define AD7795_ID 0xF +#define AD7793_ID_MASK 0xF + +/* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */ +#define AD7793_IO_IEXC1_IOUT1_IEXC2_IOUT2 0 /* IEXC1 connect to IOUT1, + * IEXC2 connect to IOUT2 */ +#define AD7793_IO_IEXC1_IOUT2_IEXC2_IOUT1 1 /* IEXC1 connect to IOUT2, + * IEXC2 connect to IOUT1 */ +#define AD7793_IO_IEXC1_IEXC2_IOUT1 2 /* Both current sources + * IEXC1,2 connect to IOUT1 */ +#define AD7793_IO_IEXC1_IEXC2_IOUT2 3 /* Both current sources + * IEXC1,2 connect to IOUT2 */ + +#define AD7793_IO_IXCEN_10uA (1 << 0) /* Excitation Current 10uA */ +#define AD7793_IO_IXCEN_210uA (2 << 0) /* Excitation Current 210uA */ +#define AD7793_IO_IXCEN_1mA (3 << 0) /* Excitation Current 1mA */ + +/* NOTE: + * The AD7792/AD7793 features a dual use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use spi bus locking. + * + * The DOUT/RDY output must also be wired to an interrupt capable GPIO. + */ + +struct ad7793_chip_info { + unsigned int id; + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ad7793_state { + const struct ad7793_chip_info *chip_info; + struct regulator *reg; + u16 int_vref_mv; + u16 mode; + u16 conf; + u32 scale_avail[8][2]; + + struct ad_sigma_delta sd; + +}; + +enum ad7793_supported_device_ids { + ID_AD7785, + ID_AD7792, + ID_AD7793, + ID_AD7794, + ID_AD7795, +}; + +static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7793_state, sd); +} + +static int ad7793_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); + + st->conf &= ~AD7793_CONF_CHAN_MASK; + st->conf |= AD7793_CONF_CHAN(channel); + + return ad_sd_write_reg(&st->sd, AD7793_REG_CONF, 2, st->conf); +} + +static int ad7793_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); + + st->mode &= ~AD7793_MODE_SEL_MASK; + st->mode |= AD7793_MODE_SEL(mode); + + return ad_sd_write_reg(&st->sd, AD7793_REG_MODE, 2, st->mode); +} + +static const struct ad_sigma_delta_info ad7793_sigma_delta_info = { + .set_channel = ad7793_set_channel, + .set_mode = ad7793_set_mode, + .has_registers = true, + .addr_shift = 3, + .read_mask = BIT(6), +}; + +static const struct ad_sd_calib_data ad7793_calib_arr[6] = { + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN1P_AIN1M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN1P_AIN1M}, + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN2P_AIN2M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN2P_AIN2M}, + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN3P_AIN3M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN3P_AIN3M} +}; + +static int ad7793_calibrate_all(struct ad7793_state *st) +{ + return ad_sd_calibrate_all(&st->sd, ad7793_calib_arr, + ARRAY_SIZE(ad7793_calib_arr)); +} + +static int ad7793_setup(struct iio_dev *indio_dev, + const struct ad7793_platform_data *pdata, + unsigned int vref_mv) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int i, ret = -1; + unsigned long long scale_uv; + u32 id; + + if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 || + pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) && + ((pdata->exitation_current != AD7793_IX_10uA) && + (pdata->exitation_current != AD7793_IX_210uA))) + return -EINVAL; + + /* reset the serial interface */ + ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret)); + if (ret < 0) + goto out; + usleep_range(500, 2000); /* Wait for at least 500us */ + + /* write/read test for device presence */ + ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id); + if (ret) + goto out; + + id &= AD7793_ID_MASK; + + if (id != st->chip_info->id) { + dev_err(&st->sd.spi->dev, "device ID query failed\n"); + goto out; + } + + st->mode = AD7793_MODE_RATE(1); + st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src); + st->conf = AD7793_CONF_REFSEL(pdata->refsel); + st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage); + if (pdata->buffered) + st->conf |= AD7793_CONF_BUF; + if (pdata->boost_enable) + st->conf |= AD7793_CONF_BOOST; + if (pdata->burnout_current) + st->conf |= AD7793_CONF_BO_EN; + if (pdata->unipolar) + st->conf |= AD7793_CONF_UNIPOLAR; + + ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE); + if (ret) + goto out; + + ret = ad7793_set_channel(&st->sd, 0); + if (ret) + goto out; + + ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1, + pdata->exitation_current | + (pdata->current_source_direction << 2)); + if (ret) + goto out; + + ret = ad7793_calibrate_all(st); + if (ret) + goto out; + + /* Populate available ADC input ranges */ + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + scale_uv = ((u64)vref_mv * 100000000) + >> (st->chip_info->channels[0].scan_type.realbits - + (!!(st->conf & AD7793_CONF_UNIPOLAR) ? 0 : 1)); + scale_uv >>= i; + + st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10; + st->scale_avail[i][0] = scale_uv; + } + + return 0; +out: + dev_err(&st->sd.spi->dev, "setup failed\n"); + return ret; +} + +static const u16 sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, 33, 19, + 17, 16, 12, 10, 8, 6, 4}; + +static ssize_t ad7793_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7793_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + sample_freq_avail[AD7793_MODE_RATE(st->mode)]); +} + +static ssize_t ad7793_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7793_state *st = iio_priv(indio_dev); + long lval; + int i, ret; + + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + mutex_unlock(&indio_dev->mlock); + + ret = kstrtol(buf, 10, &lval); + if (ret) + return ret; + + ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sample_freq_avail); i++) + if (lval == sample_freq_avail[i]) { + mutex_lock(&indio_dev->mlock); + st->mode &= ~AD7793_MODE_RATE(-1); + st->mode |= AD7793_MODE_RATE(i); + ad_sd_write_reg(&st->sd, AD7793_REG_MODE, + sizeof(st->mode), st->mode); + mutex_unlock(&indio_dev->mlock); + ret = 0; + } + + return ret ? ret : len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + ad7793_read_frequency, + ad7793_write_frequency); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "470 242 123 62 50 39 33 19 17 16 12 10 8 6 4"); + +static ssize_t ad7793_show_scale_available(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7793_state *st = iio_priv(indio_dev); + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + len += sprintf(buf + len, "%d.%09u ", st->scale_avail[i][0], + st->scale_avail[i][1]); + + len += sprintf(buf + len, "\n"); + + return len; +} + +static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available, + in_voltage-voltage_scale_available, S_IRUGO, + ad7793_show_scale_available, NULL, 0); + +static struct attribute *ad7793_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_m_in_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7793_attribute_group = { + .attrs = ad7793_attributes, +}; + +static int ad7793_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int ret; + unsigned long long scale_uv; + bool unipolar = !!(st->conf & AD7793_CONF_UNIPOLAR); + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad_sigma_delta_single_conversion(indio_dev, chan, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->differential) { + *val = st-> + scale_avail[(st->conf >> 8) & 0x7][0]; + *val2 = st-> + scale_avail[(st->conf >> 8) & 0x7][1]; + return IIO_VAL_INT_PLUS_NANO; + } else { + /* 1170mV / 2^23 * 6 */ + scale_uv = (1170ULL * 1000000000ULL * 6ULL); + } + break; + case IIO_TEMP: + /* 1170mV / 0.81 mV/C / 2^23 */ + scale_uv = 1444444444444444ULL; + break; + default: + return -EINVAL; + } + + scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1)); + *val = 0; + *val2 = scale_uv; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + if (!unipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + + /* Kelvin to Celsius */ + if (chan->type == IIO_TEMP) { + unsigned long long offset; + unsigned int shift; + + shift = chan->scan_type.realbits - (unipolar ? 0 : 1); + offset = 273ULL << shift; + do_div(offset, 1444); + *val -= offset; + } + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int ad7793_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int ret, i; + unsigned int tmp; + + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + if (val2 == st->scale_avail[i][1]) { + ret = 0; + tmp = st->conf; + st->conf &= ~AD7793_CONF_GAIN(-1); + st->conf |= AD7793_CONF_GAIN(i); + + if (tmp == st->conf) + break; + + ad_sd_write_reg(&st->sd, AD7793_REG_CONF, + sizeof(st->conf), st->conf); + ad7793_calibrate_all(st); + break; + } + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static int ad7793_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static const struct iio_info ad7793_info = { + .read_raw = &ad7793_read_raw, + .write_raw = &ad7793_write_raw, + .write_raw_get_fmt = &ad7793_write_raw_get_fmt, + .attrs = &ad7793_attribute_group, + .validate_trigger = ad_sd_validate_trigger, + .driver_module = THIS_MODULE, +}; + +#define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \ + AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \ + AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \ + AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \ + AD_SD_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \ + AD_SD_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \ + IIO_CHAN_SOFT_TIMESTAMP(6), \ +} + +#define DECLARE_AD7795_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \ + AD_SD_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD_SD_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD_SD_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(9), \ +} + +static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4); +static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0); +static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0); +static DECLARE_AD7795_CHANNELS(ad7794, 16, 32); +static DECLARE_AD7795_CHANNELS(ad7795, 24, 32); + +static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { + [ID_AD7785] = { + .id = AD7785_ID, + .channels = ad7785_channels, + .num_channels = ARRAY_SIZE(ad7785_channels), + }, + [ID_AD7792] = { + .id = AD7792_ID, + .channels = ad7792_channels, + .num_channels = ARRAY_SIZE(ad7792_channels), + }, + [ID_AD7793] = { + .id = AD7793_ID, + .channels = ad7793_channels, + .num_channels = ARRAY_SIZE(ad7793_channels), + }, + [ID_AD7794] = { + .id = AD7794_ID, + .channels = ad7794_channels, + .num_channels = ARRAY_SIZE(ad7794_channels), + }, + [ID_AD7795] = { + .id = AD7795_ID, + .channels = ad7795_channels, + .num_channels = ARRAY_SIZE(ad7795_channels), + }, +}; + +static int ad7793_probe(struct spi_device *spi) +{ + const struct ad7793_platform_data *pdata = spi->dev.platform_data; + struct ad7793_state *st; + struct iio_dev *indio_dev; + int ret, vref_mv = 0; + + if (!pdata) { + dev_err(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + if (!spi->irq) { + dev_err(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info); + + if (pdata->refsel != AD7793_REFSEL_INTERNAL) { + st->reg = regulator_get(&spi->dev, "refin"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto error_device_free; + } + + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + vref_mv = regulator_get_voltage(st->reg); + if (vref_mv < 0) { + ret = vref_mv; + goto error_disable_reg; + } + + vref_mv /= 1000; + } else { + vref_mv = 1170; /* Build-in ref */ + } + + st->chip_info = + &ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi_set_drvdata(spi, indio_dev); + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = &ad7793_info; + + ret = ad_sd_setup_buffer_and_trigger(indio_dev); + if (ret) + goto error_disable_reg; + + ret = ad7793_setup(indio_dev, pdata, vref_mv); + if (ret) + goto error_remove_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_remove_trigger; + + return 0; + +error_remove_trigger: + ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_disable_reg: + if (pdata->refsel != AD7793_REFSEL_INTERNAL) + regulator_disable(st->reg); +error_put_reg: + if (pdata->refsel != AD7793_REFSEL_INTERNAL) + regulator_put(st->reg); +error_device_free: + iio_device_free(indio_dev); + + return ret; +} + +static int ad7793_remove(struct spi_device *spi) +{ + const struct ad7793_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7793_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + ad_sd_cleanup_buffer_and_trigger(indio_dev); + + if (pdata->refsel != AD7793_REFSEL_INTERNAL) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7793_id[] = { + {"ad7785", ID_AD7785}, + {"ad7792", ID_AD7792}, + {"ad7793", ID_AD7793}, + {"ad7794", ID_AD7794}, + {"ad7795", ID_AD7795}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7793_id); + +static struct spi_driver ad7793_driver = { + .driver = { + .name = "ad7793", + .owner = THIS_MODULE, + }, + .probe = ad7793_probe, + .remove = ad7793_remove, + .id_table = ad7793_id, +}; +module_spi_driver(ad7793_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index dc8582b95b61..fb8c239b0c88 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -70,18 +70,6 @@ config AD7780 To compile this driver as a module, choose M here: the module will be called ad7780. -config AD7793 - tristate "Analog Devices AD7793 and similar ADCs driver" - depends on SPI - select AD_SIGMA_DELTA - help - Say yes here to build support for Analog Devices AD7785, AD7792, AD7793, - AD7794 and AD7795 SPI analog to digital converters (ADC). - If unsure, say N (but it's safe to say "Y"). - - To compile this driver as a module, choose M here: the - module will be called AD7793. - config AD7816 tristate "Analog Devices AD7816/7/8 temperature sensor and ADC driver" depends on SPI diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 7281451a613a..d285596272a0 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -14,7 +14,6 @@ obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7780) += ad7780.o -obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7816) += ad7816.o obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_ADT7410) += adt7410.o diff --git a/drivers/staging/iio/adc/ad7793.c b/drivers/staging/iio/adc/ad7793.c deleted file mode 100644 index 8928609a1828..000000000000 --- a/drivers/staging/iio/adc/ad7793.c +++ /dev/null @@ -1,692 +0,0 @@ -/* - * AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver - * - * Copyright 2011-2012 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "ad7793.h" - -/* Registers */ -#define AD7793_REG_COMM 0 /* Communications Register (WO, 8-bit) */ -#define AD7793_REG_STAT 0 /* Status Register (RO, 8-bit) */ -#define AD7793_REG_MODE 1 /* Mode Register (RW, 16-bit */ -#define AD7793_REG_CONF 2 /* Configuration Register (RW, 16-bit) */ -#define AD7793_REG_DATA 3 /* Data Register (RO, 16-/24-bit) */ -#define AD7793_REG_ID 4 /* ID Register (RO, 8-bit) */ -#define AD7793_REG_IO 5 /* IO Register (RO, 8-bit) */ -#define AD7793_REG_OFFSET 6 /* Offset Register (RW, 16-bit - * (AD7792)/24-bit (AD7793)) */ -#define AD7793_REG_FULLSALE 7 /* Full-Scale Register - * (RW, 16-bit (AD7792)/24-bit (AD7793)) */ - -/* Communications Register Bit Designations (AD7793_REG_COMM) */ -#define AD7793_COMM_WEN (1 << 7) /* Write Enable */ -#define AD7793_COMM_WRITE (0 << 6) /* Write Operation */ -#define AD7793_COMM_READ (1 << 6) /* Read Operation */ -#define AD7793_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */ -#define AD7793_COMM_CREAD (1 << 2) /* Continuous Read of Data Register */ - -/* Status Register Bit Designations (AD7793_REG_STAT) */ -#define AD7793_STAT_RDY (1 << 7) /* Ready */ -#define AD7793_STAT_ERR (1 << 6) /* Error (Overrange, Underrange) */ -#define AD7793_STAT_CH3 (1 << 2) /* Channel 3 */ -#define AD7793_STAT_CH2 (1 << 1) /* Channel 2 */ -#define AD7793_STAT_CH1 (1 << 0) /* Channel 1 */ - -/* Mode Register Bit Designations (AD7793_REG_MODE) */ -#define AD7793_MODE_SEL(x) (((x) & 0x7) << 13) /* Operation Mode Select */ -#define AD7793_MODE_SEL_MASK (0x7 << 13) /* Operation Mode Select mask */ -#define AD7793_MODE_CLKSRC(x) (((x) & 0x3) << 6) /* ADC Clock Source Select */ -#define AD7793_MODE_RATE(x) ((x) & 0xF) /* Filter Update Rate Select */ - -#define AD7793_MODE_CONT 0 /* Continuous Conversion Mode */ -#define AD7793_MODE_SINGLE 1 /* Single Conversion Mode */ -#define AD7793_MODE_IDLE 2 /* Idle Mode */ -#define AD7793_MODE_PWRDN 3 /* Power-Down Mode */ -#define AD7793_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */ -#define AD7793_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */ -#define AD7793_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */ -#define AD7793_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */ - -#define AD7793_CLK_INT 0 /* Internal 64 kHz Clock not - * available at the CLK pin */ -#define AD7793_CLK_INT_CO 1 /* Internal 64 kHz Clock available - * at the CLK pin */ -#define AD7793_CLK_EXT 2 /* External 64 kHz Clock */ -#define AD7793_CLK_EXT_DIV2 3 /* External Clock divided by 2 */ - -/* Configuration Register Bit Designations (AD7793_REG_CONF) */ -#define AD7793_CONF_VBIAS(x) (((x) & 0x3) << 14) /* Bias Voltage - * Generator Enable */ -#define AD7793_CONF_BO_EN (1 << 13) /* Burnout Current Enable */ -#define AD7793_CONF_UNIPOLAR (1 << 12) /* Unipolar/Bipolar Enable */ -#define AD7793_CONF_BOOST (1 << 11) /* Boost Enable */ -#define AD7793_CONF_GAIN(x) (((x) & 0x7) << 8) /* Gain Select */ -#define AD7793_CONF_REFSEL(x) ((x) << 6) /* INT/EXT Reference Select */ -#define AD7793_CONF_BUF (1 << 4) /* Buffered Mode Enable */ -#define AD7793_CONF_CHAN(x) ((x) & 0xf) /* Channel select */ -#define AD7793_CONF_CHAN_MASK 0xf /* Channel select mask */ - -#define AD7793_CH_AIN1P_AIN1M 0 /* AIN1(+) - AIN1(-) */ -#define AD7793_CH_AIN2P_AIN2M 1 /* AIN2(+) - AIN2(-) */ -#define AD7793_CH_AIN3P_AIN3M 2 /* AIN3(+) - AIN3(-) */ -#define AD7793_CH_AIN1M_AIN1M 3 /* AIN1(-) - AIN1(-) */ -#define AD7793_CH_TEMP 6 /* Temp Sensor */ -#define AD7793_CH_AVDD_MONITOR 7 /* AVDD Monitor */ - -#define AD7795_CH_AIN4P_AIN4M 4 /* AIN4(+) - AIN4(-) */ -#define AD7795_CH_AIN5P_AIN5M 5 /* AIN5(+) - AIN5(-) */ -#define AD7795_CH_AIN6P_AIN6M 6 /* AIN6(+) - AIN6(-) */ -#define AD7795_CH_AIN1M_AIN1M 8 /* AIN1(-) - AIN1(-) */ - -/* ID Register Bit Designations (AD7793_REG_ID) */ -#define AD7785_ID 0xB -#define AD7792_ID 0xA -#define AD7793_ID 0xB -#define AD7794_ID 0xF -#define AD7795_ID 0xF -#define AD7793_ID_MASK 0xF - -/* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */ -#define AD7793_IO_IEXC1_IOUT1_IEXC2_IOUT2 0 /* IEXC1 connect to IOUT1, - * IEXC2 connect to IOUT2 */ -#define AD7793_IO_IEXC1_IOUT2_IEXC2_IOUT1 1 /* IEXC1 connect to IOUT2, - * IEXC2 connect to IOUT1 */ -#define AD7793_IO_IEXC1_IEXC2_IOUT1 2 /* Both current sources - * IEXC1,2 connect to IOUT1 */ -#define AD7793_IO_IEXC1_IEXC2_IOUT2 3 /* Both current sources - * IEXC1,2 connect to IOUT2 */ - -#define AD7793_IO_IXCEN_10uA (1 << 0) /* Excitation Current 10uA */ -#define AD7793_IO_IXCEN_210uA (2 << 0) /* Excitation Current 210uA */ -#define AD7793_IO_IXCEN_1mA (3 << 0) /* Excitation Current 1mA */ - -/* NOTE: - * The AD7792/AD7793 features a dual use data out ready DOUT/RDY output. - * In order to avoid contentions on the SPI bus, it's therefore necessary - * to use spi bus locking. - * - * The DOUT/RDY output must also be wired to an interrupt capable GPIO. - */ - -struct ad7793_chip_info { - unsigned int id; - const struct iio_chan_spec *channels; - unsigned int num_channels; -}; - -struct ad7793_state { - const struct ad7793_chip_info *chip_info; - struct regulator *reg; - u16 int_vref_mv; - u16 mode; - u16 conf; - u32 scale_avail[8][2]; - - struct ad_sigma_delta sd; - -}; - -enum ad7793_supported_device_ids { - ID_AD7785, - ID_AD7792, - ID_AD7793, - ID_AD7794, - ID_AD7795, -}; - -static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd) -{ - return container_of(sd, struct ad7793_state, sd); -} - -static int ad7793_set_channel(struct ad_sigma_delta *sd, unsigned int channel) -{ - struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); - - st->conf &= ~AD7793_CONF_CHAN_MASK; - st->conf |= AD7793_CONF_CHAN(channel); - - return ad_sd_write_reg(&st->sd, AD7793_REG_CONF, 2, st->conf); -} - -static int ad7793_set_mode(struct ad_sigma_delta *sd, - enum ad_sigma_delta_mode mode) -{ - struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); - - st->mode &= ~AD7793_MODE_SEL_MASK; - st->mode |= AD7793_MODE_SEL(mode); - - return ad_sd_write_reg(&st->sd, AD7793_REG_MODE, 2, st->mode); -} - -static const struct ad_sigma_delta_info ad7793_sigma_delta_info = { - .set_channel = ad7793_set_channel, - .set_mode = ad7793_set_mode, - .has_registers = true, - .addr_shift = 3, - .read_mask = BIT(6), -}; - -static const struct ad_sd_calib_data ad7793_calib_arr[6] = { - {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN1P_AIN1M}, - {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN1P_AIN1M}, - {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN2P_AIN2M}, - {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN2P_AIN2M}, - {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN3P_AIN3M}, - {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN3P_AIN3M} -}; - -static int ad7793_calibrate_all(struct ad7793_state *st) -{ - return ad_sd_calibrate_all(&st->sd, ad7793_calib_arr, - ARRAY_SIZE(ad7793_calib_arr)); -} - -static int ad7793_setup(struct iio_dev *indio_dev, - const struct ad7793_platform_data *pdata, - unsigned int vref_mv) -{ - struct ad7793_state *st = iio_priv(indio_dev); - int i, ret = -1; - unsigned long long scale_uv; - u32 id; - - if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 || - pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) && - ((pdata->exitation_current != AD7793_IX_10uA) && - (pdata->exitation_current != AD7793_IX_210uA))) - return -EINVAL; - - /* reset the serial interface */ - ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret)); - if (ret < 0) - goto out; - usleep_range(500, 2000); /* Wait for at least 500us */ - - /* write/read test for device presence */ - ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id); - if (ret) - goto out; - - id &= AD7793_ID_MASK; - - if (id != st->chip_info->id) { - dev_err(&st->sd.spi->dev, "device ID query failed\n"); - goto out; - } - - st->mode = AD7793_MODE_RATE(1); - st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src); - st->conf = AD7793_CONF_REFSEL(pdata->refsel); - st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage); - if (pdata->buffered) - st->conf |= AD7793_CONF_BUF; - if (pdata->boost_enable) - st->conf |= AD7793_CONF_BOOST; - if (pdata->burnout_current) - st->conf |= AD7793_CONF_BO_EN; - if (pdata->unipolar) - st->conf |= AD7793_CONF_UNIPOLAR; - - ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE); - if (ret) - goto out; - - ret = ad7793_set_channel(&st->sd, 0); - if (ret) - goto out; - - ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1, - pdata->exitation_current | - (pdata->current_source_direction << 2)); - if (ret) - goto out; - - ret = ad7793_calibrate_all(st); - if (ret) - goto out; - - /* Populate available ADC input ranges */ - for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { - scale_uv = ((u64)vref_mv * 100000000) - >> (st->chip_info->channels[0].scan_type.realbits - - (!!(st->conf & AD7793_CONF_UNIPOLAR) ? 0 : 1)); - scale_uv >>= i; - - st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10; - st->scale_avail[i][0] = scale_uv; - } - - return 0; -out: - dev_err(&st->sd.spi->dev, "setup failed\n"); - return ret; -} - -static const u16 sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, 33, 19, - 17, 16, 12, 10, 8, 6, 4}; - -static ssize_t ad7793_read_frequency(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7793_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", - sample_freq_avail[AD7793_MODE_RATE(st->mode)]); -} - -static ssize_t ad7793_write_frequency(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7793_state *st = iio_priv(indio_dev); - long lval; - int i, ret; - - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&indio_dev->mlock); - return -EBUSY; - } - mutex_unlock(&indio_dev->mlock); - - ret = kstrtol(buf, 10, &lval); - if (ret) - return ret; - - ret = -EINVAL; - - for (i = 0; i < ARRAY_SIZE(sample_freq_avail); i++) - if (lval == sample_freq_avail[i]) { - mutex_lock(&indio_dev->mlock); - st->mode &= ~AD7793_MODE_RATE(-1); - st->mode |= AD7793_MODE_RATE(i); - ad_sd_write_reg(&st->sd, AD7793_REG_MODE, - sizeof(st->mode), st->mode); - mutex_unlock(&indio_dev->mlock); - ret = 0; - } - - return ret ? ret : len; -} - -static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, - ad7793_read_frequency, - ad7793_write_frequency); - -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( - "470 242 123 62 50 39 33 19 17 16 12 10 8 6 4"); - -static ssize_t ad7793_show_scale_available(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7793_state *st = iio_priv(indio_dev); - int i, len = 0; - - for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) - len += sprintf(buf + len, "%d.%09u ", st->scale_avail[i][0], - st->scale_avail[i][1]); - - len += sprintf(buf + len, "\n"); - - return len; -} - -static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available, - in_voltage-voltage_scale_available, S_IRUGO, - ad7793_show_scale_available, NULL, 0); - -static struct attribute *ad7793_attributes[] = { - &iio_dev_attr_sampling_frequency.dev_attr.attr, - &iio_const_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_in_m_in_scale_available.dev_attr.attr, - NULL -}; - -static const struct attribute_group ad7793_attribute_group = { - .attrs = ad7793_attributes, -}; - -static int ad7793_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad7793_state *st = iio_priv(indio_dev); - int ret; - unsigned long long scale_uv; - bool unipolar = !!(st->conf & AD7793_CONF_UNIPOLAR); - - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = ad_sigma_delta_single_conversion(indio_dev, chan, val); - if (ret < 0) - return ret; - - return IIO_VAL_INT; - - case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_VOLTAGE: - if (chan->differential) { - *val = st-> - scale_avail[(st->conf >> 8) & 0x7][0]; - *val2 = st-> - scale_avail[(st->conf >> 8) & 0x7][1]; - return IIO_VAL_INT_PLUS_NANO; - } else { - /* 1170mV / 2^23 * 6 */ - scale_uv = (1170ULL * 1000000000ULL * 6ULL); - } - break; - case IIO_TEMP: - /* 1170mV / 0.81 mV/C / 2^23 */ - scale_uv = 1444444444444444ULL; - break; - default: - return -EINVAL; - } - - scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1)); - *val = 0; - *val2 = scale_uv; - return IIO_VAL_INT_PLUS_NANO; - case IIO_CHAN_INFO_OFFSET: - if (!unipolar) - *val = -(1 << (chan->scan_type.realbits - 1)); - else - *val = 0; - - /* Kelvin to Celsius */ - if (chan->type == IIO_TEMP) { - unsigned long long offset; - unsigned int shift; - - shift = chan->scan_type.realbits - (unipolar ? 0 : 1); - offset = 273ULL << shift; - do_div(offset, 1444); - *val -= offset; - } - return IIO_VAL_INT; - } - return -EINVAL; -} - -static int ad7793_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad7793_state *st = iio_priv(indio_dev); - int ret, i; - unsigned int tmp; - - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&indio_dev->mlock); - return -EBUSY; - } - - switch (mask) { - case IIO_CHAN_INFO_SCALE: - ret = -EINVAL; - for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) - if (val2 == st->scale_avail[i][1]) { - ret = 0; - tmp = st->conf; - st->conf &= ~AD7793_CONF_GAIN(-1); - st->conf |= AD7793_CONF_GAIN(i); - - if (tmp == st->conf) - break; - - ad_sd_write_reg(&st->sd, AD7793_REG_CONF, - sizeof(st->conf), st->conf); - ad7793_calibrate_all(st); - break; - } - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&indio_dev->mlock); - return ret; -} - -static int ad7793_write_raw_get_fmt(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - long mask) -{ - return IIO_VAL_INT_PLUS_NANO; -} - -static const struct iio_info ad7793_info = { - .read_raw = &ad7793_read_raw, - .write_raw = &ad7793_write_raw, - .write_raw_get_fmt = &ad7793_write_raw_get_fmt, - .attrs = &ad7793_attribute_group, - .validate_trigger = ad_sd_validate_trigger, - .driver_module = THIS_MODULE, -}; - -#define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \ -const struct iio_chan_spec _name##_channels[] = { \ - AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \ - AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \ - AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \ - AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \ - AD_SD_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \ - AD_SD_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \ - IIO_CHAN_SOFT_TIMESTAMP(6), \ -} - -#define DECLARE_AD7795_CHANNELS(_name, _b, _sb) \ -const struct iio_chan_spec _name##_channels[] = { \ - AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \ - AD_SD_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ - AD_SD_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \ - AD_SD_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ - IIO_CHAN_SOFT_TIMESTAMP(9), \ -} - -static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4); -static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0); -static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0); -static DECLARE_AD7795_CHANNELS(ad7794, 16, 32); -static DECLARE_AD7795_CHANNELS(ad7795, 24, 32); - -static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { - [ID_AD7785] = { - .id = AD7785_ID, - .channels = ad7785_channels, - .num_channels = ARRAY_SIZE(ad7785_channels), - }, - [ID_AD7792] = { - .id = AD7792_ID, - .channels = ad7792_channels, - .num_channels = ARRAY_SIZE(ad7792_channels), - }, - [ID_AD7793] = { - .id = AD7793_ID, - .channels = ad7793_channels, - .num_channels = ARRAY_SIZE(ad7793_channels), - }, - [ID_AD7794] = { - .id = AD7794_ID, - .channels = ad7794_channels, - .num_channels = ARRAY_SIZE(ad7794_channels), - }, - [ID_AD7795] = { - .id = AD7795_ID, - .channels = ad7795_channels, - .num_channels = ARRAY_SIZE(ad7795_channels), - }, -}; - -static int ad7793_probe(struct spi_device *spi) -{ - const struct ad7793_platform_data *pdata = spi->dev.platform_data; - struct ad7793_state *st; - struct iio_dev *indio_dev; - int ret, vref_mv = 0; - - if (!pdata) { - dev_err(&spi->dev, "no platform data?\n"); - return -ENODEV; - } - - if (!spi->irq) { - dev_err(&spi->dev, "no IRQ?\n"); - return -ENODEV; - } - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) - return -ENOMEM; - - st = iio_priv(indio_dev); - - ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info); - - if (pdata->refsel != AD7793_REFSEL_INTERNAL) { - st->reg = regulator_get(&spi->dev, "refin"); - if (IS_ERR(st->reg)) { - ret = PTR_ERR(st->reg); - goto error_device_free; - } - - ret = regulator_enable(st->reg); - if (ret) - goto error_put_reg; - - vref_mv = regulator_get_voltage(st->reg); - if (vref_mv < 0) { - ret = vref_mv; - goto error_disable_reg; - } - - vref_mv /= 1000; - } else { - vref_mv = 1170; /* Build-in ref */ - } - - st->chip_info = - &ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - - spi_set_drvdata(spi, indio_dev); - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = st->chip_info->num_channels; - indio_dev->info = &ad7793_info; - - ret = ad_sd_setup_buffer_and_trigger(indio_dev); - if (ret) - goto error_disable_reg; - - ret = ad7793_setup(indio_dev, pdata, vref_mv); - if (ret) - goto error_remove_trigger; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_remove_trigger; - - return 0; - -error_remove_trigger: - ad_sd_cleanup_buffer_and_trigger(indio_dev); -error_disable_reg: - if (pdata->refsel != AD7793_REFSEL_INTERNAL) - regulator_disable(st->reg); -error_put_reg: - if (pdata->refsel != AD7793_REFSEL_INTERNAL) - regulator_put(st->reg); -error_device_free: - iio_device_free(indio_dev); - - return ret; -} - -static int ad7793_remove(struct spi_device *spi) -{ - const struct ad7793_platform_data *pdata = spi->dev.platform_data; - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7793_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - ad_sd_cleanup_buffer_and_trigger(indio_dev); - - if (pdata->refsel != AD7793_REFSEL_INTERNAL) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad7793_id[] = { - {"ad7785", ID_AD7785}, - {"ad7792", ID_AD7792}, - {"ad7793", ID_AD7793}, - {"ad7794", ID_AD7794}, - {"ad7795", ID_AD7795}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad7793_id); - -static struct spi_driver ad7793_driver = { - .driver = { - .name = "ad7793", - .owner = THIS_MODULE, - }, - .probe = ad7793_probe, - .remove = ad7793_remove, - .id_table = ad7793_id, -}; -module_spi_driver(ad7793_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7793.h b/drivers/staging/iio/adc/ad7793.h deleted file mode 100644 index 0e455de215e4..000000000000 --- a/drivers/staging/iio/adc/ad7793.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * AD7792/AD7793 SPI ADC driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ -#ifndef IIO_ADC_AD7793_H_ -#define IIO_ADC_AD7793_H_ - -/* - * TODO: struct ad7793_platform_data needs to go into include/linux/iio - */ - -/** - * enum ad7793_clock_source - AD7793 clock source selection - * @AD7793_CLK_SRC_INT: Internal 64 kHz clock, not available at the CLK pin. - * @AD7793_CLK_SRC_INT_CO: Internal 64 kHz clock, available at the CLK pin. - * @AD7793_CLK_SRC_EXT: Use external clock. - * @AD7793_CLK_SRC_EXT_DIV2: Use external clock divided by 2. - */ -enum ad7793_clock_source { - AD7793_CLK_SRC_INT, - AD7793_CLK_SRC_INT_CO, - AD7793_CLK_SRC_EXT, - AD7793_CLK_SRC_EXT_DIV2, -}; - -/** - * enum ad7793_bias_voltage - AD7793 bias voltage selection - * @AD7793_BIAS_VOLTAGE_DISABLED: Bias voltage generator disabled - * @AD7793_BIAS_VOLTAGE_AIN1: Bias voltage connected to AIN1(-). - * @AD7793_BIAS_VOLTAGE_AIN2: Bias voltage connected to AIN2(-). - * @AD7793_BIAS_VOLTAGE_AIN3: Bias voltage connected to AIN3(-). - * Only valid for AD7795/AD7796. - */ -enum ad7793_bias_voltage { - AD7793_BIAS_VOLTAGE_DISABLED, - AD7793_BIAS_VOLTAGE_AIN1, - AD7793_BIAS_VOLTAGE_AIN2, - AD7793_BIAS_VOLTAGE_AIN3, -}; - -/** - * enum ad7793_refsel - AD7793 reference voltage selection - * @AD7793_REFSEL_REFIN1: External reference applied between REFIN1(+) - * and REFIN1(-). - * @AD7793_REFSEL_REFIN2: External reference applied between REFIN2(+) and - * and REFIN1(-). Only valid for AD7795/AD7796. - * @AD7793_REFSEL_INTERNAL: Internal 1.17 V reference. - */ -enum ad7793_refsel { - AD7793_REFSEL_REFIN1 = 0, - AD7793_REFSEL_REFIN2 = 1, - AD7793_REFSEL_INTERNAL = 2, -}; - -/** - * enum ad7793_current_source_direction - AD7793 excitation current direction - * @AD7793_IEXEC1_IOUT1_IEXEC2_IOUT2: Current source IEXC1 connected to pin - * IOUT1, current source IEXC2 connected to pin IOUT2. - * @AD7793_IEXEC1_IOUT2_IEXEC2_IOUT1: Current source IEXC2 connected to pin - * IOUT1, current source IEXC1 connected to pin IOUT2. - * @AD7793_IEXEC1_IEXEC2_IOUT1: Both current sources connected to pin IOUT1. - * Only valid when the current sources are set to 10 uA or 210 uA. - * @AD7793_IEXEC1_IEXEC2_IOUT2: Both current sources connected to Pin IOUT2. - * Only valid when the current ources are set to 10 uA or 210 uA. - */ -enum ad7793_current_source_direction { - AD7793_IEXEC1_IOUT1_IEXEC2_IOUT2 = 0, - AD7793_IEXEC1_IOUT2_IEXEC2_IOUT1 = 1, - AD7793_IEXEC1_IEXEC2_IOUT1 = 2, - AD7793_IEXEC1_IEXEC2_IOUT2 = 3, -}; - -/** - * enum ad7793_excitation_current - AD7793 excitation current selection - * @AD7793_IX_DISABLED: Excitation current Disabled. - * @AD7793_IX_10uA: Enable 10 micro-ampere excitation current. - * @AD7793_IX_210uA: Enable 210 micro-ampere excitation current. - * @AD7793_IX_1mA: Enable 1 milli-Ampere excitation current. - */ -enum ad7793_excitation_current { - AD7793_IX_DISABLED = 0, - AD7793_IX_10uA = 1, - AD7793_IX_210uA = 2, - AD7793_IX_1mA = 3, -}; - -/** - * struct ad7793_platform_data - AD7793 platform data - * @clock_src: Clock source selection - * @burnout_current: If set to true the 100nA burnout current is enabled. - * @boost_enable: Enable boost for the bias voltage generator. - * @buffered: If set to true configure the device for buffered input mode. - * @unipolar: If set to true sample in unipolar mode, if set to false sample in - * bipolar mode. - * @refsel: Reference voltage selection - * @bias_voltage: Bias voltage selection - * @exitation_current: Excitation current selection - * @current_source_direction: Excitation current direction selection - */ -struct ad7793_platform_data { - enum ad7793_clock_source clock_src; - bool burnout_current; - bool boost_enable; - bool buffered; - bool unipolar; - - enum ad7793_refsel refsel; - enum ad7793_bias_voltage bias_voltage; - enum ad7793_excitation_current exitation_current; - enum ad7793_current_source_direction current_source_direction; -}; - -#endif /* IIO_ADC_AD7793_H_ */ diff --git a/include/linux/platform_data/ad7793.h b/include/linux/platform_data/ad7793.h new file mode 100644 index 000000000000..7ea6751aae6d --- /dev/null +++ b/include/linux/platform_data/ad7793.h @@ -0,0 +1,112 @@ +/* + * AD7792/AD7793 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ +#ifndef __LINUX_PLATFORM_DATA_AD7793_H__ +#define __LINUX_PLATFORM_DATA_AD7793_H__ + +/** + * enum ad7793_clock_source - AD7793 clock source selection + * @AD7793_CLK_SRC_INT: Internal 64 kHz clock, not available at the CLK pin. + * @AD7793_CLK_SRC_INT_CO: Internal 64 kHz clock, available at the CLK pin. + * @AD7793_CLK_SRC_EXT: Use external clock. + * @AD7793_CLK_SRC_EXT_DIV2: Use external clock divided by 2. + */ +enum ad7793_clock_source { + AD7793_CLK_SRC_INT, + AD7793_CLK_SRC_INT_CO, + AD7793_CLK_SRC_EXT, + AD7793_CLK_SRC_EXT_DIV2, +}; + +/** + * enum ad7793_bias_voltage - AD7793 bias voltage selection + * @AD7793_BIAS_VOLTAGE_DISABLED: Bias voltage generator disabled + * @AD7793_BIAS_VOLTAGE_AIN1: Bias voltage connected to AIN1(-). + * @AD7793_BIAS_VOLTAGE_AIN2: Bias voltage connected to AIN2(-). + * @AD7793_BIAS_VOLTAGE_AIN3: Bias voltage connected to AIN3(-). + * Only valid for AD7795/AD7796. + */ +enum ad7793_bias_voltage { + AD7793_BIAS_VOLTAGE_DISABLED, + AD7793_BIAS_VOLTAGE_AIN1, + AD7793_BIAS_VOLTAGE_AIN2, + AD7793_BIAS_VOLTAGE_AIN3, +}; + +/** + * enum ad7793_refsel - AD7793 reference voltage selection + * @AD7793_REFSEL_REFIN1: External reference applied between REFIN1(+) + * and REFIN1(-). + * @AD7793_REFSEL_REFIN2: External reference applied between REFIN2(+) and + * and REFIN1(-). Only valid for AD7795/AD7796. + * @AD7793_REFSEL_INTERNAL: Internal 1.17 V reference. + */ +enum ad7793_refsel { + AD7793_REFSEL_REFIN1 = 0, + AD7793_REFSEL_REFIN2 = 1, + AD7793_REFSEL_INTERNAL = 2, +}; + +/** + * enum ad7793_current_source_direction - AD7793 excitation current direction + * @AD7793_IEXEC1_IOUT1_IEXEC2_IOUT2: Current source IEXC1 connected to pin + * IOUT1, current source IEXC2 connected to pin IOUT2. + * @AD7793_IEXEC1_IOUT2_IEXEC2_IOUT1: Current source IEXC2 connected to pin + * IOUT1, current source IEXC1 connected to pin IOUT2. + * @AD7793_IEXEC1_IEXEC2_IOUT1: Both current sources connected to pin IOUT1. + * Only valid when the current sources are set to 10 uA or 210 uA. + * @AD7793_IEXEC1_IEXEC2_IOUT2: Both current sources connected to Pin IOUT2. + * Only valid when the current ources are set to 10 uA or 210 uA. + */ +enum ad7793_current_source_direction { + AD7793_IEXEC1_IOUT1_IEXEC2_IOUT2 = 0, + AD7793_IEXEC1_IOUT2_IEXEC2_IOUT1 = 1, + AD7793_IEXEC1_IEXEC2_IOUT1 = 2, + AD7793_IEXEC1_IEXEC2_IOUT2 = 3, +}; + +/** + * enum ad7793_excitation_current - AD7793 excitation current selection + * @AD7793_IX_DISABLED: Excitation current Disabled. + * @AD7793_IX_10uA: Enable 10 micro-ampere excitation current. + * @AD7793_IX_210uA: Enable 210 micro-ampere excitation current. + * @AD7793_IX_1mA: Enable 1 milli-Ampere excitation current. + */ +enum ad7793_excitation_current { + AD7793_IX_DISABLED = 0, + AD7793_IX_10uA = 1, + AD7793_IX_210uA = 2, + AD7793_IX_1mA = 3, +}; + +/** + * struct ad7793_platform_data - AD7793 platform data + * @clock_src: Clock source selection + * @burnout_current: If set to true the 100nA burnout current is enabled. + * @boost_enable: Enable boost for the bias voltage generator. + * @buffered: If set to true configure the device for buffered input mode. + * @unipolar: If set to true sample in unipolar mode, if set to false sample in + * bipolar mode. + * @refsel: Reference voltage selection + * @bias_voltage: Bias voltage selection + * @exitation_current: Excitation current selection + * @current_source_direction: Excitation current direction selection + */ +struct ad7793_platform_data { + enum ad7793_clock_source clock_src; + bool burnout_current; + bool boost_enable; + bool buffered; + bool unipolar; + + enum ad7793_refsel refsel; + enum ad7793_bias_voltage bias_voltage; + enum ad7793_excitation_current exitation_current; + enum ad7793_current_source_direction current_source_direction; +}; + +#endif /* IIO_ADC_AD7793_H_ */ -- cgit v1.2.3 From 2edb769d246e6b549ba1c5e9b283bfd058584cbb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 21 Nov 2012 16:27:00 +0000 Subject: iio:ad7793: Add support for the ad7798 and ad7799 The ad7798 and ad7799 are similar to the ad7792 and ad7793 but are missing some features like the temperature sensor, being able to use an external clocksource and a few other things. This patch adds a new 'flags' fields to the chip_info struct which allows to specify which features a certain chip variant supports. The setup code will then ignore any platform data fields which are related to non supported features. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 116 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 14 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index a109e686b9f7..91a5f7aa1ef1 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -106,6 +106,8 @@ #define AD7793_ID 0xB #define AD7794_ID 0xF #define AD7795_ID 0xF +#define AD7798_ID 0x8 +#define AD7799_ID 0x9 #define AD7793_ID_MASK 0xF /* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */ @@ -130,10 +132,16 @@ * The DOUT/RDY output must also be wired to an interrupt capable GPIO. */ +#define AD7793_FLAG_HAS_CLKSEL BIT(0) +#define AD7793_FLAG_HAS_REFSEL BIT(1) +#define AD7793_FLAG_HAS_VBIAS BIT(2) +#define AD7793_HAS_EXITATION_CURRENT BIT(3) + struct ad7793_chip_info { unsigned int id; const struct iio_chan_spec *channels; unsigned int num_channels; + unsigned int flags; }; struct ad7793_state { @@ -154,6 +162,8 @@ enum ad7793_supported_device_ids { ID_AD7793, ID_AD7794, ID_AD7795, + ID_AD7798, + ID_AD7799, }; static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd) @@ -205,6 +215,34 @@ static int ad7793_calibrate_all(struct ad7793_state *st) ARRAY_SIZE(ad7793_calib_arr)); } +static int ad7793_check_platform_data(struct ad7793_state *st, + const struct ad7793_platform_data *pdata) +{ + if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 || + pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) && + ((pdata->exitation_current != AD7793_IX_10uA) && + (pdata->exitation_current != AD7793_IX_210uA))) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) && + pdata->clock_src != AD7793_CLK_SRC_INT) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) && + pdata->refsel != AD7793_REFSEL_REFIN1) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) && + pdata->bias_voltage != AD7793_BIAS_VOLTAGE_DISABLED) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) && + pdata->exitation_current != AD7793_IX_DISABLED) + return -EINVAL; + + return 0; +} + static int ad7793_setup(struct iio_dev *indio_dev, const struct ad7793_platform_data *pdata, unsigned int vref_mv) @@ -214,11 +252,9 @@ static int ad7793_setup(struct iio_dev *indio_dev, unsigned long long scale_uv; u32 id; - if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 || - pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) && - ((pdata->exitation_current != AD7793_IX_10uA) && - (pdata->exitation_current != AD7793_IX_210uA))) - return -EINVAL; + ret = ad7793_check_platform_data(st, pdata); + if (ret) + return ret; /* reset the serial interface */ ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret)); @@ -239,12 +275,18 @@ static int ad7793_setup(struct iio_dev *indio_dev, } st->mode = AD7793_MODE_RATE(1); - st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src); - st->conf = AD7793_CONF_REFSEL(pdata->refsel); - st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage); + st->conf = 0; + + if (st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) + st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src); + if (st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) + st->conf |= AD7793_CONF_REFSEL(pdata->refsel); + if (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) + st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage); if (pdata->buffered) st->conf |= AD7793_CONF_BUF; - if (pdata->boost_enable) + if (pdata->boost_enable && + (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS)) st->conf |= AD7793_CONF_BOOST; if (pdata->burnout_current) st->conf |= AD7793_CONF_BO_EN; @@ -259,11 +301,13 @@ static int ad7793_setup(struct iio_dev *indio_dev, if (ret) goto out; - ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1, - pdata->exitation_current | - (pdata->current_source_direction << 2)); - if (ret) - goto out; + if (st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) { + ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1, + pdata->exitation_current | + (pdata->current_source_direction << 2)); + if (ret) + goto out; + } ret = ad7793_calibrate_all(st); if (ret) @@ -525,37 +569,79 @@ const struct iio_chan_spec _name##_channels[] = { \ IIO_CHAN_SOFT_TIMESTAMP(9), \ } +#define DECLARE_AD7799_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD_SD_SUPPLY_CHANNEL(4, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(5), \ +} + static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4); static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0); static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0); static DECLARE_AD7795_CHANNELS(ad7794, 16, 32); static DECLARE_AD7795_CHANNELS(ad7795, 24, 32); +static DECLARE_AD7799_CHANNELS(ad7798, 16, 16); +static DECLARE_AD7799_CHANNELS(ad7799, 24, 32); static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { [ID_AD7785] = { .id = AD7785_ID, .channels = ad7785_channels, .num_channels = ARRAY_SIZE(ad7785_channels), + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT, }, [ID_AD7792] = { .id = AD7792_ID, .channels = ad7792_channels, .num_channels = ARRAY_SIZE(ad7792_channels), + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT, }, [ID_AD7793] = { .id = AD7793_ID, .channels = ad7793_channels, .num_channels = ARRAY_SIZE(ad7793_channels), + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT, }, [ID_AD7794] = { .id = AD7794_ID, .channels = ad7794_channels, .num_channels = ARRAY_SIZE(ad7794_channels), + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT, }, [ID_AD7795] = { .id = AD7795_ID, .channels = ad7795_channels, .num_channels = ARRAY_SIZE(ad7795_channels), + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT, + }, + [ID_AD7798] = { + .id = AD7798_ID, + .channels = ad7798_channels, + .num_channels = ARRAY_SIZE(ad7798_channels), + }, + [ID_AD7799] = { + .id = AD7799_ID, + .channels = ad7799_channels, + .num_channels = ARRAY_SIZE(ad7799_channels), }, }; @@ -671,6 +757,8 @@ static const struct spi_device_id ad7793_id[] = { {"ad7793", ID_AD7793}, {"ad7794", ID_AD7794}, {"ad7795", ID_AD7795}, + {"ad7798", ID_AD7798}, + {"ad7799", ID_AD7799}, {} }; MODULE_DEVICE_TABLE(spi, ad7793_id); -- cgit v1.2.3 From fd1a8b9128410c2ea7da69180788e5d144c6ef46 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 21 Nov 2012 16:27:00 +0000 Subject: iio:ad7793: Add support for the ad7796 and ad7797 The ad7796 and ad7797 are similar to the ad7792 and ad7793 but only have a single differential input instead of two. Also some other features are missing like the programmable gain amplifier and also not all sampling frequencies supported by the ad7792/ad7793 are supported by the ad7796/ad7797. This patch adds new feature flags for the features not present in the ad7796/ad7797. The patch also adds a struct iio_info field to the chip_info struct, this becomes necessary since the ad7796/ad7797 needs a special set of sysfs attributes. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 121 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 12 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 91a5f7aa1ef1..334e31ff7a4e 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -106,6 +106,8 @@ #define AD7793_ID 0xB #define AD7794_ID 0xF #define AD7795_ID 0xF +#define AD7796_ID 0xA +#define AD7797_ID 0xB #define AD7798_ID 0x8 #define AD7799_ID 0x9 #define AD7793_ID_MASK 0xF @@ -136,12 +138,17 @@ #define AD7793_FLAG_HAS_REFSEL BIT(1) #define AD7793_FLAG_HAS_VBIAS BIT(2) #define AD7793_HAS_EXITATION_CURRENT BIT(3) +#define AD7793_FLAG_HAS_GAIN BIT(4) +#define AD7793_FLAG_HAS_BUFFER BIT(5) struct ad7793_chip_info { unsigned int id; const struct iio_chan_spec *channels; unsigned int num_channels; unsigned int flags; + + const struct iio_info *iio_info; + const u16 *sample_freq_avail; }; struct ad7793_state { @@ -162,6 +169,8 @@ enum ad7793_supported_device_ids { ID_AD7793, ID_AD7794, ID_AD7795, + ID_AD7796, + ID_AD7797, ID_AD7798, ID_AD7799, }; @@ -283,7 +292,7 @@ static int ad7793_setup(struct iio_dev *indio_dev, st->conf |= AD7793_CONF_REFSEL(pdata->refsel); if (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage); - if (pdata->buffered) + if (pdata->buffered || !(st->chip_info->flags & AD7793_FLAG_HAS_BUFFER)) st->conf |= AD7793_CONF_BUF; if (pdata->boost_enable && (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS)) @@ -293,6 +302,9 @@ static int ad7793_setup(struct iio_dev *indio_dev, if (pdata->unipolar) st->conf |= AD7793_CONF_UNIPOLAR; + if (!(st->chip_info->flags & AD7793_FLAG_HAS_GAIN)) + st->conf |= AD7793_CONF_GAIN(7); + ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE); if (ret) goto out; @@ -330,8 +342,11 @@ out: return ret; } -static const u16 sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, 33, 19, - 17, 16, 12, 10, 8, 6, 4}; +static const u16 ad7793_sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, + 33, 19, 17, 16, 12, 10, 8, 6, 4}; + +static const u16 ad7797_sample_freq_avail[16] = {0, 0, 0, 123, 62, 50, 0, + 33, 0, 17, 16, 12, 10, 8, 6, 4}; static ssize_t ad7793_read_frequency(struct device *dev, struct device_attribute *attr, @@ -341,7 +356,7 @@ static ssize_t ad7793_read_frequency(struct device *dev, struct ad7793_state *st = iio_priv(indio_dev); return sprintf(buf, "%d\n", - sample_freq_avail[AD7793_MODE_RATE(st->mode)]); + st->chip_info->sample_freq_avail[AD7793_MODE_RATE(st->mode)]); } static ssize_t ad7793_write_frequency(struct device *dev, @@ -365,10 +380,13 @@ static ssize_t ad7793_write_frequency(struct device *dev, if (ret) return ret; + if (lval == 0) + return -EINVAL; + ret = -EINVAL; - for (i = 0; i < ARRAY_SIZE(sample_freq_avail); i++) - if (lval == sample_freq_avail[i]) { + for (i = 0; i < 16; i++) + if (lval == st->chip_info->sample_freq_avail[i]) { mutex_lock(&indio_dev->mlock); st->mode &= ~AD7793_MODE_RATE(-1); st->mode |= AD7793_MODE_RATE(i); @@ -388,6 +406,9 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( "470 242 123 62 50 39 33 19 17 16 12 10 8 6 4"); +static IIO_CONST_ATTR_NAMED(sampling_frequency_available_ad7797, + sampling_frequency_available, "123 62 50 33 17 16 12 10 8 6 4"); + static ssize_t ad7793_show_scale_available(struct device *dev, struct device_attribute *attr, char *buf) { @@ -419,6 +440,16 @@ static const struct attribute_group ad7793_attribute_group = { .attrs = ad7793_attributes, }; +static struct attribute *ad7797_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available_ad7797.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7797_attribute_group = { + .attrs = ad7797_attributes, +}; + static int ad7793_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, @@ -544,6 +575,15 @@ static const struct iio_info ad7793_info = { .driver_module = THIS_MODULE, }; +static const struct iio_info ad7797_info = { + .read_raw = &ad7793_read_raw, + .write_raw = &ad7793_write_raw, + .write_raw_get_fmt = &ad7793_write_raw_get_fmt, + .attrs = &ad7793_attribute_group, + .validate_trigger = ad_sd_validate_trigger, + .driver_module = THIS_MODULE, +}; + #define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \ const struct iio_chan_spec _name##_channels[] = { \ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \ @@ -569,6 +609,15 @@ const struct iio_chan_spec _name##_channels[] = { \ IIO_CHAN_SOFT_TIMESTAMP(9), \ } +#define DECLARE_AD7797_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD_SD_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD_SD_TEMP_CHANNEL(2, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD_SD_SUPPLY_CHANNEL(3, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + #define DECLARE_AD7799_CHANNELS(_name, _b, _sb) \ const struct iio_chan_spec _name##_channels[] = { \ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ @@ -584,6 +633,8 @@ static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0); static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0); static DECLARE_AD7795_CHANNELS(ad7794, 16, 32); static DECLARE_AD7795_CHANNELS(ad7795, 24, 32); +static DECLARE_AD7797_CHANNELS(ad7796, 16, 16); +static DECLARE_AD7797_CHANNELS(ad7797, 24, 32); static DECLARE_AD7799_CHANNELS(ad7798, 16, 16); static DECLARE_AD7799_CHANNELS(ad7799, 24, 32); @@ -592,56 +643,100 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { .id = AD7785_ID, .channels = ad7785_channels, .num_channels = ARRAY_SIZE(ad7785_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, .flags = AD7793_FLAG_HAS_CLKSEL | AD7793_FLAG_HAS_REFSEL | AD7793_FLAG_HAS_VBIAS | - AD7793_HAS_EXITATION_CURRENT, + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, }, [ID_AD7792] = { .id = AD7792_ID, .channels = ad7792_channels, .num_channels = ARRAY_SIZE(ad7792_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, .flags = AD7793_FLAG_HAS_CLKSEL | AD7793_FLAG_HAS_REFSEL | AD7793_FLAG_HAS_VBIAS | - AD7793_HAS_EXITATION_CURRENT, + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, }, [ID_AD7793] = { .id = AD7793_ID, .channels = ad7793_channels, .num_channels = ARRAY_SIZE(ad7793_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, .flags = AD7793_FLAG_HAS_CLKSEL | AD7793_FLAG_HAS_REFSEL | AD7793_FLAG_HAS_VBIAS | - AD7793_HAS_EXITATION_CURRENT, + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, }, [ID_AD7794] = { .id = AD7794_ID, .channels = ad7794_channels, .num_channels = ARRAY_SIZE(ad7794_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, .flags = AD7793_FLAG_HAS_CLKSEL | AD7793_FLAG_HAS_REFSEL | AD7793_FLAG_HAS_VBIAS | - AD7793_HAS_EXITATION_CURRENT, + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, }, [ID_AD7795] = { .id = AD7795_ID, .channels = ad7795_channels, .num_channels = ARRAY_SIZE(ad7795_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, .flags = AD7793_FLAG_HAS_CLKSEL | AD7793_FLAG_HAS_REFSEL | AD7793_FLAG_HAS_VBIAS | - AD7793_HAS_EXITATION_CURRENT, + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7796] = { + .id = AD7796_ID, + .channels = ad7796_channels, + .num_channels = ARRAY_SIZE(ad7796_channels), + .iio_info = &ad7797_info, + .sample_freq_avail = ad7797_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL, + }, + [ID_AD7797] = { + .id = AD7797_ID, + .channels = ad7797_channels, + .num_channels = ARRAY_SIZE(ad7797_channels), + .iio_info = &ad7797_info, + .sample_freq_avail = ad7797_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL, }, [ID_AD7798] = { .id = AD7798_ID, .channels = ad7798_channels, .num_channels = ARRAY_SIZE(ad7798_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, }, [ID_AD7799] = { .id = AD7799_ID, .channels = ad7799_channels, .num_channels = ARRAY_SIZE(ad7799_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, }, }; @@ -702,7 +797,7 @@ static int ad7793_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; - indio_dev->info = &ad7793_info; + indio_dev->info = st->chip_info->iio_info; ret = ad_sd_setup_buffer_and_trigger(indio_dev); if (ret) @@ -757,6 +852,8 @@ static const struct spi_device_id ad7793_id[] = { {"ad7793", ID_AD7793}, {"ad7794", ID_AD7794}, {"ad7795", ID_AD7795}, + {"ad7796", ID_AD7796}, + {"ad7797", ID_AD7797}, {"ad7798", ID_AD7798}, {"ad7799", ID_AD7799}, {} -- cgit v1.2.3 From bc0a409c5ffd91b5403037ab2798b84dccee4f06 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 23 Nov 2012 15:13:00 +0000 Subject: iio: adc: Add Texas Instruments ADC081C021/027 support Add support for reading conversion results from the ADC and provide them through a single IIO channel. A proper scaling factor is also exported based on the reference voltage provided by a regulator. Signed-off-by: Thierry Reding Reviewed-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 +++ drivers/iio/adc/Makefile | 2 + drivers/iio/adc/ti-adc081c.c | 161 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 drivers/iio/adc/ti-adc081c.c (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 408557b02441..961b8d0a4bac 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -115,4 +115,14 @@ config MAX1363 max11646, max11647) Provides direct access via sysfs and buffered data via the iio dev interface. +config TI_ADC081C + tristate "Texas Instruments ADC081C021/027" + depends on I2C + help + If you say yes here you get support for Texas Instruments ADC081C021 + and ADC081C027 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc081c. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 78202d9eb961..472fd7cd2417 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1363) += max1363.o +obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o + diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c new file mode 100644 index 000000000000..f4a46dd8f43b --- /dev/null +++ b/drivers/iio/adc/ti-adc081c.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include +#include + +struct adc081c { + struct i2c_client *i2c; + struct regulator *ref; +}; + +#define REG_CONV_RES 0x00 + +static int adc081c_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc081c *adc = iio_priv(iio); + int err; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES); + if (err < 0) + return err; + + *value = (err >> 4) & 0xff; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + err = regulator_get_voltage(adc->ref); + if (err < 0) + return err; + + *value = err / 1000; + *shift = 8; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + break; + } + + return -EINVAL; +} + +static const struct iio_chan_spec adc081c_channel = { + .type = IIO_VOLTAGE, + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_RAW_SEPARATE_BIT, +}; + +static const struct iio_info adc081c_info = { + .read_raw = adc081c_read_raw, + .driver_module = THIS_MODULE, +}; + +static int adc081c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *iio; + struct adc081c *adc; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + iio = iio_device_alloc(sizeof(*adc)); + if (!iio) + return -ENOMEM; + + adc = iio_priv(iio); + adc->i2c = client; + + adc->ref = regulator_get(&client->dev, "vref"); + if (IS_ERR(adc->ref)) { + err = PTR_ERR(adc->ref); + goto iio_free; + } + + err = regulator_enable(adc->ref); + if (err < 0) + goto regulator_put; + + iio->dev.parent = &client->dev; + iio->name = dev_name(&client->dev); + iio->modes = INDIO_DIRECT_MODE; + iio->info = &adc081c_info; + + iio->channels = &adc081c_channel; + iio->num_channels = 1; + + err = iio_device_register(iio); + if (err < 0) + goto regulator_disable; + + i2c_set_clientdata(client, iio); + + return 0; + +regulator_disable: + regulator_disable(adc->ref); +regulator_put: + regulator_put(adc->ref); +iio_free: + iio_device_free(iio); + + return err; +} + +static int adc081c_remove(struct i2c_client *client) +{ + struct iio_dev *iio = i2c_get_clientdata(client); + struct adc081c *adc = iio_priv(iio); + + iio_device_unregister(iio); + regulator_disable(adc->ref); + regulator_put(adc->ref); + iio_device_free(iio); + + return 0; +} + +static const struct i2c_device_id adc081c_id[] = { + { "adc081c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adc081c_id); + +#ifdef CONFIG_OF +static const struct of_device_id adc081c_of_match[] = { + { .compatible = "ti,adc081c" }, + { } +}; +MODULE_DEVICE_TABLE(of, adc081c_of_match); +#endif + +static struct i2c_driver adc081c_driver = { + .driver = { + .name = "adc081c", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(adc081c_of_match), + }, + .probe = adc081c_probe, + .remove = adc081c_remove, + .id_table = adc081c_id, +}; +module_i2c_driver(adc081c_driver); + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 12660138b491b56d1e70333547912c56741be5e8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Nov 2012 07:24:00 +0000 Subject: iio:gyro: adis16136: divide by zero in write_frequency() It's slightly cleaner to use kstrtouint() because we pass unsigned ints to adis16136_set_freq(). On 64 bit systems, if the user passed LONG_MIN then it we would get past the test against zero but crash in adis16136_set_freq() because we truncate the high bits away. Signed-off-by: Dan Carpenter Acked-By: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/adis16136.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index 05486dfd721b..b4ee3393b772 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -203,10 +203,10 @@ static ssize_t adis16136_write_frequency(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct adis16136 *adis16136 = iio_priv(indio_dev); - long val; + unsigned int val; int ret; - ret = kstrtol(buf, 10, &val); + ret = kstrtouint(buf, 10, &val); if (ret) return ret; -- cgit v1.2.3 From afc3a57a2efc4af623df07771c6b1aef15158537 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Nov 2012 07:31:00 +0000 Subject: iio:imu: adis16480: show_firmware() buffer too small Smatch complains that snprintf() returns the number of characters, not counting the NUL terminator, which *would* have been printed if there were enough space. In other words the return value could be more than sizeof(buf). In this case, we are printing something like "ff.ff\n" which is at most 6 characters and a NUL so that's not an issue. I changed snprintf() to scnprintf() to silence the warning. But since the buffer doesn't include space for the NUL terminator, we need to make it bigger or the "\n" will be truncated off. Signed-off-by: Dan Carpenter Acked-By: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16480.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index a080b3515015..150d7faa9275 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -125,7 +125,7 @@ static ssize_t adis16480_show_firmware_revision(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct adis16480 *adis16480 = file->private_data; - char buf[6]; + char buf[7]; size_t len; u16 rev; int ret; @@ -134,7 +134,7 @@ static ssize_t adis16480_show_firmware_revision(struct file *file, if (ret < 0) return ret; - len = snprintf(buf, sizeof(buf), "%x.%x\n", rev >> 8, rev & 0xff); + len = scnprintf(buf, sizeof(buf), "%x.%x\n", rev >> 8, rev & 0xff); return simple_read_from_buffer(userbuf, count, ppos, buf, len); } -- cgit v1.2.3 From 11ef9afdba19f406743f755cdeaa4e69a5068941 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 29 Nov 2012 02:27:00 +0000 Subject: iio: gyro: adis16136: remove duplicated include from adis16136.c Remove duplicated include. Signed-off-by: Wei Yongjun Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/adis16136.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index b4ee3393b772..8cb0bcbfd609 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -22,7 +22,6 @@ #include #include -#include #include #define ADIS16136_REG_FLASH_CNT 0x00 -- cgit v1.2.3 From 583f2deb9349eb55d913eb92a31273f3907d72ea Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 29 Nov 2012 02:27:00 +0000 Subject: iio: imu: adis16480: remove duplicated include from adis16480.c Remove duplicated include. Signed-off-by: Wei Yongjun Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16480.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 150d7faa9275..8c26a5f7cd5d 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -24,7 +24,6 @@ #include #include -#include #include #define ADIS16480_PAGE_SIZE 0x80 -- cgit v1.2.3 From 1690970d0d28051e2652d9bae2f73288e017e142 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Thu, 6 Dec 2012 16:09:00 +0000 Subject: iio:adf4350: Fix typo There is a typo in the adf4350 driver turning a shift into a compare. This patch fixes it. Signed-off-by: Michael Hennerich Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4350.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index e35bb8f6fe75..c7de8b5a01ff 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -173,7 +173,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt); } while (r_cnt == 0); - tmp = freq * (u64)st->r1_mod + (st->fpfd > 1); + tmp = freq * (u64)st->r1_mod + (st->fpfd >> 1); do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */ st->r0_fract = do_div(tmp, st->r1_mod); st->r0_int = tmp; -- cgit v1.2.3 From 116797672ffdd3635eafa25db5bd312e65c5ad01 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 12 Dec 2012 15:17:00 +0000 Subject: iio: at91: fix dev var name in at91_adc_trigger_handler /opt/work/linux-2.6/drivers/iio/adc/at91_adc.c: In function 'at91_adc_trigger_handler': /opt/work/linux-2.6/drivers/iio/adc/at91_adc.c:83:22: error: 'indio_dev' undeclared (first use in this function) /opt/work/linux-2.6/drivers/iio/adc/at91_adc.c:83:22: note: each undeclared identifier is reported only once for each function it appears in Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Cc: linux-iio@vger.kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 03b85940f4ba..315bed1f401f 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -80,7 +80,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - iio_push_to_buffers(indio_dev, (u8 *)st->buffer); + iio_push_to_buffers(idev, (u8 *)st->buffer); iio_trigger_notify_done(idev->trig); -- cgit v1.2.3 From 9541cc39a6381b76dac30c8e05078eb0a543f113 Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Tue, 11 Dec 2012 18:21:00 +0000 Subject: iio: hid-sensors: respect CONFIG_IIO_TRIGGER Not much to say, without that change, hid-sensor-trigger will be always compiled if HID_SENSOR_IIO_COMMON is selected which fails if CONFIG_IIO_TRIGGER is not set because CONFIG_IIO_CONSUMERS_PER_TRIGGER will not be defined. Signed-off-by: Alexander Holler Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 1 + drivers/iio/common/hid-sensors/Kconfig | 13 ++++++++++++- drivers/iio/common/hid-sensors/Makefile | 3 ++- drivers/iio/gyro/Kconfig | 1 + drivers/iio/light/Kconfig | 1 + drivers/iio/magnetometer/Kconfig | 1 + 6 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index fe4bcd7c5b12..05e996fafc9d 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -8,6 +8,7 @@ config HID_SENSOR_ACCEL_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID Accelerometers 3D" help Say yes here to build support for the HID SENSOR diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig index ae10778da7aa..1178121b55b0 100644 --- a/drivers/iio/common/hid-sensors/Kconfig +++ b/drivers/iio/common/hid-sensors/Kconfig @@ -6,7 +6,7 @@ menu "Hid Sensor IIO Common" config HID_SENSOR_IIO_COMMON tristate "Common modules for all HID Sensor IIO drivers" depends on HID_SENSOR_HUB - select IIO_TRIGGER if IIO_BUFFER + select HID_SENSOR_IIO_TRIGGER if IIO_BUFFER help Say yes here to build support for HID sensor to use HID sensor common processing for attributes and IIO triggers. @@ -14,6 +14,17 @@ config HID_SENSOR_IIO_COMMON HID sensor drivers, this module contains processing for those attributes. +config HID_SENSOR_IIO_TRIGGER + tristate "Common module (trigger) for all HID Sensor IIO drivers" + depends on HID_SENSOR_HUB && HID_SENSOR_IIO_COMMON + select IIO_TRIGGER + help + Say yes here to build trigger support for HID sensors. + Triggers will be send if all requested attributes were read. + + If this driver is compiled as a module, it will be named + hid-sensor-trigger. + config HID_SENSOR_ENUM_BASE_QUIRKS bool "ENUM base quirks for HID Sensor IIO drivers" depends on HID_SENSOR_IIO_COMMON diff --git a/drivers/iio/common/hid-sensors/Makefile b/drivers/iio/common/hid-sensors/Makefile index 1f463e00c242..22e7c5a82325 100644 --- a/drivers/iio/common/hid-sensors/Makefile +++ b/drivers/iio/common/hid-sensors/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o -hid-sensor-iio-common-y := hid-sensor-attributes.o hid-sensor-trigger.o +obj-$(CONFIG_HID_SENSOR_IIO_TRIGGER) += hid-sensor-trigger.o +hid-sensor-iio-common-y := hid-sensor-attributes.o diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 48ed1483ff27..96b68f63a902 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -17,6 +17,7 @@ config HID_SENSOR_GYRO_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID Gyroscope 3D" help Say yes here to build support for the HID SENSOR diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 1763c9bcb98a..dbf80abc834f 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -47,6 +47,7 @@ config HID_SENSOR_ALS select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID ALS" help Say yes here to build support for the HID SENSOR diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index c1f0cdd57037..ff11d68225cf 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -8,6 +8,7 @@ config HID_SENSOR_MAGNETOMETER_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID Magenetometer 3D" help Say yes here to build support for the HID SENSOR -- cgit v1.2.3 From 36ce0c1c3ab7ea54fa6c2b3a0803c7ab0adcefbf Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 14 Dec 2012 07:47:00 +0000 Subject: iio: adc: ad7266: Don't set error code to st->vref_uv regulator_get_voltage() may return negative error code. Add error checking to avoid setting error code to st->vref_uv. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7266.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index a6f4fc5f8201..e36107dc30b9 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -411,7 +411,11 @@ static int __devinit ad7266_probe(struct spi_device *spi) if (ret) goto error_put_reg; - st->vref_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + st->vref_uv = ret; } else { /* Use internal reference */ st->vref_uv = 2500000; -- cgit v1.2.3 From 272cc9c8b5d0dffe9fb03e6c0ab3da2fde9c20e9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 14 Dec 2012 07:48:00 +0000 Subject: iio: dac: ad5380: Don't set error code to st->vref regulator_get_voltage() may return negative error code. Add error checking to avoid setting error code to st->vref_uv. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5380.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 14991ac55f26..4aca1895ce52 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -406,7 +406,11 @@ static int __devinit ad5380_probe(struct device *dev, struct regmap *regmap, goto error_free_reg; } - st->vref = regulator_get_voltage(st->vref_reg); + ret = regulator_get_voltage(st->vref_reg); + if (ret < 0) + goto error_disable_reg; + + st->vref = ret; } else { st->vref = st->chip_info->int_vref; ctrl |= AD5380_CTRL_INT_VREF_EN; -- cgit v1.2.3 From 7e2dcc69824288916b55f90cd6db4992a0d95374 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 14 Dec 2012 07:55:00 +0000 Subject: iio: dac: ad5791: Don't set error code to [pos|neg]_voltage_uv regulator_get_voltage() may return negative error code. Don't set error code to to pos_voltage_uv and neg_voltage_uv. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5791.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index 2bd2e37280ff..6efe83e32ac6 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -365,7 +365,11 @@ static int __devinit ad5791_probe(struct spi_device *spi) if (ret) goto error_put_reg_pos; - pos_voltage_uv = regulator_get_voltage(st->reg_vdd); + ret = regulator_get_voltage(st->reg_vdd); + if (ret < 0) + goto error_disable_reg_pos; + + pos_voltage_uv = ret; } st->reg_vss = regulator_get(&spi->dev, "vss"); @@ -374,7 +378,11 @@ static int __devinit ad5791_probe(struct spi_device *spi) if (ret) goto error_put_reg_neg; - neg_voltage_uv = regulator_get_voltage(st->reg_vss); + ret = regulator_get_voltage(st->reg_vss); + if (ret < 0) + goto error_disable_reg_neg; + + neg_voltage_uv = ret; } st->pwr_down = true; @@ -428,6 +436,7 @@ error_put_reg_neg: if (!IS_ERR(st->reg_vss)) regulator_put(st->reg_vss); +error_disable_reg_pos: if (!IS_ERR(st->reg_vdd)) regulator_disable(st->reg_vdd); error_put_reg_pos: -- cgit v1.2.3 From 0ce2fdaa97c6a8d84e9fce3d920a45a493145623 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 14 Dec 2012 12:06:00 +0000 Subject: iio: dac: ad5504: Don't set error code to voltage_uv regulator_get_voltage() may return negative error code. Add error checking to avoid setting error code to voltage_uv. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5504.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 242bdc7d0044..7f7c026b9d8a 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -296,7 +296,11 @@ static int __devinit ad5504_probe(struct spi_device *spi) if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(reg); + ret = regulator_get_voltage(reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } spi_set_drvdata(spi, indio_dev); -- cgit v1.2.3 From 8434e7856551015650f62afa48e8f9e9b4bf80f2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 14 Dec 2012 12:07:00 +0000 Subject: iio: dac: ad5624r_spi: Don't set error code to voltage_uv regulator_get_voltage() may return negative error code. Add error checking to avoid setting error code to voltage_uv. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5624r_spi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 6a7d6a48cc6d..14ea3dbf4902 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -238,7 +238,11 @@ static int __devinit ad5624r_probe(struct spi_device *spi) if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } spi_set_drvdata(spi, indio_dev); -- cgit v1.2.3 From 359570ad3fcfcc3a3031b3a5e01bfb616233eca0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 14 Dec 2012 12:08:00 +0000 Subject: iio: dac: ad5686: Don't set error code to voltage_uv regulator_get_voltage() may return negative error code. Add error checking to avoid setting error code to voltage_uv. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index bc92ff9309c2..01eb1d0e4e41 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -332,7 +332,11 @@ static int __devinit ad5686_probe(struct spi_device *spi) if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } st->chip_info = -- cgit v1.2.3 From 13e57ee2056221296f1926d49d00edd224058bea Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 18 Dec 2012 03:33:00 +0000 Subject: iio: dac: ad5446: Don't set error code to voltage_uv regulator_get_voltage() may return negative error code. Add error checking to avoid setting error code to voltage_uv. Signed-off-by: Axel Lin Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5446.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 3310cbbd41e7..ecb639540dff 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -226,7 +226,11 @@ static int __devinit ad5446_probe(struct device *dev, const char *name, if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(reg); + ret = regulator_get_voltage(reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } indio_dev = iio_device_alloc(sizeof(*st)); -- cgit v1.2.3 From 678fb42e2b2c815c3475884017aac4da22124d0d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 24 Dec 2012 06:24:00 +0000 Subject: iio: (max1363) Fix probe error path Instantiating the driver with no available regulator results in: [39711.686393] i2c i2c-7: new_device: Instantiated device max1139 at 0x35 [39711.688687] BUG: unable to handle kernel paging request at fffffffffffffe13 [39711.688734] IP: [] regulator_disable+0x1b/0x80 [39711.688788] PGD 1c0e067 PUD 1c0f067 PMD 0 [39711.688835] Oops: 0000 [#1] SMP Caused by bad probe error path. Fix it. Driver should also not attempt to free the interrupt in its error path if none was allocated. Fix that problem as well. Finally, testing if the regulator was allocated is not necessary in the remove function, since the probe function bails out if this is the case. Remove that check. Signed-off-by: Guenter Roeck Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1363.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 1e84b5b55093..31f3485303d0 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1605,19 +1605,20 @@ static int __devinit max1363_probe(struct i2c_client *client, return 0; error_free_irq: - free_irq(st->client->irq, indio_dev); + if (client->irq) + free_irq(st->client->irq, indio_dev); error_uninit_buffer: iio_buffer_unregister(indio_dev); error_cleanup_buffer: max1363_buffer_cleanup(indio_dev); error_free_available_scan_masks: kfree(indio_dev->available_scan_masks); -error_unregister_map: - iio_map_array_unregister(indio_dev, client->dev.platform_data); error_disable_reg: regulator_disable(st->reg); error_put_reg: regulator_put(st->reg); +error_unregister_map: + iio_map_array_unregister(indio_dev, client->dev.platform_data); error_free_device: iio_device_free(indio_dev); error_out: @@ -1635,10 +1636,8 @@ static int __devexit max1363_remove(struct i2c_client *client) iio_buffer_unregister(indio_dev); max1363_buffer_cleanup(indio_dev); kfree(indio_dev->available_scan_masks); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } + regulator_disable(st->reg); + regulator_put(st->reg); iio_map_array_unregister(indio_dev, client->dev.platform_data); iio_device_free(indio_dev); -- cgit v1.2.3 From fc52692c49969ec72595766929b9f54ac402da34 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 21 Dec 2012 13:21:43 -0800 Subject: Drivers: iio: remove __dev* attributes. CONFIG_HOTPLUG is going away as an option. As a result, the __dev* markings need to be removed. This change removes the use of __devinit, __devexit_p, and __devexit from these drivers. Based on patches originally written by Bill Pemberton, but redone by me in order to handle some of the coding style issues better, by hand. Cc: Bill Pemberton Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/accel/hid-sensor-accel-3d.c | 4 ++-- drivers/iio/adc/ad7266.c | 8 ++++---- drivers/iio/adc/ad7298.c | 6 +++--- drivers/iio/adc/ad7476.c | 6 +++--- drivers/iio/adc/ad7791.c | 10 +++++----- drivers/iio/adc/ad7887.c | 6 +++--- drivers/iio/adc/at91_adc.c | 6 +++--- drivers/iio/adc/lp8788_adc.c | 6 +++--- drivers/iio/adc/max1363.c | 10 +++++----- drivers/iio/adc/ti_am335x_adc.c | 6 +++--- drivers/iio/adc/viperboard_adc.c | 6 +++--- drivers/iio/amplifiers/ad8366.c | 6 +++--- drivers/iio/dac/ad5064.c | 18 +++++++++--------- drivers/iio/dac/ad5360.c | 8 ++++---- drivers/iio/dac/ad5380.c | 22 +++++++++++----------- drivers/iio/dac/ad5421.c | 6 +++--- drivers/iio/dac/ad5446.c | 18 +++++++++--------- drivers/iio/dac/ad5449.c | 6 +++--- drivers/iio/dac/ad5504.c | 6 +++--- drivers/iio/dac/ad5624r_spi.c | 6 +++--- drivers/iio/dac/ad5686.c | 6 +++--- drivers/iio/dac/ad5755.c | 16 ++++++++-------- drivers/iio/dac/ad5764.c | 6 +++--- drivers/iio/dac/ad5791.c | 6 +++--- drivers/iio/dac/max517.c | 6 +++--- drivers/iio/dac/mcp4725.c | 8 ++++---- drivers/iio/frequency/ad9523.c | 6 +++--- drivers/iio/frequency/adf4350.c | 6 +++--- drivers/iio/gyro/hid-sensor-gyro-3d.c | 4 ++-- drivers/iio/light/adjd_s311.c | 8 ++++---- drivers/iio/light/hid-sensor-als.c | 4 ++-- drivers/iio/light/lm3533-als.c | 19 +++++++++---------- drivers/iio/light/vcnl4000.c | 8 ++++---- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 4 ++-- 34 files changed, 138 insertions(+), 139 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index e67bb912bd19..0b0c3c66f6c0 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -278,7 +278,7 @@ static int accel_3d_parse_report(struct platform_device *pdev, } /* Function to initialize the processing for usage id */ -static int __devinit hid_accel_3d_probe(struct platform_device *pdev) +static int hid_accel_3d_probe(struct platform_device *pdev) { int ret = 0; static const char *name = "accel_3d"; @@ -375,7 +375,7 @@ error_ret: } /* Function to deinitialize the processing for usage id */ -static int __devinit hid_accel_3d_remove(struct platform_device *pdev) +static int hid_accel_3d_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index a6f4fc5f8201..4a5f639bc684 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -367,7 +367,7 @@ static const struct ad7266_chan_info ad7266_chan_infos[] = { }, }; -static void __devinit ad7266_init_channels(struct iio_dev *indio_dev) +static void ad7266_init_channels(struct iio_dev *indio_dev) { struct ad7266_state *st = iio_priv(indio_dev); bool is_differential, is_signed; @@ -391,7 +391,7 @@ static const char * const ad7266_gpio_labels[] = { "AD0", "AD1", "AD2", }; -static int __devinit ad7266_probe(struct spi_device *spi) +static int ad7266_probe(struct spi_device *spi) { struct ad7266_platform_data *pdata = spi->dev.platform_data; struct iio_dev *indio_dev; @@ -494,7 +494,7 @@ error_put_reg: return ret; } -static int __devexit ad7266_remove(struct spi_device *spi) +static int ad7266_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad7266_state *st = iio_priv(indio_dev); @@ -525,7 +525,7 @@ static struct spi_driver ad7266_driver = { .owner = THIS_MODULE, }, .probe = ad7266_probe, - .remove = __devexit_p(ad7266_remove), + .remove = ad7266_remove, .id_table = ad7266_id, }; module_spi_driver(ad7266_driver); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 2364807a5d6c..b34d754994d5 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -292,7 +292,7 @@ static const struct iio_info ad7298_info = { .driver_module = THIS_MODULE, }; -static int __devinit ad7298_probe(struct spi_device *spi) +static int ad7298_probe(struct spi_device *spi) { struct ad7298_platform_data *pdata = spi->dev.platform_data; struct ad7298_state *st; @@ -370,7 +370,7 @@ error_free: return ret; } -static int __devexit ad7298_remove(struct spi_device *spi) +static int ad7298_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad7298_state *st = iio_priv(indio_dev); @@ -398,7 +398,7 @@ static struct spi_driver ad7298_driver = { .owner = THIS_MODULE, }, .probe = ad7298_probe, - .remove = __devexit_p(ad7298_remove), + .remove = ad7298_remove, .id_table = ad7298_id, }; module_spi_driver(ad7298_driver); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 330248bfebae..1491fa6debb2 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -207,7 +207,7 @@ static const struct iio_info ad7476_info = { .read_raw = &ad7476_read_raw, }; -static int __devinit ad7476_probe(struct spi_device *spi) +static int ad7476_probe(struct spi_device *spi) { struct ad7476_state *st; struct iio_dev *indio_dev; @@ -277,7 +277,7 @@ error_ret: return ret; } -static int __devexit ad7476_remove(struct spi_device *spi) +static int ad7476_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad7476_state *st = iio_priv(indio_dev); @@ -322,7 +322,7 @@ static struct spi_driver ad7476_driver = { .owner = THIS_MODULE, }, .probe = ad7476_probe, - .remove = __devexit_p(ad7476_remove), + .remove = ad7476_remove, .id_table = ad7476_id, }; module_spi_driver(ad7476_driver); diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index e93740843b2b..5e8d1da6887f 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -325,8 +325,8 @@ static const struct iio_info ad7791_no_filter_info = { .driver_module = THIS_MODULE, }; -static int __devinit ad7791_setup(struct ad7791_state *st, - struct ad7791_platform_data *pdata) +static int ad7791_setup(struct ad7791_state *st, + struct ad7791_platform_data *pdata) { /* Set to poweron-reset default values */ st->mode = AD7791_MODE_BUFFER; @@ -349,7 +349,7 @@ static int __devinit ad7791_setup(struct ad7791_state *st, st->mode); } -static int __devinit ad7791_probe(struct spi_device *spi) +static int ad7791_probe(struct spi_device *spi) { struct ad7791_platform_data *pdata = spi->dev.platform_data; struct iio_dev *indio_dev; @@ -418,7 +418,7 @@ err_iio_free: return ret; } -static int __devexit ad7791_remove(struct spi_device *spi) +static int ad7791_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad7791_state *st = iio_priv(indio_dev); @@ -450,7 +450,7 @@ static struct spi_driver ad7791_driver = { .owner = THIS_MODULE, }, .probe = ad7791_probe, - .remove = __devexit_p(ad7791_remove), + .remove = ad7791_remove, .id_table = ad7791_spi_ids, }; module_spi_driver(ad7791_driver); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 81153fafac7a..a33d5cd1a536 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -233,7 +233,7 @@ static const struct iio_info ad7887_info = { .driver_module = THIS_MODULE, }; -static int __devinit ad7887_probe(struct spi_device *spi) +static int ad7887_probe(struct spi_device *spi) { struct ad7887_platform_data *pdata = spi->dev.platform_data; struct ad7887_state *st; @@ -340,7 +340,7 @@ error_free: return ret; } -static int __devexit ad7887_remove(struct spi_device *spi) +static int ad7887_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad7887_state *st = iio_priv(indio_dev); @@ -368,7 +368,7 @@ static struct spi_driver ad7887_driver = { .owner = THIS_MODULE, }, .probe = ad7887_probe, - .remove = __devexit_p(ad7887_remove), + .remove = ad7887_remove, .id_table = ad7887_id, }; module_spi_driver(ad7887_driver); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 03b85940f4ba..04b013561f0f 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -514,7 +514,7 @@ static const struct iio_info at91_adc_info = { .read_raw = &at91_adc_read_raw, }; -static int __devinit at91_adc_probe(struct platform_device *pdev) +static int at91_adc_probe(struct platform_device *pdev) { unsigned int prsc, mstrclk, ticks, adc_clk; int ret; @@ -678,7 +678,7 @@ error_ret: return ret; } -static int __devexit at91_adc_remove(struct platform_device *pdev) +static int at91_adc_remove(struct platform_device *pdev) { struct iio_dev *idev = platform_get_drvdata(pdev); struct at91_adc_state *st = iio_priv(idev); @@ -702,7 +702,7 @@ MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); static struct platform_driver at91_adc_driver = { .probe = at91_adc_probe, - .remove = __devexit_p(at91_adc_remove), + .remove = at91_adc_remove, .driver = { .name = "at91_adc", .of_match_table = of_match_ptr(at91_adc_dt_ids), diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c index a93aaf0bb841..72955e45e9e0 100644 --- a/drivers/iio/adc/lp8788_adc.c +++ b/drivers/iio/adc/lp8788_adc.c @@ -193,7 +193,7 @@ static inline void lp8788_iio_map_unregister(struct iio_dev *indio_dev, iio_map_array_unregister(indio_dev, adc->map); } -static int __devinit lp8788_adc_probe(struct platform_device *pdev) +static int lp8788_adc_probe(struct platform_device *pdev) { struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); struct iio_dev *indio_dev; @@ -236,7 +236,7 @@ err_iio_map: return ret; } -static int __devexit lp8788_adc_remove(struct platform_device *pdev) +static int lp8788_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct lp8788_adc *adc = iio_priv(indio_dev); @@ -250,7 +250,7 @@ static int __devexit lp8788_adc_remove(struct platform_device *pdev) static struct platform_driver lp8788_adc_driver = { .probe = lp8788_adc_probe, - .remove = __devexit_p(lp8788_adc_remove), + .remove = lp8788_adc_remove, .driver = { .name = LP8788_DEV_ADC, .owner = THIS_MODULE, diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 1e84b5b55093..b5669be6f396 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1402,7 +1402,7 @@ static int max1363_initial_setup(struct max1363_state *st) return max1363_set_scan_mode(st); } -static int __devinit max1363_alloc_scan_masks(struct iio_dev *indio_dev) +static int max1363_alloc_scan_masks(struct iio_dev *indio_dev) { struct max1363_state *st = iio_priv(indio_dev); unsigned long *masks; @@ -1525,8 +1525,8 @@ static void max1363_buffer_cleanup(struct iio_dev *indio_dev) iio_kfifo_free(indio_dev->buffer); } -static int __devinit max1363_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max1363_probe(struct i2c_client *client, + const struct i2c_device_id *id) { int ret; struct max1363_state *st; @@ -1624,7 +1624,7 @@ error_out: return ret; } -static int __devexit max1363_remove(struct i2c_client *client) +static int max1363_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct max1363_state *st = iio_priv(indio_dev); @@ -1690,7 +1690,7 @@ static struct i2c_driver max1363_driver = { .name = "max1363", }, .probe = max1363_probe, - .remove = __devexit_p(max1363_remove), + .remove = max1363_remove, .id_table = max1363_id, }; module_i2c_driver(max1363_driver); diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 02a43c87a8a3..cd030e100c39 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -136,7 +136,7 @@ static const struct iio_info tiadc_info = { .read_raw = &tiadc_read_raw, }; -static int __devinit tiadc_probe(struct platform_device *pdev) +static int tiadc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct tiadc_device *adc_dev; @@ -188,7 +188,7 @@ err_ret: return err; } -static int __devexit tiadc_remove(struct platform_device *pdev) +static int tiadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -250,7 +250,7 @@ static struct platform_driver tiadc_driver = { .pm = TIADC_PM_OPS, }, .probe = tiadc_probe, - .remove = __devexit_p(tiadc_remove), + .remove = tiadc_remove, }; module_platform_driver(tiadc_driver); diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c index 10136a8b20d4..ad0261533dee 100644 --- a/drivers/iio/adc/viperboard_adc.c +++ b/drivers/iio/adc/viperboard_adc.c @@ -116,7 +116,7 @@ static const struct iio_info vprbrd_adc_iio_info = { .driver_module = THIS_MODULE, }; -static int __devinit vprbrd_adc_probe(struct platform_device *pdev) +static int vprbrd_adc_probe(struct platform_device *pdev) { struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); struct vprbrd_adc *adc; @@ -154,7 +154,7 @@ error: return ret; } -static int __devexit vprbrd_adc_remove(struct platform_device *pdev) +static int vprbrd_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -170,7 +170,7 @@ static struct platform_driver vprbrd_adc_driver = { .owner = THIS_MODULE, }, .probe = vprbrd_adc_probe, - .remove = __devexit_p(vprbrd_adc_remove), + .remove = vprbrd_adc_remove, }; module_platform_driver(vprbrd_adc_driver); diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index d8281cdbfc4a..d6c0af23a2a7 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -133,7 +133,7 @@ static const struct iio_chan_spec ad8366_channels[] = { AD8366_CHAN(1), }; -static int __devinit ad8366_probe(struct spi_device *spi) +static int ad8366_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct ad8366_state *st; @@ -182,7 +182,7 @@ error_put_reg: return ret; } -static int __devexit ad8366_remove(struct spi_device *spi) +static int ad8366_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad8366_state *st = iio_priv(indio_dev); @@ -211,7 +211,7 @@ static struct spi_driver ad8366_driver = { .owner = THIS_MODULE, }, .probe = ad8366_probe, - .remove = __devexit_p(ad8366_remove), + .remove = ad8366_remove, .id_table = ad8366_id, }; diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index eb281a2c295b..2fe1d4edcb2f 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -424,8 +424,8 @@ static const char * const ad5064_vref_name(struct ad5064_state *st, return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref]; } -static int __devinit ad5064_probe(struct device *dev, enum ad5064_type type, - const char *name, ad5064_write_func write) +static int ad5064_probe(struct device *dev, enum ad5064_type type, + const char *name, ad5064_write_func write) { struct iio_dev *indio_dev; struct ad5064_state *st; @@ -495,7 +495,7 @@ error_free: return ret; } -static int __devexit ad5064_remove(struct device *dev) +static int ad5064_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5064_state *st = iio_priv(indio_dev); @@ -523,7 +523,7 @@ static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd, return spi_write(spi, &st->data.spi, sizeof(st->data.spi)); } -static int __devinit ad5064_spi_probe(struct spi_device *spi) +static int ad5064_spi_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); @@ -531,7 +531,7 @@ static int __devinit ad5064_spi_probe(struct spi_device *spi) ad5064_spi_write); } -static int __devexit ad5064_spi_remove(struct spi_device *spi) +static int ad5064_spi_remove(struct spi_device *spi) { return ad5064_remove(&spi->dev); } @@ -563,7 +563,7 @@ static struct spi_driver ad5064_spi_driver = { .owner = THIS_MODULE, }, .probe = ad5064_spi_probe, - .remove = __devexit_p(ad5064_spi_remove), + .remove = ad5064_spi_remove, .id_table = ad5064_spi_ids, }; @@ -596,14 +596,14 @@ static int ad5064_i2c_write(struct ad5064_state *st, unsigned int cmd, return i2c_master_send(i2c, st->data.i2c, 3); } -static int __devinit ad5064_i2c_probe(struct i2c_client *i2c, +static int ad5064_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { return ad5064_probe(&i2c->dev, id->driver_data, id->name, ad5064_i2c_write); } -static int __devexit ad5064_i2c_remove(struct i2c_client *i2c) +static int ad5064_i2c_remove(struct i2c_client *i2c) { return ad5064_remove(&i2c->dev); } @@ -625,7 +625,7 @@ static struct i2c_driver ad5064_i2c_driver = { .owner = THIS_MODULE, }, .probe = ad5064_i2c_probe, - .remove = __devexit_p(ad5064_i2c_remove), + .remove = ad5064_i2c_remove, .id_table = ad5064_i2c_ids, }; diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index 8fce84fe70b1..54b46fd3aede 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -433,7 +433,7 @@ static const char * const ad5360_vref_name[] = { "vref0", "vref1", "vref2" }; -static int __devinit ad5360_alloc_channels(struct iio_dev *indio_dev) +static int ad5360_alloc_channels(struct iio_dev *indio_dev) { struct ad5360_state *st = iio_priv(indio_dev); struct iio_chan_spec *channels; @@ -456,7 +456,7 @@ static int __devinit ad5360_alloc_channels(struct iio_dev *indio_dev) return 0; } -static int __devinit ad5360_probe(struct spi_device *spi) +static int ad5360_probe(struct spi_device *spi) { enum ad5360_type type = spi_get_device_id(spi)->driver_data; struct iio_dev *indio_dev; @@ -524,7 +524,7 @@ error_free: return ret; } -static int __devexit ad5360_remove(struct spi_device *spi) +static int ad5360_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad5360_state *st = iio_priv(indio_dev); @@ -560,7 +560,7 @@ static struct spi_driver ad5360_driver = { .owner = THIS_MODULE, }, .probe = ad5360_probe, - .remove = __devexit_p(ad5360_remove), + .remove = ad5360_remove, .id_table = ad5360_ids, }; module_spi_driver(ad5360_driver); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 14991ac55f26..6c7898c765d9 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -338,7 +338,7 @@ static const struct ad5380_chip_info ad5380_chip_info_tbl[] = { }, }; -static int __devinit ad5380_alloc_channels(struct iio_dev *indio_dev) +static int ad5380_alloc_channels(struct iio_dev *indio_dev) { struct ad5380_state *st = iio_priv(indio_dev); struct iio_chan_spec *channels; @@ -361,8 +361,8 @@ static int __devinit ad5380_alloc_channels(struct iio_dev *indio_dev) return 0; } -static int __devinit ad5380_probe(struct device *dev, struct regmap *regmap, - enum ad5380_type type, const char *name) +static int ad5380_probe(struct device *dev, struct regmap *regmap, + enum ad5380_type type, const char *name) { struct iio_dev *indio_dev; struct ad5380_state *st; @@ -441,7 +441,7 @@ error_out: return ret; } -static int __devexit ad5380_remove(struct device *dev) +static int ad5380_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5380_state *st = iio_priv(indio_dev); @@ -478,7 +478,7 @@ static const struct regmap_config ad5380_regmap_config = { #if IS_ENABLED(CONFIG_SPI_MASTER) -static int __devinit ad5380_spi_probe(struct spi_device *spi) +static int ad5380_spi_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); struct regmap *regmap; @@ -491,7 +491,7 @@ static int __devinit ad5380_spi_probe(struct spi_device *spi) return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name); } -static int __devexit ad5380_spi_remove(struct spi_device *spi) +static int ad5380_spi_remove(struct spi_device *spi) { return ad5380_remove(&spi->dev); } @@ -523,7 +523,7 @@ static struct spi_driver ad5380_spi_driver = { .owner = THIS_MODULE, }, .probe = ad5380_spi_probe, - .remove = __devexit_p(ad5380_spi_remove), + .remove = ad5380_spi_remove, .id_table = ad5380_spi_ids, }; @@ -552,8 +552,8 @@ static inline void ad5380_spi_unregister_driver(void) #if IS_ENABLED(CONFIG_I2C) -static int __devinit ad5380_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ad5380_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct regmap *regmap; @@ -565,7 +565,7 @@ static int __devinit ad5380_i2c_probe(struct i2c_client *i2c, return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name); } -static int __devexit ad5380_i2c_remove(struct i2c_client *i2c) +static int ad5380_i2c_remove(struct i2c_client *i2c) { return ad5380_remove(&i2c->dev); } @@ -597,7 +597,7 @@ static struct i2c_driver ad5380_i2c_driver = { .owner = THIS_MODULE, }, .probe = ad5380_i2c_probe, - .remove = __devexit_p(ad5380_i2c_remove), + .remove = ad5380_i2c_remove, .id_table = ad5380_i2c_ids, }; diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c index cdbc5bf25c31..43be948db83e 100644 --- a/drivers/iio/dac/ad5421.c +++ b/drivers/iio/dac/ad5421.c @@ -449,7 +449,7 @@ static const struct iio_info ad5421_info = { .driver_module = THIS_MODULE, }; -static int __devinit ad5421_probe(struct spi_device *spi) +static int ad5421_probe(struct spi_device *spi) { struct ad5421_platform_data *pdata = dev_get_platdata(&spi->dev); struct iio_dev *indio_dev; @@ -516,7 +516,7 @@ error_free: return ret; } -static int __devexit ad5421_remove(struct spi_device *spi) +static int ad5421_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); @@ -534,7 +534,7 @@ static struct spi_driver ad5421_driver = { .owner = THIS_MODULE, }, .probe = ad5421_probe, - .remove = __devexit_p(ad5421_remove), + .remove = ad5421_remove, }; module_spi_driver(ad5421_driver); diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 3310cbbd41e7..29f653dab2f7 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -212,8 +212,8 @@ static const struct iio_info ad5446_info = { .driver_module = THIS_MODULE, }; -static int __devinit ad5446_probe(struct device *dev, const char *name, - const struct ad5446_chip_info *chip_info) +static int ad5446_probe(struct device *dev, const char *name, + const struct ad5446_chip_info *chip_info) { struct ad5446_state *st; struct iio_dev *indio_dev; @@ -461,7 +461,7 @@ static const struct spi_device_id ad5446_spi_ids[] = { }; MODULE_DEVICE_TABLE(spi, ad5446_spi_ids); -static int __devinit ad5446_spi_probe(struct spi_device *spi) +static int ad5446_spi_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); @@ -469,7 +469,7 @@ static int __devinit ad5446_spi_probe(struct spi_device *spi) &ad5446_spi_chip_info[id->driver_data]); } -static int __devexit ad5446_spi_remove(struct spi_device *spi) +static int ad5446_spi_remove(struct spi_device *spi) { return ad5446_remove(&spi->dev); } @@ -480,7 +480,7 @@ static struct spi_driver ad5446_spi_driver = { .owner = THIS_MODULE, }, .probe = ad5446_spi_probe, - .remove = __devexit_p(ad5446_spi_remove), + .remove = ad5446_spi_remove, .id_table = ad5446_spi_ids, }; @@ -539,14 +539,14 @@ static const struct ad5446_chip_info ad5446_i2c_chip_info[] = { }, }; -static int __devinit ad5446_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ad5446_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { return ad5446_probe(&i2c->dev, id->name, &ad5446_i2c_chip_info[id->driver_data]); } -static int __devexit ad5446_i2c_remove(struct i2c_client *i2c) +static int ad5446_i2c_remove(struct i2c_client *i2c) { return ad5446_remove(&i2c->dev); } @@ -568,7 +568,7 @@ static struct i2c_driver ad5446_i2c_driver = { .owner = THIS_MODULE, }, .probe = ad5446_i2c_probe, - .remove = __devexit_p(ad5446_i2c_remove), + .remove = ad5446_i2c_remove, .id_table = ad5446_i2c_ids, }; diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index 0ee6f8eeba8d..c4731b7b577b 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -266,7 +266,7 @@ static const char *ad5449_vref_name(struct ad5449 *st, int n) return "VREFB"; } -static int __devinit ad5449_spi_probe(struct spi_device *spi) +static int ad5449_spi_probe(struct spi_device *spi) { struct ad5449_platform_data *pdata = spi->dev.platform_data; const struct spi_device_id *id = spi_get_device_id(spi); @@ -333,7 +333,7 @@ error_free: return ret; } -static int __devexit ad5449_spi_remove(struct spi_device *spi) +static int ad5449_spi_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad5449 *st = iio_priv(indio_dev); @@ -366,7 +366,7 @@ static struct spi_driver ad5449_spi_driver = { .owner = THIS_MODULE, }, .probe = ad5449_spi_probe, - .remove = __devexit_p(ad5449_spi_remove), + .remove = ad5449_spi_remove, .id_table = ad5449_spi_ids, }; module_spi_driver(ad5449_spi_driver); diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 242bdc7d0044..b2a31a0468ed 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -277,7 +277,7 @@ static const struct iio_chan_spec ad5504_channels[] = { AD5504_CHANNEL(3), }; -static int __devinit ad5504_probe(struct spi_device *spi) +static int ad5504_probe(struct spi_device *spi) { struct ad5504_platform_data *pdata = spi->dev.platform_data; struct iio_dev *indio_dev; @@ -352,7 +352,7 @@ error_ret: return ret; } -static int __devexit ad5504_remove(struct spi_device *spi) +static int ad5504_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad5504_state *st = iio_priv(indio_dev); @@ -383,7 +383,7 @@ static struct spi_driver ad5504_driver = { .owner = THIS_MODULE, }, .probe = ad5504_probe, - .remove = __devexit_p(ad5504_remove), + .remove = ad5504_remove, .id_table = ad5504_id, }; module_spi_driver(ad5504_driver); diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 6a7d6a48cc6d..e9947969f9fe 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -220,7 +220,7 @@ static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = { }, }; -static int __devinit ad5624r_probe(struct spi_device *spi) +static int ad5624r_probe(struct spi_device *spi) { struct ad5624r_state *st; struct iio_dev *indio_dev; @@ -282,7 +282,7 @@ error_ret: return ret; } -static int __devexit ad5624r_remove(struct spi_device *spi) +static int ad5624r_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad5624r_state *st = iio_priv(indio_dev); @@ -314,7 +314,7 @@ static struct spi_driver ad5624r_driver = { .owner = THIS_MODULE, }, .probe = ad5624r_probe, - .remove = __devexit_p(ad5624r_remove), + .remove = ad5624r_remove, .id_table = ad5624r_id, }; module_spi_driver(ad5624r_driver); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index bc92ff9309c2..36e51382ae52 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -313,7 +313,7 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { }; -static int __devinit ad5686_probe(struct spi_device *spi) +static int ad5686_probe(struct spi_device *spi) { struct ad5686_state *st; struct iio_dev *indio_dev; @@ -379,7 +379,7 @@ error_put_reg: return ret; } -static int __devexit ad5686_remove(struct spi_device *spi) +static int ad5686_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad5686_state *st = iio_priv(indio_dev); @@ -408,7 +408,7 @@ static struct spi_driver ad5686_driver = { .owner = THIS_MODULE, }, .probe = ad5686_probe, - .remove = __devexit_p(ad5686_remove), + .remove = ad5686_remove, .id_table = ad5686_id, }; module_spi_driver(ad5686_driver); diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index 5db3506034c5..0869bbd27d30 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -447,8 +447,8 @@ static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode) } } -static int __devinit ad5755_setup_pdata(struct iio_dev *indio_dev, - const struct ad5755_platform_data *pdata) +static int ad5755_setup_pdata(struct iio_dev *indio_dev, + const struct ad5755_platform_data *pdata) { struct ad5755_state *st = iio_priv(indio_dev); unsigned int val; @@ -503,7 +503,7 @@ static int __devinit ad5755_setup_pdata(struct iio_dev *indio_dev, return 0; } -static bool __devinit ad5755_is_voltage_mode(enum ad5755_mode mode) +static bool ad5755_is_voltage_mode(enum ad5755_mode mode) { switch (mode) { case AD5755_MODE_VOLTAGE_0V_5V: @@ -516,8 +516,8 @@ static bool __devinit ad5755_is_voltage_mode(enum ad5755_mode mode) } } -static int __devinit ad5755_init_channels(struct iio_dev *indio_dev, - const struct ad5755_platform_data *pdata) +static int ad5755_init_channels(struct iio_dev *indio_dev, + const struct ad5755_platform_data *pdata) { struct ad5755_state *st = iio_priv(indio_dev); struct iio_chan_spec *channels = st->channels; @@ -562,7 +562,7 @@ static const struct ad5755_platform_data ad5755_default_pdata = { }, }; -static int __devinit ad5755_probe(struct spi_device *spi) +static int ad5755_probe(struct spi_device *spi) { enum ad5755_type type = spi_get_device_id(spi)->driver_data; const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev); @@ -614,7 +614,7 @@ error_free: return ret; } -static int __devexit ad5755_remove(struct spi_device *spi) +static int ad5755_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); @@ -640,7 +640,7 @@ static struct spi_driver ad5755_driver = { .owner = THIS_MODULE, }, .probe = ad5755_probe, - .remove = __devexit_p(ad5755_remove), + .remove = ad5755_remove, .id_table = ad5755_id, }; module_spi_driver(ad5755_driver); diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c index ffce30447445..7f9045e6daa4 100644 --- a/drivers/iio/dac/ad5764.c +++ b/drivers/iio/dac/ad5764.c @@ -273,7 +273,7 @@ static const struct iio_info ad5764_info = { .driver_module = THIS_MODULE, }; -static int __devinit ad5764_probe(struct spi_device *spi) +static int ad5764_probe(struct spi_device *spi) { enum ad5764_type type = spi_get_device_id(spi)->driver_data; struct iio_dev *indio_dev; @@ -340,7 +340,7 @@ error_free: return ret; } -static int __devexit ad5764_remove(struct spi_device *spi) +static int ad5764_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad5764_state *st = iio_priv(indio_dev); @@ -372,7 +372,7 @@ static struct spi_driver ad5764_driver = { .owner = THIS_MODULE, }, .probe = ad5764_probe, - .remove = __devexit_p(ad5764_remove), + .remove = ad5764_remove, .id_table = ad5764_ids, }; module_spi_driver(ad5764_driver); diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index 2bd2e37280ff..c84180f23139 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -346,7 +346,7 @@ static const struct iio_info ad5791_info = { .driver_module = THIS_MODULE, }; -static int __devinit ad5791_probe(struct spi_device *spi) +static int ad5791_probe(struct spi_device *spi) { struct ad5791_platform_data *pdata = spi->dev.platform_data; struct iio_dev *indio_dev; @@ -439,7 +439,7 @@ error_ret: return ret; } -static int __devexit ad5791_remove(struct spi_device *spi) +static int ad5791_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad5791_state *st = iio_priv(indio_dev); @@ -475,7 +475,7 @@ static struct spi_driver ad5791_driver = { .owner = THIS_MODULE, }, .probe = ad5791_probe, - .remove = __devexit_p(ad5791_remove), + .remove = ad5791_remove, .id_table = ad5791_id, }; module_spi_driver(ad5791_driver); diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index c3d748c25939..352abe2004a4 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -156,7 +156,7 @@ static const struct iio_chan_spec max517_channels[] = { MAX517_CHANNEL(1) }; -static int __devinit max517_probe(struct i2c_client *client, +static int max517_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max517_data *data; @@ -210,7 +210,7 @@ exit: return err; } -static int __devexit max517_remove(struct i2c_client *client) +static int max517_remove(struct i2c_client *client) { iio_device_unregister(i2c_get_clientdata(client)); iio_device_free(i2c_get_clientdata(client)); @@ -232,7 +232,7 @@ static struct i2c_driver max517_driver = { .pm = MAX517_PM_OPS, }, .probe = max517_probe, - .remove = __devexit_p(max517_remove), + .remove = max517_remove, .id_table = max517_id, }; module_i2c_driver(max517_driver); diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index e0e168bd5b45..8f88cc4059a2 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -141,8 +141,8 @@ static const struct iio_info mcp4725_info = { .driver_module = THIS_MODULE, }; -static int __devinit mcp4725_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mcp4725_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct mcp4725_data *data; struct iio_dev *indio_dev; @@ -195,7 +195,7 @@ exit: return err; } -static int __devexit mcp4725_remove(struct i2c_client *client) +static int mcp4725_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); @@ -217,7 +217,7 @@ static struct i2c_driver mcp4725_driver = { .pm = MCP4725_PM_OPS, }, .probe = mcp4725_probe, - .remove = __devexit_p(mcp4725_remove), + .remove = mcp4725_remove, .id_table = mcp4725_id, }; module_i2c_driver(mcp4725_driver); diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index b737c64a402d..80307473e3a9 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -959,7 +959,7 @@ static int ad9523_setup(struct iio_dev *indio_dev) return 0; } -static int __devinit ad9523_probe(struct spi_device *spi) +static int ad9523_probe(struct spi_device *spi) { struct ad9523_platform_data *pdata = spi->dev.platform_data; struct iio_dev *indio_dev; @@ -1020,7 +1020,7 @@ error_put_reg: return ret; } -static int __devexit ad9523_remove(struct spi_device *spi) +static int ad9523_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad9523_state *st = iio_priv(indio_dev); @@ -1049,7 +1049,7 @@ static struct spi_driver ad9523_driver = { .owner = THIS_MODULE, }, .probe = ad9523_probe, - .remove = __devexit_p(ad9523_remove), + .remove = ad9523_remove, .id_table = ad9523_id, }; module_spi_driver(ad9523_driver); diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index e35bb8f6fe75..e5033b4cfba0 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -355,7 +355,7 @@ static const struct iio_info adf4350_info = { .driver_module = THIS_MODULE, }; -static int __devinit adf4350_probe(struct spi_device *spi) +static int adf4350_probe(struct spi_device *spi) { struct adf4350_platform_data *pdata = spi->dev.platform_data; struct iio_dev *indio_dev; @@ -440,7 +440,7 @@ error_put_reg: return ret; } -static int __devexit adf4350_remove(struct spi_device *spi) +static int adf4350_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct adf4350_state *st = iio_priv(indio_dev); @@ -476,7 +476,7 @@ static struct spi_driver adf4350_driver = { .owner = THIS_MODULE, }, .probe = adf4350_probe, - .remove = __devexit_p(adf4350_remove), + .remove = adf4350_remove, .id_table = adf4350_id, }; module_spi_driver(adf4350_driver); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 4c8b158e40e1..06e7cc35450c 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -278,7 +278,7 @@ static int gyro_3d_parse_report(struct platform_device *pdev, } /* Function to initialize the processing for usage id */ -static int __devinit hid_gyro_3d_probe(struct platform_device *pdev) +static int hid_gyro_3d_probe(struct platform_device *pdev) { int ret = 0; static const char *name = "gyro_3d"; @@ -375,7 +375,7 @@ error_ret: } /* Function to deinitialize the processing for usage id */ -static int __devinit hid_gyro_3d_remove(struct platform_device *pdev) +static int hid_gyro_3d_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 36d210a06b28..d5b9d39d95b2 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -286,8 +286,8 @@ static const struct iio_info adjd_s311_info = { .driver_module = THIS_MODULE, }; -static int __devinit adjd_s311_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adjd_s311_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct adjd_s311_data *data; struct iio_dev *indio_dev; @@ -330,7 +330,7 @@ exit: return err; } -static int __devexit adjd_s311_remove(struct i2c_client *client) +static int adjd_s311_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct adjd_s311_data *data = iio_priv(indio_dev); @@ -354,7 +354,7 @@ static struct i2c_driver adjd_s311_driver = { .name = ADJD_S311_DRV_NAME, }, .probe = adjd_s311_probe, - .remove = __devexit_p(adjd_s311_remove), + .remove = adjd_s311_remove, .id_table = adjd_s311_id, }; module_i2c_driver(adjd_s311_driver); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 23eeeef64e84..e2d042f2a544 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -245,7 +245,7 @@ static int als_parse_report(struct platform_device *pdev, } /* Function to initialize the processing for usage id */ -static int __devinit hid_als_probe(struct platform_device *pdev) +static int hid_als_probe(struct platform_device *pdev) { int ret = 0; static const char *name = "als"; @@ -341,7 +341,7 @@ error_ret: } /* Function to deinitialize the processing for usage id */ -static int __devinit hid_als_remove(struct platform_device *pdev) +static int hid_als_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index e45712a921ce..7503012ce933 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -718,8 +718,7 @@ static struct attribute_group lm3533_als_attribute_group = { .attrs = lm3533_als_attributes }; -static int __devinit lm3533_als_set_input_mode(struct lm3533_als *als, - bool pwm_mode) +static int lm3533_als_set_input_mode(struct lm3533_als *als, bool pwm_mode) { u8 mask = LM3533_ALS_INPUT_MODE_MASK; u8 val; @@ -740,7 +739,7 @@ static int __devinit lm3533_als_set_input_mode(struct lm3533_als *als, return 0; } -static int __devinit lm3533_als_set_resistor(struct lm3533_als *als, u8 val) +static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val) { int ret; @@ -756,8 +755,8 @@ static int __devinit lm3533_als_set_resistor(struct lm3533_als *als, u8 val) return 0; } -static int __devinit lm3533_als_setup(struct lm3533_als *als, - struct lm3533_als_platform_data *pdata) +static int lm3533_als_setup(struct lm3533_als *als, + struct lm3533_als_platform_data *pdata) { int ret; @@ -775,7 +774,7 @@ static int __devinit lm3533_als_setup(struct lm3533_als *als, return 0; } -static int __devinit lm3533_als_setup_irq(struct lm3533_als *als, void *dev) +static int lm3533_als_setup_irq(struct lm3533_als *als, void *dev) { u8 mask = LM3533_ALS_INT_ENABLE_MASK; int ret; @@ -799,7 +798,7 @@ static int __devinit lm3533_als_setup_irq(struct lm3533_als *als, void *dev) return 0; } -static int __devinit lm3533_als_enable(struct lm3533_als *als) +static int lm3533_als_enable(struct lm3533_als *als) { u8 mask = LM3533_ALS_ENABLE_MASK; int ret; @@ -830,7 +829,7 @@ static const struct iio_info lm3533_als_info = { .read_raw = &lm3533_als_read_raw, }; -static int __devinit lm3533_als_probe(struct platform_device *pdev) +static int lm3533_als_probe(struct platform_device *pdev) { struct lm3533 *lm3533; struct lm3533_als_platform_data *pdata; @@ -901,7 +900,7 @@ err_free_dev: return ret; } -static int __devexit lm3533_als_remove(struct platform_device *pdev) +static int lm3533_als_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct lm3533_als *als = iio_priv(indio_dev); @@ -922,7 +921,7 @@ static struct platform_driver lm3533_als_driver = { .owner = THIS_MODULE, }, .probe = lm3533_als_probe, - .remove = __devexit_p(lm3533_als_remove), + .remove = lm3533_als_remove, }; module_platform_driver(lm3533_als_driver); diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index e49cb9784a6f..2aa748fbdc0e 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -150,8 +150,8 @@ static const struct iio_info vcnl4000_info = { .driver_module = THIS_MODULE, }; -static int __devinit vcnl4000_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int vcnl4000_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct vcnl4000_data *data; struct iio_dev *indio_dev; @@ -190,7 +190,7 @@ error_free_dev: return ret; } -static int __devexit vcnl4000_remove(struct i2c_client *client) +static int vcnl4000_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); @@ -206,7 +206,7 @@ static struct i2c_driver vcnl4000_driver = { .owner = THIS_MODULE, }, .probe = vcnl4000_probe, - .remove = __devexit_p(vcnl4000_remove), + .remove = vcnl4000_remove, .id_table = vcnl4000_id, }; diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index 8e75eb76ccd9..7ac2c7483ba8 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -279,7 +279,7 @@ static int magn_3d_parse_report(struct platform_device *pdev, } /* Function to initialize the processing for usage id */ -static int __devinit hid_magn_3d_probe(struct platform_device *pdev) +static int hid_magn_3d_probe(struct platform_device *pdev) { int ret = 0; static char *name = "magn_3d"; @@ -376,7 +376,7 @@ error_ret: } /* Function to deinitialize the processing for usage id */ -static int __devinit hid_magn_3d_remove(struct platform_device *pdev) +static int hid_magn_3d_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); -- cgit v1.2.3