| /* |
| * Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373 |
| * multi-channel 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 = 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)) \ |
| } |
| |
| 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; |
| |
| switch (mask) { |
| case 0: |
| if (val >= max_val || val < 0) |
| return -EINVAL; |
| |
| return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA, |
| chan->address, val, chan->scan_type.shift); |
| |
| case IIO_CHAN_INFO_CALIBBIAS: |
| if (val >= max_val || val < 0) |
| return -EINVAL; |
| |
| return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET, |
| chan->address, val, chan->scan_type.shift); |
| |
| case IIO_CHAN_INFO_CALIBSCALE: |
| if (val >= max_val || val < 0) |
| return -EINVAL; |
| |
| return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN, |
| chan->address, val, chan->scan_type.shift); |
| |
| case IIO_CHAN_INFO_OFFSET: |
| 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); |
| return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, |
| AD5360_REG_SF_OFS(ofs_index), val, 0); |
| default: |
| break; |
| } |
| |
| return -EINVAL; |
| } |
| |
| 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 int ofs_index; |
| int scale_uv; |
| 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 IIO_CHAN_INFO_SCALE: |
| /* 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 IIO_CHAN_INFO_CALIBBIAS: |
| ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET, |
| chan->address); |
| if (ret < 0) |
| return ret; |
| *val = ret; |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_CALIBSCALE: |
| ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN, |
| chan->address); |
| if (ret < 0) |
| return ret; |
| *val = ret; |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_OFFSET: |
| 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(st->chip_info->num_channels, |
| sizeof(struct iio_chan_spec), 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 }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(spi, ad5360_ids); |
| |
| static struct spi_driver ad5360_driver = { |
| .driver = { |
| .name = "ad5360", |
| .owner = THIS_MODULE, |
| }, |
| .probe = ad5360_probe, |
| .remove = __devexit_p(ad5360_remove), |
| .id_table = ad5360_ids, |
| }; |
| module_spi_driver(ad5360_driver); |
| |
| MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); |
| MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC"); |
| MODULE_LICENSE("GPL v2"); |