On 10/17/11 12:03, Lars-Peter Clausen wrote: > On 10/17/2011 12:36 PM, Jonathan Cameron wrote: >> On 10/17/11 11:09, Lars-Peter Clausen wrote: >>> This patch adds support for the Analog Devices AD5360, AD5361, AD5362, AD5363, >>> AD5370, AD5371, AD5372, AD5373 multi-channel digital-to-analog converters. >>> >>> Signed-off-by: Lars-Peter Clausen <lars@xxxxxxxxxx> >>> >>> --- >>> >>> I'm not entirly sure about one aspect of this driver. The chip has multiple >>> reference voltages and each vref is shared between multiple channels. There is >>> an adjustable offset per vref. For the current implementation I choose to have a >>> offset attribute for each channel which returns the offset which is used for >>> that channel. So now if that offset gets written to, the offset for all other >>> channels which share the same offset gets updated as well. >> That is fine. We do exactly the same thing all over the place. Docs probably need >> a strongly worded note that userspace should never expect these things to be independent. >> Always check you got what you want afterwards! >> >>> The alternative would be to register a iio device per vref and have a shared >>> offset attribute. >> You have gone to a lot of effort here to do channel allocation dynamically. >> I'm not entirely sure it wouldn't have been better to simply do it statically >> with a few macros to keep the code to a minimum. Still what you have is clean >> so this is more be being a lazy reviewer than a real complaint. Static stuff >> is easier to review! > We'd have a total of ~200 channel specs if we'd define them all static, > so we'd either end up with a lot of copy'n'paste or some crude macro magic. > >> Couple of nitpicks in line. Fix them up and I'm happy. > Ok, thanks. Shall I add your ack and send it directly to Greg, or should I > resend to you first? resend first and wait for a day or so to see if Michael in particular wants to comment. >>> --- >>> drivers/staging/iio/dac/Kconfig | 10 + >>> drivers/staging/iio/dac/Makefile | 1 + >>> drivers/staging/iio/dac/ad5360.c | 584 ++++++++++++++++++++++++++++++++++++++ >>> 3 files changed, 595 insertions(+), 0 deletions(-) >>> create mode 100644 drivers/staging/iio/dac/ad5360.c >>> >>> diff --git a/drivers/staging/iio/dac/Kconfig b/drivers/staging/iio/dac/Kconfig >>> index 3000156..d5c95dc 100644 >>> --- a/drivers/staging/iio/dac/Kconfig >>> +++ b/drivers/staging/iio/dac/Kconfig >>> @@ -3,6 +3,16 @@ >>> # >>> menu "Digital to analog convertors" >>> >>> +config AD5360 >>> + tristate "Analog Devices Analog Devices AD5360/61/62/63/70/71/73 DAC driver" >>> + depends on SPI >>> + help >>> + Say yes here to build support for Analog Devices AD5360/61/62/63/70/71/73 >> Please list full part numbers in the help. Makes it easy to grep for them. >>> + digital-to-analog convertors (DAC). This driver uses the common SPI interface. >>> + >>> + To compile this driver as module choose M here: the module will be called >>> + ad5360. >>> + >>> config AD5624R_SPI >>> tristate "Analog Devices AD5624/44/64R DAC spi driver" >>> depends on SPI >>> diff --git a/drivers/staging/iio/dac/Makefile b/drivers/staging/iio/dac/Makefile >>> index 7f4f2ed..e0a8c97 100644 >>> --- a/drivers/staging/iio/dac/Makefile >>> +++ b/drivers/staging/iio/dac/Makefile >>> @@ -2,6 +2,7 @@ >>> # Makefile for industrial I/O DAC drivers >>> # >>> >>> +obj-$(CONFIG_AD5360) += ad5360.o >>> obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o >>> obj-$(CONFIG_AD5504) += ad5504.o >>> obj-$(CONFIG_AD5446) += ad5446.o >>> diff --git a/drivers/staging/iio/dac/ad5360.c b/drivers/staging/iio/dac/ad5360.c >>> new file mode 100644 >>> index 0000000..6cdff44 >>> --- /dev/null >>> +++ b/drivers/staging/iio/dac/ad5360.c >>> @@ -0,0 +1,584 @@ >>> +/* >>> + * AD5360 Digital to analog converters driver >>> + * >>> + * Copyright 2011 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/regulator/consumer.h> >>> + >>> +#include "../iio.h" >>> +#include "../sysfs.h" >>> +#include "dac.h" >>> + >>> +#define AD5360_CMD(x) ((x) << 22) >>> +#define AD5360_ADDR(x) ((x) << 16) >>> + >>> +#define AD5360_READBACK_TYPE(x) ((x) << 13) >>> +#define AD5360_READBACK_ADDR(x) ((x) << 7) >>> + >>> +#define AD5360_CHAN_ADDR(chan) ((chan) + 0x8) >>> + >>> +#define AD5360_CMD_WRITE_DATA 0x3 >>> +#define AD5360_CMD_WRITE_OFFSET 0x2 >>> +#define AD5360_CMD_WRITE_GAIN 0x1 >>> +#define AD5360_CMD_SPECIAL_FUNCTION 0x0 >>> + >>> +/* Special function register addresses */ >>> +#define AD5360_REG_SF_NOP 0x0 >>> +#define AD5360_REG_SF_CTRL 0x1 >>> +#define AD5360_REG_SF_OFS(x) (0x2 + (x)) >>> +#define AD5360_REG_SF_READBACK 0x5 >>> + >>> +#define AD5360_SF_CTRL_PWR_DOWN BIT(0) >>> + >>> +#define AD5360_READBACK_X1A 0x0 >>> +#define AD5360_READBACK_X1B 0x1 >>> +#define AD5360_READBACK_OFFSET 0x2 >>> +#define AD5360_READBACK_GAIN 0x3 >>> +#define AD5360_READBACK_SF 0x4 >>> + >>> + >>> +/** >>> + * struct ad5360_chip_info - chip specific information >>> + * @channel_template: channel specification template >>> + * @num_channels: number of channels >>> + * @channels_per_group: number of channels per group >>> + * @num_vrefs: number of vref supplies for the chip >>> +*/ >>> + >>> +struct ad5360_chip_info { >>> + struct iio_chan_spec channel_template; >>> + unsigned int num_channels; >>> + unsigned int channels_per_group; >>> + unsigned int num_vrefs; >>> +}; >>> + >>> +/** >>> + * struct ad5360_state - driver instance specific data >>> + * @spi: spi_device >>> + * @chip_info: chip model specific constants, available modes etc >>> + * @vref_reg: vref supply regulators >>> + * @ctrl: control register cache >>> + * @data: spi transfer buffers >>> + */ >>> + >>> +struct ad5360_state { >>> + struct spi_device *spi; >>> + const struct ad5360_chip_info *chip_info; >>> + struct regulator_bulk_data vref_reg[3]; >>> + unsigned int ctrl; >>> + >>> + /* >>> + * DMA (thus cache coherency maintenance) requires the >>> + * transfer buffers to live in their own cache lines. >>> + */ >>> + union { >>> + __be32 d32; >>> + u8 d8[4]; >>> + } data[2] ____cacheline_aligned; >>> +}; >>> + >>> +enum ad5360_type { >>> + ID_AD5360, >>> + ID_AD5361, >>> + ID_AD5362, >>> + ID_AD5363, >>> + ID_AD5370, >>> + ID_AD5371, >>> + ID_AD5372, >>> + ID_AD5373, >>> +}; >>> + >>> +#define AD5360_CHANNEL(bits) { \ >>> + .type = IIO_VOLTAGE, \ >>> + .indexed = 1, \ >>> + .output = 1, \ >>> + .info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE) | \ >>> + (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) | \ >>> + (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE) | \ >>> + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE), \ >>> + .scan_type = IIO_ST('u', (bits), 16, 16 - (bits)) \ >>> +} >>> + >>> +static const struct ad5360_chip_info ad5360_chip_info_tbl[] = { >>> + [ID_AD5360] = { >>> + .channel_template = AD5360_CHANNEL(16), >>> + .num_channels = 16, >>> + .channels_per_group = 8, >>> + .num_vrefs = 2, >>> + }, >>> + [ID_AD5361] = { >>> + .channel_template = AD5360_CHANNEL(14), >>> + .num_channels = 16, >>> + .channels_per_group = 8, >>> + .num_vrefs = 2, >>> + }, >>> + [ID_AD5362] = { >>> + .channel_template = AD5360_CHANNEL(16), >>> + .num_channels = 8, >>> + .channels_per_group = 4, >>> + .num_vrefs = 2, >>> + }, >>> + [ID_AD5363] = { >>> + .channel_template = AD5360_CHANNEL(14), >>> + .num_channels = 8, >>> + .channels_per_group = 4, >>> + .num_vrefs = 2, >>> + }, >>> + [ID_AD5370] = { >>> + .channel_template = AD5360_CHANNEL(16), >>> + .num_channels = 40, >>> + .channels_per_group = 8, >>> + .num_vrefs = 2, >>> + }, >>> + [ID_AD5371] = { >>> + .channel_template = AD5360_CHANNEL(14), >>> + .num_channels = 40, >>> + .channels_per_group = 8, >>> + .num_vrefs = 3, >>> + }, >>> + [ID_AD5372] = { >>> + .channel_template = AD5360_CHANNEL(16), >>> + .num_channels = 32, >>> + .channels_per_group = 8, >>> + .num_vrefs = 2, >>> + }, >>> + [ID_AD5373] = { >>> + .channel_template = AD5360_CHANNEL(14), >>> + .num_channels = 32, >>> + .channels_per_group = 8, >>> + .num_vrefs = 2, >>> + }, >>> +}; >>> + >>> +static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st, >>> + unsigned int channel) >>> +{ >>> + unsigned int i; >>> + >>> + /* The first groups have their own vref, while the remaining groups >>> + * share the last vref */ >>> + i = channel / st->chip_info->channels_per_group; >>> + if (i >= st->chip_info->num_vrefs) >>> + i = st->chip_info->num_vrefs - 1; >>> + >>> + return i; >>> +} >>> + >>> +static int ad5360_get_channel_vref(struct ad5360_state *st, >>> + unsigned int channel) >>> +{ >>> + unsigned int i = ad5360_get_channel_vref_index(st, channel); >>> + >>> + return regulator_get_voltage(st->vref_reg[i].consumer); >>> +} >>> + >>> + >>> +static int ad5360_write_unlocked(struct iio_dev *indio_dev, >>> + unsigned int cmd, unsigned int addr, unsigned int val, >>> + unsigned int shift) >>> +{ >>> + struct ad5360_state *st = iio_priv(indio_dev); >>> + >>> + val <<= shift; >>> + val |= AD5360_CMD(cmd) | AD5360_ADDR(addr); >>> + st->data[0].d32 = cpu_to_be32(val); >>> + >>> + return spi_write(st->spi, &st->data[0].d8[1], 3); >>> +} >>> + >>> +static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd, >>> + unsigned int addr, unsigned int val, unsigned int shift) >>> +{ >>> + int ret; >>> + >>> + mutex_lock(&indio_dev->mlock); >>> + ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); >>> + mutex_unlock(&indio_dev->mlock); >>> + >>> + return ret; >>> +} >>> + >>> +static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, >>> + unsigned int addr) >>> +{ >>> + struct ad5360_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, >>> + }, { >>> + .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(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) | >>> + AD5360_ADDR(AD5360_REG_SF_READBACK) | >>> + AD5360_READBACK_TYPE(type) | >>> + AD5360_READBACK_ADDR(addr)); >>> + >>> + 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 ssize_t ad5360_read_dac_powerdown(struct device *dev, >>> + struct device_attribute *attr, >>> + char *buf) >>> +{ >>> + struct iio_dev *indio_dev = dev_get_drvdata(dev); >>> + struct ad5360_state *st = iio_priv(indio_dev); >>> + >>> + return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN)); >>> +} >>> + >>> +static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, >>> + unsigned int clr) >>> +{ >>> + struct ad5360_state *st = iio_priv(indio_dev); >>> + unsigned int ret; >>> + >>> + mutex_lock(&indio_dev->mlock); >>> + >>> + st->ctrl |= set; >>> + st->ctrl &= ~clr; >>> + >>> + ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, >>> + AD5360_REG_SF_CTRL, st->ctrl, 0); >>> + >>> + mutex_unlock(&indio_dev->mlock); >>> + >>> + return ret; >>> +} >>> + >>> +static ssize_t ad5360_write_dac_powerdown(struct device *dev, >>> + struct device_attribute *attr, const char *buf, size_t len) >>> +{ >>> + struct iio_dev *indio_dev = dev_get_drvdata(dev); >>> + bool pwr_down; >>> + int ret; >>> + >>> + ret = strtobool(buf, &pwr_down); >>> + if (ret) >>> + return ret; >>> + >>> + if (pwr_down) >>> + ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0); >>> + else >>> + ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN); >>> + >>> + return ret ? ret : len; >>> +} >>> + >>> +static IIO_DEVICE_ATTR(out_voltage_powerdown, >>> + S_IRUGO | S_IWUSR, >>> + ad5360_read_dac_powerdown, >>> + ad5360_write_dac_powerdown, 0); >>> + >>> +static struct attribute *ad5360_attributes[] = { >>> + &iio_dev_attr_out_voltage_powerdown.dev_attr.attr, >>> + NULL, >>> +}; >>> + >>> +static const struct attribute_group ad5360_attribute_group = { >>> + .attrs = ad5360_attributes, >>> +}; >>> + >>> +static int ad5360_write_raw(struct iio_dev *indio_dev, >>> + struct iio_chan_spec const *chan, >>> + int val, >>> + int val2, >>> + long mask) >>> +{ >>> + struct ad5360_state *st = iio_priv(indio_dev); >>> + int max_val = (1 << chan->scan_type.realbits); >>> + unsigned int ofs_index; >>> + int ret; >>> + >>> + switch (mask) { >>> + case 0: >>> + if (val >= max_val || val < 0) >>> + return -EINVAL; >>> + >>> + ret = ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA, >>> + chan->address, val, >>> + chan->scan_type.shift); >> Given you already return directly on error, I'd just return directly >> on success as well rather than breaking. Slightly cleaner code >> and no need to have ret. >> >>> + break; >>> + case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE): >>> + if (val >= max_val || val < 0) >>> + return -EINVAL; >>> + >>> + ret = ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET, >>> + chan->address, val, >>> + chan->scan_type.shift); >>> + break; >>> + case (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE): >>> + if (val >= max_val || val < 0) >>> + return -EINVAL; >>> + >>> + ret = ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN, >>> + chan->address, val, >>> + chan->scan_type.shift); >>> + break; >>> + case (1 << IIO_CHAN_INFO_OFFSET_SEPARATE): >>> + if (val <= -max_val || val > 0) >>> + return -EINVAL; >>> + >>> + val = -val; >>> + >>> + /* offset is supposed to have the same scale as raw, but it >>> + * is always 14bits wide, so on a chip where the raw value has >>> + * more bits, we need to shift offset. */ >>> + val >>= (chan->scan_type.realbits - 14); >>> + >>> + /* There is one DAC offset register per vref. Changing one >>> + * channels offset will also change the offset for all other >>> + * channels which share the same vref supply. */ >>> + ofs_index = ad5360_get_channel_vref_index(st, chan->channel); >>> + ret = ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, >>> + AD5360_REG_SF_OFS(ofs_index), val, 0); >>> + break; >>> + default: >>> + ret = -EINVAL; >>> + } >>> + >>> + return ret; >>> +} >>> + >>> +static int ad5360_read_raw(struct iio_dev *indio_dev, >>> + struct iio_chan_spec const *chan, >>> + int *val, >>> + int *val2, >>> + long m) >>> +{ >>> + struct ad5360_state *st = iio_priv(indio_dev); >>> + unsigned long scale_uv; >>> + unsigned int ofs_index; >>> + int ret; >>> + >>> + switch (m) { >>> + case 0: >>> + ret = ad5360_read(indio_dev, AD5360_READBACK_X1A, >>> + chan->address); >>> + if (ret < 0) >>> + return ret; >>> + *val = ret >> chan->scan_type.shift; >>> + return IIO_VAL_INT; >>> + case (1 << IIO_CHAN_INFO_SCALE_SEPARATE): >>> + /* vout = 4 * vref * dac_code */ >>> + scale_uv = ad5360_get_channel_vref(st, chan->channel) * 4 * 100; >>> + if (scale_uv < 0) >>> + return scale_uv; >>> + >>> + scale_uv >>= (chan->scan_type.realbits); >>> + *val = scale_uv / 100000; >>> + *val2 = (scale_uv % 100000) * 10; >>> + return IIO_VAL_INT_PLUS_MICRO; >>> + case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE): >>> + ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET, >>> + chan->address); >>> + if (ret < 0) >>> + return ret; >>> + *val = ret; >>> + return IIO_VAL_INT; >>> + case (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE): >>> + ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN, >>> + chan->address); >>> + if (ret < 0) >>> + return ret; >>> + *val = ret; >>> + return IIO_VAL_INT; >>> + case (1 << IIO_CHAN_INFO_OFFSET_SEPARATE): >>> + ofs_index = ad5360_get_channel_vref_index(st, chan->channel); >>> + ret = ad5360_read(indio_dev, AD5360_READBACK_SF, >>> + AD5360_REG_SF_OFS(ofs_index)); >>> + if (ret < 0) >>> + return ret; >>> + >>> + ret <<= (chan->scan_type.realbits - 14); >>> + *val = -ret; >>> + return IIO_VAL_INT; >>> + } >>> + >>> + return -EINVAL; >>> +} >>> + >>> +static const struct iio_info ad5360_info = { >>> + .read_raw = ad5360_read_raw, >>> + .write_raw = ad5360_write_raw, >>> + .attrs = &ad5360_attribute_group, >>> + .driver_module = THIS_MODULE, >>> +}; >>> + >>> +static const char * const ad5360_vref_name[] = { >>> + "vref0", "vref1", "vref2" >>> +}; >>> + >>> +static int __devinit ad5360_alloc_channels(struct iio_dev *indio_dev) >>> +{ >>> + struct ad5360_state *st = iio_priv(indio_dev); >>> + struct iio_chan_spec *channels; >>> + unsigned int i; >>> + >>> + channels = kcalloc(sizeof(struct iio_chan_spec), >>> + st->chip_info->num_channels, GFP_KERNEL); >>> + >>> + if (!channels) >>> + return -ENOMEM; >>> + >>> + for (i = 0; i < st->chip_info->num_channels; ++i) { >>> + channels[i] = st->chip_info->channel_template; >>> + channels[i].channel = i; >>> + channels[i].address = AD5360_CHAN_ADDR(i); >>> + } >>> + >>> + indio_dev->channels = channels; >>> + >>> + return 0; >>> +} >>> + >>> +static int __devinit ad5360_probe(struct spi_device *spi) >>> +{ >>> + enum ad5360_type type = spi_get_device_id(spi)->driver_data; >>> + struct iio_dev *indio_dev; >>> + struct ad5360_state *st; >>> + unsigned int i; >>> + int ret; >>> + >>> + indio_dev = iio_allocate_device(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 = &ad5360_chip_info_tbl[type]; >>> + st->spi = spi; >>> + >>> + indio_dev->dev.parent = &spi->dev; >>> + indio_dev->name = spi_get_device_id(spi)->name; >>> + indio_dev->info = &ad5360_info; >>> + indio_dev->modes = INDIO_DIRECT_MODE; >>> + indio_dev->num_channels = st->chip_info->num_channels; >>> + >>> + ret = ad5360_alloc_channels(indio_dev); >>> + if (ret) { >>> + dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret); >>> + goto error_free; >>> + } >>> + >>> + for (i = 0; i < st->chip_info->num_vrefs; ++i) >>> + st->vref_reg[i].supply = ad5360_vref_name[i]; >>> + >>> + ret = regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs, >>> + st->vref_reg); >>> + if (ret) { >>> + dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret); >>> + goto error_free_channels; >>> + } >>> + >>> + ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg); >>> + if (ret) { >>> + dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret); >>> + goto error_free_reg; >>> + } >>> + >>> + ret = iio_device_register(indio_dev); >>> + if (ret) { >>> + dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); >>> + goto error_disable_reg; >>> + } >>> + >>> + return 0; >>> + >>> +error_disable_reg: >>> + regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); >>> +error_free_reg: >>> + regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); >>> +error_free_channels: >>> + kfree(indio_dev->channels); >>> +error_free: >>> + iio_free_device(indio_dev); >>> + >>> + return ret; >>> +} >>> + >>> +static int __devexit ad5360_remove(struct spi_device *spi) >>> +{ >>> + struct iio_dev *indio_dev = spi_get_drvdata(spi); >>> + struct ad5360_state *st = iio_priv(indio_dev); >>> + >>> + iio_device_unregister(indio_dev); >>> + >>> + kfree(indio_dev->channels); >>> + >>> + regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); >>> + regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); >>> + >>> + iio_free_device(indio_dev); >>> + >>> + return 0; >>> +} >>> + >>> +static const struct spi_device_id ad5360_ids[] = { >>> + { "ad5360", ID_AD5360 }, >>> + { "ad5361", ID_AD5361 }, >>> + { "ad5362", ID_AD5362 }, >>> + { "ad5363", ID_AD5363 }, >>> + { "ad5370", ID_AD5370 }, >>> + { "ad5371", ID_AD5371 }, >>> + { "ad5372", ID_AD5372 }, >>> + { "ad5373", ID_AD5373 }, >>> + {} >>> +}; >>> + >>> +static struct spi_driver ad5360_driver = { >>> + .driver = { >>> + .name = "ad5360", >>> + .owner = THIS_MODULE, >>> + }, >>> + .probe = ad5360_probe, >>> + .remove = __devexit_p(ad5360_remove), >>> + .id_table = ad5360_ids, >>> +}; >>> + >>> +static __init int ad5360_spi_init(void) >>> +{ >>> + return spi_register_driver(&ad5360_driver); >>> +} >>> +module_init(ad5360_spi_init); >>> + >>> +static __exit void ad5360_spi_exit(void) >>> +{ >>> + spi_unregister_driver(&ad5360_driver); >>> +} >>> +module_exit(ad5360_spi_exit); >>> + >>> +MODULE_AUTHOR("Lars-Peter Clausen <lars@xxxxxxxxxx>"); >>> +MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC"); >>> +MODULE_LICENSE("GPL v2"); >> > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html