diff options
Diffstat (limited to 'drivers/iio/dac/ad5686.c')
-rw-r--r-- | drivers/iio/dac/ad5686.c | 428 |
1 files changed, 245 insertions, 183 deletions
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 20254df7f9c7..e136f0fd38f0 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * AD5686R, AD5685R, AD5684R Digital to analog converters driver * * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. */ #include <linux/interrupt.h> @@ -11,7 +10,6 @@ #include <linux/device.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> @@ -19,116 +17,7 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#define AD5686_DAC_CHANNELS 4 - -#define AD5686_ADDR(x) ((x) << 16) -#define AD5686_CMD(x) ((x) << 20) - -#define AD5686_ADDR_DAC(chan) (0x1 << (chan)) -#define AD5686_ADDR_ALL_DAC 0xF - -#define AD5686_CMD_NOOP 0x0 -#define AD5686_CMD_WRITE_INPUT_N 0x1 -#define AD5686_CMD_UPDATE_DAC_N 0x2 -#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3 -#define AD5686_CMD_POWERDOWN_DAC 0x4 -#define AD5686_CMD_LDAC_MASK 0x5 -#define AD5686_CMD_RESET 0x6 -#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7 -#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 -#define AD5686_CMD_READBACK_ENABLE 0x9 - -#define AD5686_LDAC_PWRDN_NONE 0x0 -#define AD5686_LDAC_PWRDN_1K 0x1 -#define AD5686_LDAC_PWRDN_100K 0x2 -#define AD5686_LDAC_PWRDN_3STATE 0x3 - -/** - * struct ad5686_chip_info - chip specific information - * @int_vref_mv: AD5620/40/60: the internal reference voltage - * @channel: channel specification -*/ - -struct ad5686_chip_info { - u16 int_vref_mv; - struct iio_chan_spec channel[AD5686_DAC_CHANNELS]; -}; - -/** - * struct ad5446_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator - * @vref_mv: actual reference voltage used - * @pwr_down_mask: power down mask - * @pwr_down_mode: current power down mode - * @data: spi transfer buffers - */ - -struct ad5686_state { - struct spi_device *spi; - const struct ad5686_chip_info *chip_info; - struct regulator *reg; - unsigned short vref_mv; - unsigned pwr_down_mask; - unsigned pwr_down_mode; - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - - union { - __be32 d32; - u8 d8[4]; - } data[3] ____cacheline_aligned; -}; - -/** - * ad5686_supported_device_ids: - */ - -enum ad5686_supported_device_ids { - ID_AD5684, - ID_AD5685, - ID_AD5686, -}; -static int ad5686_spi_write(struct ad5686_state *st, - u8 cmd, u8 addr, u16 val, u8 shift) -{ - val <<= shift; - - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr) | - val); - - return spi_write(st->spi, &st->data[0].d8[1], 3); -} - -static int ad5686_spi_read(struct ad5686_state *st, u8 addr) -{ - 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[2].d8[1], - .len = 3, - }, - }; - int ret; - - st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) | - AD5686_ADDR(addr)); - st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); - - ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); - if (ret < 0) - return ret; - - return be32_to_cpu(st->data[2].d32); -} +#include "ad5686.h" static const char * const ad5686_powerdown_modes[] = { "1kohm_to_gnd", @@ -137,7 +26,7 @@ static const char * const ad5686_powerdown_modes[] = { }; static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) + const struct iio_chan_spec *chan) { struct ad5686_state *st = iio_priv(indio_dev); @@ -145,7 +34,8 @@ static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, } static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) + const struct iio_chan_spec *chan, + unsigned int mode) { struct ad5686_state *st = iio_priv(indio_dev); @@ -163,21 +53,25 @@ static const struct iio_enum ad5686_powerdown_mode_enum = { }; static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) + uintptr_t private, const struct iio_chan_spec *chan, char *buf) { struct ad5686_state *st = iio_priv(indio_dev); return sprintf(buf, "%d\n", !!(st->pwr_down_mask & - (0x3 << (chan->channel * 2)))); + (0x3 << (chan->channel * 2)))); } static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, + size_t len) { bool readin; int ret; struct ad5686_state *st = iio_priv(indio_dev); + unsigned int val, ref_bit_msk; + u8 shift; ret = strtobool(buf, &readin); if (ret) @@ -188,8 +82,28 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, else st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); - ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0, - st->pwr_down_mask & st->pwr_down_mode, 0); + switch (st->chip_info->regmap_type) { + case AD5683_REGMAP: + shift = 13; + ref_bit_msk = AD5683_REF_BIT_MSK; + break; + case AD5686_REGMAP: + shift = 0; + ref_bit_msk = 0; + break; + case AD5693_REGMAP: + shift = 13; + ref_bit_msk = AD5693_REF_BIT_MSK; + break; + default: + return -EINVAL; + } + + val = ((st->pwr_down_mask & st->pwr_down_mode) << shift); + if (!st->use_internal_vref) + val |= ref_bit_msk; + + ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0, val); return ret ? ret : len; } @@ -206,7 +120,7 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: mutex_lock(&indio_dev->mlock); - ret = ad5686_spi_read(st, chan->address); + ret = st->read(st, chan->address); mutex_unlock(&indio_dev->mlock); if (ret < 0) return ret; @@ -221,10 +135,10 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, } static int ad5686_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) { struct ad5686_state *st = iio_priv(indio_dev); int ret; @@ -235,11 +149,10 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, return -EINVAL; mutex_lock(&indio_dev->mlock); - ret = ad5686_spi_write(st, - AD5686_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, - val, - chan->scan_type.shift); + ret = st->write(st, + AD5686_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, + val << chan->scan_type.shift); mutex_unlock(&indio_dev->mlock); break; default: @@ -266,14 +179,14 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { { }, }; -#define AD5868_CHANNEL(chan, bits, _shift) { \ +#define AD5868_CHANNEL(chan, addr, bits, _shift) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .output = 1, \ .channel = chan, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ - .address = AD5686_ADDR_DAC(chan), \ + .address = addr, \ .scan_type = { \ .sign = 'u', \ .realbits = (bits), \ @@ -283,45 +196,191 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .ext_info = ad5686_ext_info, \ } +#define DECLARE_AD5693_CHANNELS(name, bits, _shift) \ +static struct iio_chan_spec name[] = { \ + AD5868_CHANNEL(0, 0, bits, _shift), \ +} + +#define DECLARE_AD5686_CHANNELS(name, bits, _shift) \ +static struct iio_chan_spec name[] = { \ + AD5868_CHANNEL(0, 1, bits, _shift), \ + AD5868_CHANNEL(1, 2, bits, _shift), \ + AD5868_CHANNEL(2, 4, bits, _shift), \ + AD5868_CHANNEL(3, 8, bits, _shift), \ +} + +#define DECLARE_AD5676_CHANNELS(name, bits, _shift) \ +static struct iio_chan_spec name[] = { \ + AD5868_CHANNEL(0, 0, bits, _shift), \ + AD5868_CHANNEL(1, 1, bits, _shift), \ + AD5868_CHANNEL(2, 2, bits, _shift), \ + AD5868_CHANNEL(3, 3, bits, _shift), \ + AD5868_CHANNEL(4, 4, bits, _shift), \ + AD5868_CHANNEL(5, 5, bits, _shift), \ + AD5868_CHANNEL(6, 6, bits, _shift), \ + AD5868_CHANNEL(7, 7, bits, _shift), \ +} + +DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4); +DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0); +DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4); +DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); +DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0); +DECLARE_AD5693_CHANNELS(ad5693_channels, 16, 0); +DECLARE_AD5693_CHANNELS(ad5692r_channels, 14, 2); +DECLARE_AD5693_CHANNELS(ad5691r_channels, 12, 4); + static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { + [ID_AD5671R] = { + .channels = ad5672_channels, + .int_vref_mv = 2500, + .num_channels = 8, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5672R] = { + .channels = ad5672_channels, + .int_vref_mv = 2500, + .num_channels = 8, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5675R] = { + .channels = ad5676_channels, + .int_vref_mv = 2500, + .num_channels = 8, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5676] = { + .channels = ad5676_channels, + .num_channels = 8, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5676R] = { + .channels = ad5676_channels, + .int_vref_mv = 2500, + .num_channels = 8, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5681R] = { + .channels = ad5691r_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5683_REGMAP, + }, + [ID_AD5682R] = { + .channels = ad5692r_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5683_REGMAP, + }, + [ID_AD5683] = { + .channels = ad5693_channels, + .num_channels = 1, + .regmap_type = AD5683_REGMAP, + }, + [ID_AD5683R] = { + .channels = ad5693_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5683_REGMAP, + }, [ID_AD5684] = { - .channel[0] = AD5868_CHANNEL(0, 12, 4), - .channel[1] = AD5868_CHANNEL(1, 12, 4), - .channel[2] = AD5868_CHANNEL(2, 12, 4), - .channel[3] = AD5868_CHANNEL(3, 12, 4), + .channels = ad5684_channels, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5684R] = { + .channels = ad5684_channels, .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, }, - [ID_AD5685] = { - .channel[0] = AD5868_CHANNEL(0, 14, 2), - .channel[1] = AD5868_CHANNEL(1, 14, 2), - .channel[2] = AD5868_CHANNEL(2, 14, 2), - .channel[3] = AD5868_CHANNEL(3, 14, 2), + [ID_AD5685R] = { + .channels = ad5685r_channels, .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, }, [ID_AD5686] = { - .channel[0] = AD5868_CHANNEL(0, 16, 0), - .channel[1] = AD5868_CHANNEL(1, 16, 0), - .channel[2] = AD5868_CHANNEL(2, 16, 0), - .channel[3] = AD5868_CHANNEL(3, 16, 0), + .channels = ad5686_channels, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5686R] = { + .channels = ad5686_channels, + .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5691R] = { + .channels = ad5691r_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5693_REGMAP, + }, + [ID_AD5692R] = { + .channels = ad5692r_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5693_REGMAP, + }, + [ID_AD5693] = { + .channels = ad5693_channels, + .num_channels = 1, + .regmap_type = AD5693_REGMAP, + }, + [ID_AD5693R] = { + .channels = ad5693_channels, .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5693_REGMAP, + }, + [ID_AD5694] = { + .channels = ad5684_channels, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5694R] = { + .channels = ad5684_channels, + .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5696] = { + .channels = ad5686_channels, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, + }, + [ID_AD5696R] = { + .channels = ad5686_channels, + .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, }, }; - -static int ad5686_probe(struct spi_device *spi) +int ad5686_probe(struct device *dev, + enum ad5686_supported_device_ids chip_type, + const char *name, ad5686_write_func write, + ad5686_read_func read) { struct ad5686_state *st; struct iio_dev *indio_dev; - int ret, voltage_uv = 0; + unsigned int val, ref_bit_msk; + u8 cmd; + int ret, i, voltage_uv = 0; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); + dev_set_drvdata(dev, indio_dev); + + st->dev = dev; + st->write = write; + st->read = read; - st->reg = devm_regulator_get_optional(&spi->dev, "vcc"); + st->reg = devm_regulator_get_optional(dev, "vcc"); if (!IS_ERR(st->reg)) { ret = regulator_enable(st->reg); if (ret) @@ -334,28 +393,47 @@ static int ad5686_probe(struct spi_device *spi) voltage_uv = ret; } - st->chip_info = - &ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + st->chip_info = &ad5686_chip_info_tbl[chip_type]; if (voltage_uv) st->vref_mv = voltage_uv / 1000; else st->vref_mv = st->chip_info->int_vref_mv; - st->spi = spi; - /* Set all the power down mode for all channels to 1K pulldown */ - st->pwr_down_mode = 0x55; + for (i = 0; i < st->chip_info->num_channels; i++) + st->pwr_down_mode |= (0x01 << (i * 2)); - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = dev; + indio_dev->name = name; indio_dev->info = &ad5686_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channel; - indio_dev->num_channels = AD5686_DAC_CHANNELS; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + + switch (st->chip_info->regmap_type) { + case AD5683_REGMAP: + cmd = AD5686_CMD_CONTROL_REG; + ref_bit_msk = AD5683_REF_BIT_MSK; + st->use_internal_vref = !voltage_uv; + break; + case AD5686_REGMAP: + cmd = AD5686_CMD_INTERNAL_REFER_SETUP; + ref_bit_msk = 0; + break; + case AD5693_REGMAP: + cmd = AD5686_CMD_CONTROL_REG; + ref_bit_msk = AD5693_REF_BIT_MSK; + st->use_internal_vref = !voltage_uv; + break; + default: + ret = -EINVAL; + goto error_disable_reg; + } + + val = (voltage_uv | ref_bit_msk); - ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, - !!voltage_uv, 0); + ret = st->write(st, cmd, 0, !!val); if (ret) goto error_disable_reg; @@ -370,10 +448,11 @@ error_disable_reg: regulator_disable(st->reg); return ret; } +EXPORT_SYMBOL_GPL(ad5686_probe); -static int ad5686_remove(struct spi_device *spi) +int ad5686_remove(struct device *dev) { - struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5686_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); @@ -382,24 +461,7 @@ static int ad5686_remove(struct spi_device *spi) return 0; } - -static const struct spi_device_id ad5686_id[] = { - {"ad5684", ID_AD5684}, - {"ad5685", ID_AD5685}, - {"ad5686", ID_AD5686}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5686_id); - -static struct spi_driver ad5686_driver = { - .driver = { - .name = "ad5686", - }, - .probe = ad5686_probe, - .remove = ad5686_remove, - .id_table = ad5686_id, -}; -module_spi_driver(ad5686_driver); +EXPORT_SYMBOL_GPL(ad5686_remove); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); |