diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/iio/adc/Kconfig | 15 | ||||
-rw-r--r-- | drivers/iio/adc/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/adc/ad7476.c (renamed from drivers/staging/iio/adc/ad7476_core.c) | 208 | ||||
-rw-r--r-- | drivers/iio/dac/Kconfig | 11 | ||||
-rw-r--r-- | drivers/iio/dac/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/dac/ad5755.c | 650 | ||||
-rw-r--r-- | drivers/iio/industrialio-core.c | 6 | ||||
-rw-r--r-- | drivers/iio/inkern.c | 131 | ||||
-rw-r--r-- | drivers/staging/iio/Documentation/inkernel.txt | 6 | ||||
-rw-r--r-- | drivers/staging/iio/adc/Kconfig | 14 | ||||
-rw-r--r-- | drivers/staging/iio/adc/Makefile | 4 | ||||
-rw-r--r-- | drivers/staging/iio/adc/ad7476.h | 66 | ||||
-rw-r--r-- | drivers/staging/iio/adc/ad7476_ring.c | 64 | ||||
-rw-r--r-- | drivers/staging/iio/iio_hwmon.c | 33 | ||||
-rw-r--r-- | drivers/staging/iio/trigger/iio-trig-bfin-timer.c | 109 | ||||
-rw-r--r-- | drivers/staging/iio/trigger/iio-trig-bfin-timer.h | 24 |
16 files changed, 1072 insertions, 271 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index d0ae71ec2aa0..03791a6de349 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -30,6 +30,21 @@ config AD7791 To compile this driver as a module, choose M here: the module will be called ad7791. +config AD7476 + tristate "Analog Devices AD7476 and similar 1-channel ADCs driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7273, AD7274, AD7276, + AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, + AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC). + + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7476. + config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index f187ff6c2a16..9824a70f4fd8 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -4,5 +4,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_AT91_ADC) += at91_adc.o diff --git a/drivers/staging/iio/adc/ad7476_core.c b/drivers/iio/adc/ad7476.c index 4f6d59e89fcd..7f2f45a0a48d 100644 --- a/drivers/staging/iio/adc/ad7476_core.c +++ b/drivers/iio/adc/ad7476.c @@ -18,8 +18,76 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> -#include "ad7476.h" +#define RES_MASK(bits) ((1 << (bits)) - 1) + +struct ad7476_state; + +struct ad7476_chip_info { + unsigned int int_vref_uv; + struct iio_chan_spec channel[2]; + void (*reset)(struct ad7476_state *); +}; + +struct ad7476_state { + struct spi_device *spi; + const struct ad7476_chip_info *chip_info; + struct regulator *reg; + struct spi_transfer xfer; + struct spi_message msg; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * Make the buffer large enough for one 16 bit sample and one 64 bit + * aligned 64 bit timestamp. + */ + unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)] + ____cacheline_aligned; +}; + +enum ad7476_supported_device_ids { + ID_AD7091R, + ID_AD7276, + ID_AD7277, + ID_AD7278, + ID_AD7466, + ID_AD7467, + ID_AD7468, + ID_AD7495, + ID_AD7940, +}; + +static irqreturn_t ad7476_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7476_state *st = iio_priv(indio_dev); + s64 time_ns; + int b_sent; + + b_sent = spi_sync(st->spi, &st->msg); + if (b_sent < 0) + goto done; + + time_ns = iio_get_time_ns(); + + if (indio_dev->scan_timestamp) + ((s64 *)st->data)[1] = time_ns; + + iio_push_to_buffer(indio_dev->buffer, st->data); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void ad7091_reset(struct ad7476_state *st) +{ + /* Any transfers with 8 scl cycles will reset the device */ + spi_read(st->spi, st->data, 1); +} static int ad7476_scan_direct(struct ad7476_state *st) { @@ -29,7 +97,7 @@ static int ad7476_scan_direct(struct ad7476_state *st) if (ret) return ret; - return (st->data[0] << 8) | st->data[1]; + return be16_to_cpup((__be16 *)st->data); } static int ad7476_read_raw(struct iio_dev *indio_dev, @@ -40,7 +108,7 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, { int ret; struct ad7476_state *st = iio_priv(indio_dev); - unsigned int scale_uv; + int scale_uv; switch (m) { case IIO_CHAN_INFO_RAW: @@ -57,62 +125,80 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, RES_MASK(st->chip_info->channel[0].scan_type.realbits); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - scale_uv = (st->int_vref_mv * 1000) - >> st->chip_info->channel[0].scan_type.realbits; - *val = scale_uv/1000; - *val2 = (scale_uv%1000)*1000; + if (!st->chip_info->int_vref_uv) { + scale_uv = regulator_get_voltage(st->reg); + if (scale_uv < 0) + return scale_uv; + } else { + scale_uv = st->chip_info->int_vref_uv; + } + scale_uv >>= chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; return IIO_VAL_INT_PLUS_MICRO; } return -EINVAL; } -#define AD7476_CHAN(bits) \ +#define _AD7476_CHAN(bits, _shift, _info_mask) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + .info_mask = _info_mask | \ IIO_CHAN_INFO_SCALE_SHARED_BIT, \ .scan_type = { \ .sign = 'u', \ - .realbits = bits, \ + .realbits = (bits), \ .storagebits = 16, \ - .shift = 12 - bits, \ + .shift = (_shift), \ + .endianness = IIO_BE, \ }, \ } +#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \ + IIO_CHAN_INFO_RAW_SEPARATE_BIT) +#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \ + IIO_CHAN_INFO_RAW_SEPARATE_BIT) +#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0) + static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { - [ID_AD7466] = { - .channel[0] = AD7476_CHAN(12), + [ID_AD7091R] = { + .channel[0] = AD7091R_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .reset = ad7091_reset, }, - [ID_AD7467] = { - .channel[0] = AD7476_CHAN(10), + [ID_AD7276] = { + .channel[0] = AD7940_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, - [ID_AD7468] = { - .channel[0] = AD7476_CHAN(8), + [ID_AD7277] = { + .channel[0] = AD7940_CHAN(10), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, - [ID_AD7475] = { - .channel[0] = AD7476_CHAN(12), + [ID_AD7278] = { + .channel[0] = AD7940_CHAN(8), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, - [ID_AD7476] = { + [ID_AD7466] = { .channel[0] = AD7476_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, - [ID_AD7477] = { + [ID_AD7467] = { .channel[0] = AD7476_CHAN(10), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, - [ID_AD7478] = { + [ID_AD7468] = { .channel[0] = AD7476_CHAN(8), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, [ID_AD7495] = { .channel[0] = AD7476_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), - .int_vref_mv = 2500, + .int_vref_uv = 2500000, + }, + [ID_AD7940] = { + .channel[0] = AD7940_CHAN(14), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, }; @@ -123,10 +209,9 @@ static const struct iio_info ad7476_info = { static int __devinit ad7476_probe(struct spi_device *spi) { - struct ad7476_platform_data *pdata = spi->dev.platform_data; struct ad7476_state *st; struct iio_dev *indio_dev; - int ret, voltage_uv = 0; + int ret; indio_dev = iio_device_alloc(sizeof(*st)); if (indio_dev == NULL) { @@ -134,25 +219,18 @@ static int __devinit ad7476_probe(struct spi_device *spi) goto error_ret; } st = iio_priv(indio_dev); - st->reg = regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - goto error_put_reg; - - voltage_uv = regulator_get_voltage(st->reg); - } st->chip_info = &ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - if (st->chip_info->int_vref_mv) - st->int_vref_mv = st->chip_info->int_vref_mv; - else if (pdata && pdata->vref_mv) - st->int_vref_mv = pdata->vref_mv; - else if (voltage_uv) - st->int_vref_mv = voltage_uv / 1000; - else - dev_warn(&spi->dev, "reference voltage unspecified\n"); + st->reg = regulator_get(&spi->dev, "vcc"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto error_free_dev; + } + + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; spi_set_drvdata(spi, indio_dev); @@ -173,23 +251,26 @@ static int __devinit ad7476_probe(struct spi_device *spi) spi_message_init(&st->msg); spi_message_add_tail(&st->xfer, &st->msg); - ret = ad7476_register_ring_funcs_and_init(indio_dev); + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &ad7476_trigger_handler, NULL); if (ret) goto error_disable_reg; + if (st->chip_info->reset) + st->chip_info->reset(st); + ret = iio_device_register(indio_dev); if (ret) goto error_ring_unregister; return 0; error_ring_unregister: - ad7476_ring_cleanup(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); + regulator_disable(st->reg); error_put_reg: - if (!IS_ERR(st->reg)) - regulator_put(st->reg); + regulator_put(st->reg); +error_free_dev: iio_device_free(indio_dev); error_ret: @@ -202,28 +283,35 @@ static int __devexit ad7476_remove(struct spi_device *spi) struct ad7476_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); - ad7476_ring_cleanup(indio_dev); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(st->reg); + regulator_put(st->reg); iio_device_free(indio_dev); return 0; } static const struct spi_device_id ad7476_id[] = { + {"ad7091r", ID_AD7091R}, + {"ad7273", ID_AD7277}, + {"ad7274", ID_AD7276}, + {"ad7276", ID_AD7276}, + {"ad7277", ID_AD7277}, + {"ad7278", ID_AD7278}, {"ad7466", ID_AD7466}, {"ad7467", ID_AD7467}, {"ad7468", ID_AD7468}, - {"ad7475", ID_AD7475}, - {"ad7476", ID_AD7476}, - {"ad7476a", ID_AD7476}, - {"ad7477", ID_AD7477}, - {"ad7477a", ID_AD7477}, - {"ad7478", ID_AD7478}, - {"ad7478a", ID_AD7478}, + {"ad7475", ID_AD7466}, + {"ad7476", ID_AD7466}, + {"ad7476a", ID_AD7466}, + {"ad7477", ID_AD7467}, + {"ad7477a", ID_AD7467}, + {"ad7478", ID_AD7468}, + {"ad7478a", ID_AD7468}, {"ad7495", ID_AD7495}, + {"ad7910", ID_AD7467}, + {"ad7920", ID_AD7466}, + {"ad7940", ID_AD7940}, {} }; MODULE_DEVICE_TABLE(spi, ad7476_id); @@ -240,5 +328,5 @@ static struct spi_driver ad7476_driver = { module_spi_driver(ad7476_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); -MODULE_DESCRIPTION("Analog Devices AD7475/6/7/8(A) AD7466/7/8 ADC"); +MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 78452630f85d..b1c0ee5294ca 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -77,6 +77,17 @@ config AD5504 To compile this driver as a module, choose M here: the module will be called ad5504. +config AD5755 + tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD5755, AD5755-1, + AD5757, AD5735, AD5737 quad channel Digital to + Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5755. + config AD5764 tristate "Analog Devices AD5764/64R/44/44R DAC driver" depends on SPI_MASTER diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 9ea3ceeefc07..c0d333b23ba3 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o obj-$(CONFIG_AD5064) += ad5064.o obj-$(CONFIG_AD5504) += ad5504.o obj-$(CONFIG_AD5446) += ad5446.o +obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c new file mode 100644 index 000000000000..e6dbe5ff7e5a --- /dev/null +++ b/drivers/iio/dac/ad5755.c @@ -0,0 +1,650 @@ +/* + * AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/platform_data/ad5755.h> + +#define AD5755_NUM_CHANNELS 4 + +#define AD5755_ADDR(x) ((x) << 16) + +#define AD5755_WRITE_REG_DATA(chan) (chan) +#define AD5755_WRITE_REG_GAIN(chan) (0x08 | (chan)) +#define AD5755_WRITE_REG_OFFSET(chan) (0x10 | (chan)) +#define AD5755_WRITE_REG_CTRL(chan) (0x1c | (chan)) + +#define AD5755_READ_REG_DATA(chan) (chan) +#define AD5755_READ_REG_CTRL(chan) (0x4 | (chan)) +#define AD5755_READ_REG_GAIN(chan) (0x8 | (chan)) +#define AD5755_READ_REG_OFFSET(chan) (0xc | (chan)) +#define AD5755_READ_REG_CLEAR(chan) (0x10 | (chan)) +#define AD5755_READ_REG_SLEW(chan) (0x14 | (chan)) +#define AD5755_READ_REG_STATUS 0x18 +#define AD5755_READ_REG_MAIN 0x19 +#define AD5755_READ_REG_DC_DC 0x1a + +#define AD5755_CTRL_REG_SLEW 0x0 +#define AD5755_CTRL_REG_MAIN 0x1 +#define AD5755_CTRL_REG_DAC 0x2 +#define AD5755_CTRL_REG_DC_DC 0x3 +#define AD5755_CTRL_REG_SW 0x4 + +#define AD5755_READ_FLAG 0x800000 + +#define AD5755_NOOP 0x1CE000 + +#define AD5755_DAC_INT_EN BIT(8) +#define AD5755_DAC_CLR_EN BIT(7) +#define AD5755_DAC_OUT_EN BIT(6) +#define AD5755_DAC_INT_CURRENT_SENSE_RESISTOR BIT(5) +#define AD5755_DAC_DC_DC_EN BIT(4) +#define AD5755_DAC_VOLTAGE_OVERRANGE_EN BIT(3) + +#define AD5755_DC_DC_MAXV 0 +#define AD5755_DC_DC_FREQ_SHIFT 2 +#define AD5755_DC_DC_PHASE_SHIFT 4 +#define AD5755_EXT_DC_DC_COMP_RES BIT(6) + +#define AD5755_SLEW_STEP_SIZE_SHIFT 0 +#define AD5755_SLEW_RATE_SHIFT 3 +#define AD5755_SLEW_ENABLE BIT(12) + +/** + * struct ad5755_chip_info - chip specific information + * @channel_template: channel specification + * @calib_shift: shift for the calibration data registers + * @has_voltage_out: whether the chip has voltage outputs + */ +struct ad5755_chip_info { + const struct iio_chan_spec channel_template; + unsigned int calib_shift; + bool has_voltage_out; +}; + +/** + * struct ad5755_state - driver instance specific data + * @spi: spi device the driver is attached to + * @chip_info: chip model specific constants, available modes etc + * @pwr_down: bitmask which contains hether a channel is powered down or not + * @ctrl: software shadow of the channel ctrl registers + * @channels: iio channel spec for the device + * @data: spi transfer buffers + */ +struct ad5755_state { + struct spi_device *spi; + const struct ad5755_chip_info *chip_info; + unsigned int pwr_down; + unsigned int ctrl[AD5755_NUM_CHANNELS]; + struct iio_chan_spec channels[AD5755_NUM_CHANNELS]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + + union { + u32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +enum ad5755_type { + ID_AD5755, + ID_AD5757, + ID_AD5735, + ID_AD5737, +}; + +static int ad5755_write_unlocked(struct iio_dev *indio_dev, + unsigned int reg, unsigned int val) +{ + struct ad5755_state *st = iio_priv(indio_dev); + + st->data[0].d32 = cpu_to_be32((reg << 16) | val); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ad5755_write_ctrl_unlocked(struct iio_dev *indio_dev, + unsigned int channel, unsigned int reg, unsigned int val) +{ + return ad5755_write_unlocked(indio_dev, + AD5755_WRITE_REG_CTRL(channel), (reg << 13) | val); +} + +static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg, + unsigned int val) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad5755_write_unlocked(indio_dev, reg, val); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel, + unsigned int reg, unsigned int val) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr) +{ + struct ad5755_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .tx_buf = &st->data[1].d8[1], + .rx_buf = &st->data[1].d8[1], + .len = 3, + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + mutex_lock(&indio_dev->mlock); + + st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16)); + st->data[1].d32 = cpu_to_be32(AD5755_NOOP); + + ret = spi_sync(st->spi, &m); + if (ret >= 0) + ret = be32_to_cpu(st->data[1].d32) & 0xffff; + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5755_update_dac_ctrl(struct iio_dev *indio_dev, + unsigned int channel, unsigned int set, unsigned int clr) +{ + struct ad5755_state *st = iio_priv(indio_dev); + int ret; + + st->ctrl[channel] |= set; + st->ctrl[channel] &= ~clr; + + ret = ad5755_write_ctrl_unlocked(indio_dev, channel, + AD5755_CTRL_REG_DAC, st->ctrl[channel]); + + return ret; +} + +static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev, + unsigned int channel, bool pwr_down) +{ + struct ad5755_state *st = iio_priv(indio_dev); + unsigned int mask = BIT(channel); + + mutex_lock(&indio_dev->mlock); + + if ((bool)(st->pwr_down & mask) == pwr_down) + goto out_unlock; + + if (!pwr_down) { + st->pwr_down &= ~mask; + ad5755_update_dac_ctrl(indio_dev, channel, + AD5755_DAC_INT_EN | AD5755_DAC_DC_DC_EN, 0); + udelay(200); + ad5755_update_dac_ctrl(indio_dev, channel, + AD5755_DAC_OUT_EN, 0); + } else { + st->pwr_down |= mask; + ad5755_update_dac_ctrl(indio_dev, channel, + 0, AD5755_DAC_INT_EN | AD5755_DAC_OUT_EN | + AD5755_DAC_DC_DC_EN); + } + +out_unlock: + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static const int ad5755_min_max_table[][2] = { + [AD5755_MODE_VOLTAGE_0V_5V] = { 0, 5000 }, + [AD5755_MODE_VOLTAGE_0V_10V] = { 0, 10000 }, + [AD5755_MODE_VOLTAGE_PLUSMINUS_5V] = { -5000, 5000 }, + [AD5755_MODE_VOLTAGE_PLUSMINUS_10V] = { -10000, 10000 }, + [AD5755_MODE_CURRENT_4mA_20mA] = { 4, 20 }, + [AD5755_MODE_CURRENT_0mA_20mA] = { 0, 20 }, + [AD5755_MODE_CURRENT_0mA_24mA] = { 0, 24 }, +}; + +static void ad5755_get_min_max(struct ad5755_state *st, + struct iio_chan_spec const *chan, int *min, int *max) +{ + enum ad5755_mode mode = st->ctrl[chan->channel] & 7; + *min = ad5755_min_max_table[mode][0]; + *max = ad5755_min_max_table[mode][1]; +} + +static inline int ad5755_get_offset(struct ad5755_state *st, + struct iio_chan_spec const *chan) +{ + int min, max; + + ad5755_get_min_max(st, chan, &min, &max); + return (min * (1 << chan->scan_type.realbits)) / (max - min); +} + +static inline int ad5755_get_scale(struct ad5755_state *st, + struct iio_chan_spec const *chan) +{ + int min, max; + + ad5755_get_min_max(st, chan, &min, &max); + return ((max - min) * 1000000000ULL) >> chan->scan_type.realbits; +} + +static int ad5755_chan_reg_info(struct ad5755_state *st, + struct iio_chan_spec const *chan, long info, bool write, + unsigned int *reg, unsigned int *shift, unsigned int *offset) +{ + switch (info) { + case IIO_CHAN_INFO_RAW: + if (write) + *reg = AD5755_WRITE_REG_DATA(chan->address); + else + *reg = AD5755_READ_REG_DATA(chan->address); + *shift = chan->scan_type.shift; + *offset = 0; + break; + case IIO_CHAN_INFO_CALIBBIAS: + if (write) + *reg = AD5755_WRITE_REG_OFFSET(chan->address); + else + *reg = AD5755_READ_REG_OFFSET(chan->address); + *shift = st->chip_info->calib_shift; + *offset = 32768; + break; + case IIO_CHAN_INFO_CALIBSCALE: + if (write) + *reg = AD5755_WRITE_REG_GAIN(chan->address); + else + *reg = AD5755_READ_REG_GAIN(chan->address); + *shift = st->chip_info->calib_shift; + *offset = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ad5755_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + struct ad5755_state *st = iio_priv(indio_dev); + unsigned int reg, shift, offset; + int ret; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = ad5755_get_scale(st, chan); + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + *val = ad5755_get_offset(st, chan); + return IIO_VAL_INT; + default: + ret = ad5755_chan_reg_info(st, chan, info, false, + ®, &shift, &offset); + if (ret) + return ret; + + ret = ad5755_read(indio_dev, reg); + if (ret < 0) + return ret; + + *val = (ret - offset) >> shift; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad5755_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int val, int val2, long info) +{ + struct ad5755_state *st = iio_priv(indio_dev); + unsigned int shift, reg, offset; + int ret; + + ret = ad5755_chan_reg_info(st, chan, info, true, + ®, &shift, &offset); + if (ret) + return ret; + + val <<= shift; + val += offset; + + if (val < 0 || val > 0xffff) + return -EINVAL; + + return ad5755_write(indio_dev, reg, val); +} + +static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv, + const struct iio_chan_spec *chan, char *buf) +{ + struct ad5755_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + (bool)(st->pwr_down & (1 << chan->channel))); +} + +static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv, + struct iio_chan_spec const *chan, const char *buf, size_t len) +{ + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + ret = ad5755_set_channel_pwr_down(indio_dev, chan->channel, pwr_down); + return ret ? ret : len; +} + +static const struct iio_info ad5755_info = { + .read_raw = ad5755_read_raw, + .write_raw = ad5755_write_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5755_ext_info[] = { + { + .name = "powerdown", + .read = ad5755_read_powerdown, + .write = ad5755_write_powerdown, + }, + { }, +}; + +#define AD5755_CHANNEL(_bits) { \ + .indexed = 1, \ + .output = 1, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \ + .ext_info = ad5755_ext_info, \ +} + +static const struct ad5755_chip_info ad5755_chip_info_tbl[] = { + [ID_AD5735] = { + .channel_template = AD5755_CHANNEL(14), + .has_voltage_out = true, + .calib_shift = 4, + }, + [ID_AD5737] = { + .channel_template = AD5755_CHANNEL(14), + .has_voltage_out = false, + .calib_shift = 4, + }, + [ID_AD5755] = { + .channel_template = AD5755_CHANNEL(16), + .has_voltage_out = true, + .calib_shift = 0, + }, + [ID_AD5757] = { + .channel_template = AD5755_CHANNEL(16), + .has_voltage_out = false, + .calib_shift = 0, + }, +}; + +static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode) +{ + switch (mode) { + case AD5755_MODE_VOLTAGE_0V_5V: + case AD5755_MODE_VOLTAGE_0V_10V: + case AD5755_MODE_VOLTAGE_PLUSMINUS_5V: + case AD5755_MODE_VOLTAGE_PLUSMINUS_10V: + return st->chip_info->has_voltage_out; + case AD5755_MODE_CURRENT_4mA_20mA: + case AD5755_MODE_CURRENT_0mA_20mA: + case AD5755_MODE_CURRENT_0mA_24mA: + return true; + default: + return false; + } +} + +static int __devinit ad5755_setup_pdata(struct iio_dev *indio_dev, + const struct ad5755_platform_data *pdata) +{ + struct ad5755_state *st = iio_priv(indio_dev); + unsigned int ret; + unsigned int val; + unsigned int i; + + if (pdata->dc_dc_phase > AD5755_DC_DC_PHASE_90_DEGREE || + pdata->dc_dc_freq > AD5755_DC_DC_FREQ_650kHZ || + pdata->dc_dc_maxv > AD5755_DC_DC_MAXV_29V5) + return -EINVAL; + + val = pdata->dc_dc_maxv << AD5755_DC_DC_MAXV; + val |= pdata->dc_dc_freq << AD5755_DC_DC_FREQ_SHIFT; + val |= pdata->dc_dc_phase << AD5755_DC_DC_PHASE_SHIFT; + if (pdata->ext_dc_dc_compenstation_resistor) + val |= AD5755_EXT_DC_DC_COMP_RES; + + ret = ad5755_write_ctrl(indio_dev, 0, AD5755_CTRL_REG_DC_DC, val); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) { + val = pdata->dac[i].slew.step_size << + AD5755_SLEW_STEP_SIZE_SHIFT; + val |= pdata->dac[i].slew.rate << + AD5755_SLEW_RATE_SHIFT; + if (pdata->dac[i].slew.enable) + val |= AD5755_SLEW_ENABLE; + + ret = ad5755_write_ctrl(indio_dev, i, + AD5755_CTRL_REG_SLEW, val); + if (ret < 0) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) { + if (!ad5755_is_valid_mode(st, pdata->dac[i].mode)) + return -EINVAL; + + val = 0; + if (!pdata->dac[i].ext_current_sense_resistor) + val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR; + if (pdata->dac[i].enable_voltage_overrange) + val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN; + val |= pdata->dac[i].mode; + + ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static bool __devinit ad5755_is_voltage_mode(enum ad5755_mode mode) +{ + switch (mode) { + case AD5755_MODE_VOLTAGE_0V_5V: + case AD5755_MODE_VOLTAGE_0V_10V: + case AD5755_MODE_VOLTAGE_PLUSMINUS_5V: + case AD5755_MODE_VOLTAGE_PLUSMINUS_10V: + return true; + default: + return false; + } +} + +static int __devinit ad5755_init_channels(struct iio_dev *indio_dev, + const struct ad5755_platform_data *pdata) +{ + struct ad5755_state *st = iio_priv(indio_dev); + struct iio_chan_spec *channels = st->channels; + unsigned int i; + + for (i = 0; i < AD5755_NUM_CHANNELS; ++i) { + channels[i] = st->chip_info->channel_template; + channels[i].channel = i; + channels[i].address = i; + if (pdata && ad5755_is_voltage_mode(pdata->dac[i].mode)) + channels[i].type = IIO_VOLTAGE; + else + channels[i].type = IIO_CURRENT; + } + + indio_dev->channels = channels; + + return 0; +} + +#define AD5755_DEFAULT_DAC_PDATA { \ + .mode = AD5755_MODE_CURRENT_4mA_20mA, \ + .ext_current_sense_resistor = true, \ + .enable_voltage_overrange = false, \ + .slew = { \ + .enable = false, \ + .rate = AD5755_SLEW_RATE_64k, \ + .step_size = AD5755_SLEW_STEP_SIZE_1, \ + }, \ + } + +static const struct ad5755_platform_data ad5755_default_pdata = { + .ext_dc_dc_compenstation_resistor = false, + .dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE, + .dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ, + .dc_dc_maxv = AD5755_DC_DC_MAXV_23V, + .dac = { + [0] = AD5755_DEFAULT_DAC_PDATA, + [1] = AD5755_DEFAULT_DAC_PDATA, + [2] = AD5755_DEFAULT_DAC_PDATA, + [3] = AD5755_DEFAULT_DAC_PDATA, + }, +}; + +static int __devinit ad5755_probe(struct spi_device *spi) +{ + enum ad5755_type type = spi_get_device_id(spi)->driver_data; + const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev); + struct iio_dev *indio_dev; + struct ad5755_state *st; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->chip_info = &ad5755_chip_info_tbl[type]; + st->spi = spi; + st->pwr_down = 0xf; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5755_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = AD5755_NUM_CHANNELS; + + if (!pdata) + pdata = &ad5755_default_pdata; + + ret = ad5755_init_channels(indio_dev, pdata); + if (ret) + goto error_free; + + ret = ad5755_setup_pdata(indio_dev, pdata); + if (ret) + goto error_free; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); + goto error_free; + } + + return 0; + +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5755_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + iio_device_unregister(indio_dev); + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5755_id[] = { + { "ad5755", ID_AD5755 }, + { "ad5755-1", ID_AD5755 }, + { "ad5757", ID_AD5757 }, + { "ad5735", ID_AD5735 }, + { "ad5737", ID_AD5737 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5755_id); + +static struct spi_driver ad5755_driver = { + .driver = { + .name = "ad5755", + .owner = THIS_MODULE, + }, + .probe = ad5755_probe, + .remove = __devexit_p(ad5755_remove), + .id_table = ad5755_id, +}; +module_spi_driver(ad5755_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Analog Devices AD5755/55-1/57/35/37 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 0499330d6e98..6eb24dbc081e 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -366,6 +366,7 @@ static ssize_t iio_read_channel_info(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + unsigned long long tmp; int val, val2; bool scale_db = false; int ret = indio_dev->info->read_raw(indio_dev, this_attr->c, @@ -391,6 +392,11 @@ static ssize_t iio_read_channel_info(struct device *dev, return sprintf(buf, "-%d.%09u\n", val, -val2); else return sprintf(buf, "%d.%09u\n", val, val2); + case IIO_VAL_FRACTIONAL: + tmp = div_s64((s64)val * 1000000000LL, val2); + val2 = do_div(tmp, 1000000000LL); + val = tmp; + return sprintf(buf, "%d.%09u\n", val, val2); default: return 0; } diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index a14e55dd8ddc..25b00761005a 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -130,18 +130,27 @@ struct iio_channel *iio_channel_get(const char *name, const char *channel_name) if (c == NULL) return ERR_PTR(-ENODEV); - channel = kmalloc(sizeof(*channel), GFP_KERNEL); + channel = kzalloc(sizeof(*channel), GFP_KERNEL); if (channel == NULL) return ERR_PTR(-ENOMEM); channel->indio_dev = c->indio_dev; - if (c->map->adc_channel_label) + if (c->map->adc_channel_label) { channel->channel = iio_chan_spec_from_name(channel->indio_dev, c->map->adc_channel_label); + if (channel->channel == NULL) + goto error_no_chan; + } + return channel; + +error_no_chan: + iio_device_put(c->indio_dev); + kfree(channel); + return ERR_PTR(-EINVAL); } EXPORT_SYMBOL_GPL(iio_channel_get); @@ -229,9 +238,21 @@ void iio_channel_release_all(struct iio_channel *channels) } EXPORT_SYMBOL_GPL(iio_channel_release_all); +static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, + enum iio_chan_info_enum info) +{ + int unused; + + if (val2 == NULL) + val2 = &unused; + + return chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel, + val, val2, info); +} + int iio_read_channel_raw(struct iio_channel *chan, int *val) { - int val2, ret; + int ret; mutex_lock(&chan->indio_dev->info_exist_lock); if (chan->indio_dev->info == NULL) { @@ -239,10 +260,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val) goto err_unlock; } - ret = chan->indio_dev->info->read_raw(chan->indio_dev, - chan->channel, - val, &val2, - IIO_CHAN_INFO_RAW); + ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); err_unlock: mutex_unlock(&chan->indio_dev->info_exist_lock); @@ -250,6 +268,100 @@ err_unlock: } EXPORT_SYMBOL_GPL(iio_read_channel_raw); +static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, + int raw, int *processed, unsigned int scale) +{ + int scale_type, scale_val, scale_val2, offset; + s64 raw64 = raw; + int ret; + + ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_SCALE); + if (ret == 0) + raw64 += offset; + + scale_type = iio_channel_read(chan, &scale_val, &scale_val2, + IIO_CHAN_INFO_SCALE); + if (scale_type < 0) + return scale_type; + + switch (scale_type) { + case IIO_VAL_INT: + *processed = raw64 * scale_val; + break; + case IIO_VAL_INT_PLUS_MICRO: + if (scale_val2 < 0) + *processed = -raw64 * scale_val; + else + *processed = raw64 * scale_val; + *processed += div_s64(raw64 * (s64)scale_val2 * scale, + 1000000LL); + break; + case IIO_VAL_INT_PLUS_NANO: + if (scale_val2 < 0) + *processed = -raw64 * scale_val; + else + *processed = raw64 * scale_val; + *processed += div_s64(raw64 * (s64)scale_val2 * scale, + 1000000000LL); + break; + case IIO_VAL_FRACTIONAL: + *processed = div_s64(raw64 * (s64)scale_val * scale, + scale_val2); + break; + default: + return -EINVAL; + } + + return 0; +} + +int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, + int *processed, unsigned int scale) +{ + int ret; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (chan->indio_dev->info == NULL) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed, + scale); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); + +int iio_read_channel_processed(struct iio_channel *chan, int *val) +{ + int ret; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (chan->indio_dev->info == NULL) { + ret = -ENODEV; + goto err_unlock; + } + + if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) { + ret = iio_channel_read(chan, val, NULL, + IIO_CHAN_INFO_PROCESSED); + } else { + ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); + if (ret < 0) + goto err_unlock; + ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 1); + } + +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(iio_read_channel_processed); + int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) { int ret; @@ -260,10 +372,7 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) goto err_unlock; } - ret = chan->indio_dev->info->read_raw(chan->indio_dev, - chan->channel, - val, val2, - IIO_CHAN_INFO_SCALE); + ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE); err_unlock: mutex_unlock(&chan->indio_dev->info_exist_lock); diff --git a/drivers/staging/iio/Documentation/inkernel.txt b/drivers/staging/iio/Documentation/inkernel.txt index a05823e955d2..ab528409bba6 100644 --- a/drivers/staging/iio/Documentation/inkernel.txt +++ b/drivers/staging/iio/Documentation/inkernel.txt @@ -48,11 +48,11 @@ There are then a number of functions that can be used to get information about this channel such as it's current reading. e.g. -iio_st_read_channel_raw() - get a reading -iio_st_read_channel_type() - get the type of channel +iio_read_channel_raw() - get a reading +iio_get_channel_type() - get the type of channel There is also provision for retrieving all of the channels associated with a given consumer. This is useful for generic drivers such as iio_hwmon where the number and naming of channels is not known by the -consumer driver. To do this, use iio_st_channel_get_all. +consumer driver. To do this, use iio_channel_get_all. diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index a1fa1729acbe..1b4a356639a5 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -68,20 +68,6 @@ config AD799X_RING_BUFFER Say yes here to include ring buffer support in the AD799X ADC driver. -config AD7476 - tristate "Analog Devices AD7475/6/7/8 AD7466/7/8 and AD7495 ADC driver" - depends on SPI - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Say yes here to build support for Analog Devices - AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495 - SPI analog to digital converters (ADC). - If unsure, say N (but it's safe to say "Y"). - - To compile this driver as a module, choose M here: the - module will be called ad7476. - config AD7887 tristate "Analog Devices AD7887 ADC driver" depends on SPI diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index ecac9a0bb358..62ee02e80cf9 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -17,10 +17,6 @@ ad799x-y := ad799x_core.o ad799x-$(CONFIG_AD799X_RING_BUFFER) += ad799x_ring.o obj-$(CONFIG_AD799X) += ad799x.o -ad7476-y := ad7476_core.o -ad7476-$(CONFIG_IIO_BUFFER) += ad7476_ring.o -obj-$(CONFIG_AD7476) += ad7476.o - ad7887-y := ad7887_core.o ad7887-$(CONFIG_IIO_BUFFER) += ad7887_ring.o obj-$(CONFIG_AD7887) += ad7887.o diff --git a/drivers/staging/iio/adc/ad7476.h b/drivers/staging/iio/adc/ad7476.h deleted file mode 100644 index b1dd9317fe1f..000000000000 --- a/drivers/staging/iio/adc/ad7476.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * AD7476/5/7/8 (A) SPI ADC driver - * - * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ -#ifndef IIO_ADC_AD7476_H_ -#define IIO_ADC_AD7476_H_ - -#define RES_MASK(bits) ((1 << (bits)) - 1) - -/* - * TODO: struct ad7476_platform_data needs to go into include/linux/iio - */ - -struct ad7476_platform_data { - u16 vref_mv; -}; - -struct ad7476_chip_info { - u16 int_vref_mv; - struct iio_chan_spec channel[2]; -}; - -struct ad7476_state { - struct spi_device *spi; - const struct ad7476_chip_info *chip_info; - struct regulator *reg; - u16 int_vref_mv; - struct spi_transfer xfer; - struct spi_message msg; - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - unsigned char data[2] ____cacheline_aligned; -}; - -enum ad7476_supported_device_ids { - ID_AD7466, - ID_AD7467, - ID_AD7468, - ID_AD7475, - ID_AD7476, - ID_AD7477, - ID_AD7478, - ID_AD7495 -}; - -#ifdef CONFIG_IIO_BUFFER -int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev); -void ad7476_ring_cleanup(struct iio_dev *indio_dev); -#else /* CONFIG_IIO_BUFFER */ - -static inline int -ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev) -{ - return 0; -} - -static inline void ad7476_ring_cleanup(struct iio_dev *indio_dev) -{ -} -#endif /* CONFIG_IIO_BUFFER */ -#endif /* IIO_ADC_AD7476_H_ */ diff --git a/drivers/staging/iio/adc/ad7476_ring.c b/drivers/staging/iio/adc/ad7476_ring.c deleted file mode 100644 index 940681f29b87..000000000000 --- a/drivers/staging/iio/adc/ad7476_ring.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2010-2012 Analog Devices Inc. - * Copyright (C) 2008 Jonathan Cameron - * - * Licensed under the GPL-2 or later. - * - * ad7476_ring.c - */ - -#include <linux/interrupt.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/spi/spi.h> - -#include <linux/iio/iio.h> -#include <linux/iio/buffer.h> -#include <linux/iio/trigger_consumer.h> -#include <linux/iio/triggered_buffer.h> - -#include "ad7476.h" - -static irqreturn_t ad7476_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct ad7476_state *st = iio_priv(indio_dev); - s64 time_ns; - __u8 *rxbuf; - int b_sent; - - rxbuf = kzalloc(indio_dev->scan_bytes, GFP_KERNEL); - if (rxbuf == NULL) - goto done; - - b_sent = spi_read(st->spi, rxbuf, - st->chip_info->channel[0].scan_type.storagebits / 8); - if (b_sent < 0) - goto done; - - time_ns = iio_get_time_ns(); - - if (indio_dev->scan_timestamp) - memcpy(rxbuf + indio_dev->scan_bytes - sizeof(s64), - &time_ns, sizeof(time_ns)); - - iio_push_to_buffer(indio_dev->buffer, rxbuf); -done: - iio_trigger_notify_done(indio_dev->trig); - kfree(rxbuf); - - return IRQ_HANDLED; -} - -int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev) -{ - return iio_triggered_buffer_setup(indio_dev, NULL, - &ad7476_trigger_handler, NULL); -} - -void ad7476_ring_cleanup(struct iio_dev *indio_dev) -{ - iio_triggered_buffer_cleanup(indio_dev); -} diff --git a/drivers/staging/iio/iio_hwmon.c b/drivers/staging/iio/iio_hwmon.c index 69d4a3b0ced4..5d491227e01b 100644 --- a/drivers/staging/iio/iio_hwmon.c +++ b/drivers/staging/iio/iio_hwmon.c @@ -42,40 +42,17 @@ static ssize_t iio_hwmon_read_val(struct device *dev, struct device_attribute *attr, char *buf) { - long result; - int val, ret, scaleint, scalepart; + int result; + int ret; struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); struct iio_hwmon_state *state = dev_get_drvdata(dev); - /* - * No locking between this pair, so theoretically possible - * the scale has changed. - */ - ret = iio_read_channel_raw(&state->channels[sattr->index], - &val); + ret = iio_read_channel_processed(&state->channels[sattr->index], + &result); if (ret < 0) return ret; - ret = iio_read_channel_scale(&state->channels[sattr->index], - &scaleint, &scalepart); - if (ret < 0) - return ret; - switch (ret) { - case IIO_VAL_INT: - result = val * scaleint; - break; - case IIO_VAL_INT_PLUS_MICRO: - result = (s64)val * (s64)scaleint + - div_s64((s64)val * (s64)scalepart, 1000000LL); - break; - case IIO_VAL_INT_PLUS_NANO: - result = (s64)val * (s64)scaleint + - div_s64((s64)val * (s64)scalepart, 1000000000LL); - break; - default: - return -EINVAL; - } - return sprintf(buf, "%ld\n", result); + return sprintf(buf, "%d\n", result); } static void iio_hwmon_free_attrs(struct iio_hwmon_state *st) diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c index ce6a7b1b8860..52062d786f84 100644 --- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c +++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c @@ -14,14 +14,18 @@ #include <linux/delay.h> #include <asm/gptimers.h> +#include <asm/portmux.h> #include <linux/iio/iio.h> #include <linux/iio/trigger.h> +#include "iio-trig-bfin-timer.h" + struct bfin_timer { unsigned short id, bit; unsigned long irqbit; int irq; + int pin; }; /* @@ -30,22 +34,22 @@ struct bfin_timer { */ static struct bfin_timer iio_bfin_timer_code[MAX_BLACKFIN_GPTIMERS] = { - {TIMER0_id, TIMER0bit, TIMER_STATUS_TIMIL0, IRQ_TIMER0}, - {TIMER1_id, TIMER1bit, TIMER_STATUS_TIMIL1, IRQ_TIMER1}, - {TIMER2_id, TIMER2bit, TIMER_STATUS_TIMIL2, IRQ_TIMER2}, + {TIMER0_id, TIMER0bit, TIMER_STATUS_TIMIL0, IRQ_TIMER0, P_TMR0}, + {TIMER1_id, TIMER1bit, TIMER_STATUS_TIMIL1, IRQ_TIMER1, P_TMR1}, + {TIMER2_id, TIMER2bit, TIMER_STATUS_TIMIL2, IRQ_TIMER2, P_TMR2}, #if (MAX_BLACKFIN_GPTIMERS > 3) - {TIMER3_id, TIMER3bit, TIMER_STATUS_TIMIL3, IRQ_TIMER3}, - {TIMER4_id, TIMER4bit, TIMER_STATUS_TIMIL4, IRQ_TIMER4}, - {TIMER5_id, TIMER5bit, TIMER_STATUS_TIMIL5, IRQ_TIMER5}, - {TIMER6_id, TIMER6bit, TIMER_STATUS_TIMIL6, IRQ_TIMER6}, - {TIMER7_id, TIMER7bit, TIMER_STATUS_TIMIL7, IRQ_TIMER7}, + {TIMER3_id, TIMER3bit, TIMER_STATUS_TIMIL3, IRQ_TIMER3, P_TMR3}, + {TIMER4_id, TIMER4bit, TIMER_STATUS_TIMIL4, IRQ_TIMER4, P_TMR4}, + {TIMER5_id, TIMER5bit, TIMER_STATUS_TIMIL5, IRQ_TIMER5, P_TMR5}, + {TIMER6_id, TIMER6bit, TIMER_STATUS_TIMIL6, IRQ_TIMER6, P_TMR6}, + {TIMER7_id, TIMER7bit, TIMER_STATUS_TIMIL7, IRQ_TIMER7, P_TMR7}, #endif #if (MAX_BLACKFIN_GPTIMERS > 8) - {TIMER8_id, TIMER8bit, TIMER_STATUS_TIMIL8, IRQ_TIMER8}, - {TIMER9_id, TIMER9bit, TIMER_STATUS_TIMIL9, IRQ_TIMER9}, - {TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10}, + {TIMER8_id, TIMER8bit, TIMER_STATUS_TIMIL8, IRQ_TIMER8, P_TMR8}, + {TIMER9_id, TIMER9bit, TIMER_STATUS_TIMIL9, IRQ_TIMER9, P_TMR9}, + {TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10, P_TMR10}, #if (MAX_BLACKFIN_GPTIMERS > 11) - {TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11}, + {TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11, P_TMR11}, #endif #endif }; @@ -54,15 +58,33 @@ struct bfin_tmr_state { struct iio_trigger *trig; struct bfin_timer *t; unsigned timer_num; + bool output_enable; + unsigned int duty; int irq; }; +static int iio_bfin_tmr_set_state(struct iio_trigger *trig, bool state) +{ + struct bfin_tmr_state *st = trig->private_data; + + if (get_gptimer_period(st->t->id) == 0) + return -EINVAL; + + if (state) + enable_gptimers(st->t->bit); + else + disable_gptimers(st->t->bit); + + return 0; +} + static ssize_t iio_bfin_tmr_frequency_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct iio_trigger *trig = to_iio_trigger(dev); struct bfin_tmr_state *st = trig->private_data; - long val; + unsigned long val; + bool enabled; int ret; ret = strict_strtoul(buf, 10, &val); @@ -74,20 +96,25 @@ static ssize_t iio_bfin_tmr_frequency_store(struct device *dev, goto error_ret; } - disable_gptimers(st->t->bit); + enabled = get_enabled_gptimers() & st->t->bit; + + if (enabled) + disable_gptimers(st->t->bit); if (!val) goto error_ret; val = get_sclk() / val; - if (val <= 4) { + if (val <= 4 || val <= st->duty) { ret = -EINVAL; goto error_ret; } set_gptimer_period(st->t->id, val); - set_gptimer_pwidth(st->t->id, 1); - enable_gptimers(st->t->bit); + set_gptimer_pwidth(st->t->id, val - st->duty); + + if (enabled) + enable_gptimers(st->t->bit); error_ret: return ret ? ret : count; @@ -99,9 +126,15 @@ static ssize_t iio_bfin_tmr_frequency_show(struct device *dev, { struct iio_trigger *trig = to_iio_trigger(dev); struct bfin_tmr_state *st = trig->private_data; + unsigned int period = get_gptimer_period(st->t->id); + unsigned long val; + + if (period == 0) + val = 0; + else + val = get_sclk() / get_gptimer_period(st->t->id); - return sprintf(buf, "%lu\n", - get_sclk() / get_gptimer_period(st->t->id)); + return sprintf(buf, "%lu\n", val); } static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_bfin_tmr_frequency_show, @@ -121,7 +154,6 @@ static const struct attribute_group *iio_bfin_tmr_trigger_attr_groups[] = { NULL }; - static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid) { struct bfin_tmr_state *st = devid; @@ -145,11 +177,14 @@ static int iio_bfin_tmr_get_number(int irq) static const struct iio_trigger_ops iio_bfin_tmr_trigger_ops = { .owner = THIS_MODULE, + .set_trigger_state = iio_bfin_tmr_set_state, }; static int __devinit iio_bfin_tmr_trigger_probe(struct platform_device *pdev) { + struct iio_bfin_timer_trigger_pdata *pdata = pdev->dev.platform_data; struct bfin_tmr_state *st; + unsigned int config; int ret; st = kzalloc(sizeof(*st), GFP_KERNEL); @@ -193,13 +228,43 @@ static int __devinit iio_bfin_tmr_trigger_probe(struct platform_device *pdev) goto out4; } - set_gptimer_config(st->t->id, OUT_DIS | PWM_OUT | PERIOD_CNT | IRQ_ENA); + config = PWM_OUT | PERIOD_CNT | IRQ_ENA; + + if (pdata && pdata->output_enable) { + unsigned long long val; + + st->output_enable = true; + + ret = peripheral_request(st->t->pin, st->trig->name); + if (ret) + goto out_free_irq; + + val = (unsigned long long)get_sclk() * pdata->duty_ns; + do_div(val, NSEC_PER_SEC); + st->duty = val; + + /** + * The interrupt will be generated at the end of the period, + * since we want the interrupt to be generated at end of the + * pulse we invert both polarity and duty cycle, so that the + * pulse will be generated directly before the interrupt. + */ + if (pdata->active_low) + config |= PULSE_HI; + } else { + st->duty = 1; + config |= OUT_DIS; + } + + set_gptimer_config(st->t->id, config); dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d", st->timer_num, st->irq); platform_set_drvdata(pdev, st); return 0; +out_free_irq: + free_irq(st->irq, st); out4: iio_trigger_unregister(st->trig); out2: @@ -215,6 +280,8 @@ static int __devexit iio_bfin_tmr_trigger_remove(struct platform_device *pdev) struct bfin_tmr_state *st = platform_get_drvdata(pdev); disable_gptimers(st->t->bit); + if (st->output_enable) + peripheral_free(st->t->pin); free_irq(st->irq, st); iio_trigger_unregister(st->trig); iio_trigger_put(st->trig); diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.h b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h new file mode 100644 index 000000000000..c07321f8d94c --- /dev/null +++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h @@ -0,0 +1,24 @@ +#ifndef __IIO_BFIN_TIMER_TRIGGER_H__ +#define __IIO_BFIN_TIMER_TRIGGER_H__ + +/** + * struct iio_bfin_timer_trigger_pdata - timer trigger platform data + * @output_enable: Enable external trigger pulse generation. + * @active_low: Whether the trigger pulse is active low. + * @duty_ns: Length of the trigger pulse in nanoseconds. + * + * This struct is used to configure the output pulse generation of the blackfin + * timer trigger. If output_enable is set to true an external trigger signal + * will generated on the pin corresponding to the timer. This is useful for + * converters which needs an external signal to start conversion. active_low and + * duty_ns are used to configure the type of the trigger pulse. If output_enable + * is set to false no external trigger pulse will be generated and active_low + * and duty_ns are ignored. + **/ +struct iio_bfin_timer_trigger_pdata { + bool output_enable; + bool active_low; + unsigned int duty_ns; +}; + +#endif |