* [PATCH 1/2] iio: Add a logarithmic fractional value type @ 2012-10-16 16:29 Lars-Peter Clausen 2012-10-16 16:29 ` [PATCH 2/2] iio:dac: Add support for the ad5449 Lars-Peter Clausen 2012-10-19 15:56 ` [PATCH 1/2] iio: Add a logarithmic fractional value type Jonathan Cameron 0 siblings, 2 replies; 4+ messages in thread From: Lars-Peter Clausen @ 2012-10-16 16:29 UTC (permalink / raw) To: Jonathan Cameron; +Cc: linux-iio, drivers, Lars-Peter Clausen 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 <lars@metafoo.de> --- drivers/iio/industrialio-core.c | 5 +++++ drivers/iio/inkern.c | 3 +++ include/linux/iio/types.h | 1 + 3 files changed, 9 insertions(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 6eb24db..37650a7 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 f2b78d4..2ee4450 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 5c647ec..87b196a 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_ */ -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] iio:dac: Add support for the ad5449 2012-10-16 16:29 [PATCH 1/2] iio: Add a logarithmic fractional value type Lars-Peter Clausen @ 2012-10-16 16:29 ` Lars-Peter Clausen 2012-10-19 15:56 ` Jonathan Cameron 2012-10-19 15:56 ` [PATCH 1/2] iio: Add a logarithmic fractional value type Jonathan Cameron 1 sibling, 1 reply; 4+ messages in thread From: Lars-Peter Clausen @ 2012-10-16 16:29 UTC (permalink / raw) To: Jonathan Cameron; +Cc: linux-iio, drivers, Lars-Peter Clausen 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 <lars@metafoo.de> --- 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 diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index b1c0ee5..f68756e 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 c0d333b..5b528eb 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 0000000..5b43030 --- /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 <lars@metafoo.de> + * + * 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/regulator/consumer.h> +#include <asm/unaligned.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include <linux/platform_data/ad5449.h> + +#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 <lars@metafoo.de>"); +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 0000000..bd712bd --- /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 <lars@metafoo.de> + * + * 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 -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 2/2] iio:dac: Add support for the ad5449 2012-10-16 16:29 ` [PATCH 2/2] iio:dac: Add support for the ad5449 Lars-Peter Clausen @ 2012-10-19 15:56 ` Jonathan Cameron 0 siblings, 0 replies; 4+ messages in thread From: Jonathan Cameron @ 2012-10-19 15:56 UTC (permalink / raw) To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers On 10/16/2012 05:29 PM, Lars-Peter Clausen wrote: > This patch adds support for the AD5415, AD5426, AD5429, AD5432, AD5439, AD5443 > and AD5449 single and dual channel DACs. > Clean, short and generally sweet. Just how I like it. > Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Merged to togreg branch of iio.git. > --- > 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 > > diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig > index b1c0ee5..f68756e 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 c0d333b..5b528eb 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 0000000..5b43030 > --- /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 <lars@metafoo.de> > + * > + * 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/regulator/consumer.h> > +#include <asm/unaligned.h> > + > +#include <linux/iio/iio.h> > +#include <linux/iio/sysfs.h> > + > +#include <linux/platform_data/ad5449.h> > + > +#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 <lars@metafoo.de>"); > +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 0000000..bd712bd > --- /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 <lars@metafoo.de> > + * > + * 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 > ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 1/2] iio: Add a logarithmic fractional value type 2012-10-16 16:29 [PATCH 1/2] iio: Add a logarithmic fractional value type Lars-Peter Clausen 2012-10-16 16:29 ` [PATCH 2/2] iio:dac: Add support for the ad5449 Lars-Peter Clausen @ 2012-10-19 15:56 ` Jonathan Cameron 1 sibling, 0 replies; 4+ messages in thread From: Jonathan Cameron @ 2012-10-19 15:56 UTC (permalink / raw) To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers On 10/16/2012 05:29 PM, Lars-Peter Clausen wrote: > 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. Only slight issue with this is that continual addition of these will eventually make for a mess. Having said that, this one is sensible and as you say common. > > Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Added to togreg branch of iio.git > --- > drivers/iio/industrialio-core.c | 5 +++++ > drivers/iio/inkern.c | 3 +++ > include/linux/iio/types.h | 1 + > 3 files changed, 9 insertions(+) > > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c > index 6eb24db..37650a7 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 f2b78d4..2ee4450 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 5c647ec..87b196a 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_ */ > ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2012-10-19 17:42 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-10-16 16:29 [PATCH 1/2] iio: Add a logarithmic fractional value type Lars-Peter Clausen 2012-10-16 16:29 ` [PATCH 2/2] iio:dac: Add support for the ad5449 Lars-Peter Clausen 2012-10-19 15:56 ` Jonathan Cameron 2012-10-19 15:56 ` [PATCH 1/2] iio: Add a logarithmic fractional value type Jonathan Cameron
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).