From 25ffba7a2a8dfbb9549fc54657037fe49ce1fc8a Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Wed, 11 Apr 2018 14:51:51 +0300 Subject: iio:dac:ad5686: Style fixes no functional changes This patch fixes some indentation issues and does not modify the functionality of the driver. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers/iio/dac') diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 20254df7f9c7..f7f975c8d4bd 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -24,7 +24,7 @@ #define AD5686_ADDR(x) ((x) << 16) #define AD5686_CMD(x) ((x) << 20) -#define AD5686_ADDR_DAC(chan) (0x1 << (chan)) +#define AD5686_ADDR_DAC(chan) (0x1 << (chan)) #define AD5686_ADDR_ALL_DAC 0xF #define AD5686_CMD_NOOP 0x0 @@ -137,7 +137,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 +145,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,17 +164,19 @@ 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; @@ -221,10 +224,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; -- cgit From 16a3c17575c41d8f2e8406e89b9f3ba1eaabea19 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Wed, 11 Apr 2018 14:52:12 +0300 Subject: iio:dac:ad5686: Change license description Add GPLv2+ SPDX identifier and remove license notice to keep the whole purpose of using an SPDx id. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio/dac') diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index f7f975c8d4bd..e328513dd4c2 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 -- cgit From f4a27306d48d39a2084ed25a9e3d2bb0833562fd Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Wed, 11 Apr 2018 14:52:31 +0300 Subject: iio:dac:ad5686: Add support for various number of channels This patch adds a new num_channels attribute as part of the ad5686_chip_info struct which replaces the AD5686_DAC_CHANNELS define. This is a necessary step, since this driver should support similar devices which differ only in the number of channels. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'drivers/iio/dac') diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index e328513dd4c2..87d6e6e3d298 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -18,8 +18,6 @@ #include #include -#define AD5686_DAC_CHANNELS 4 - #define AD5686_ADDR(x) ((x) << 16) #define AD5686_CMD(x) ((x) << 20) @@ -45,12 +43,14 @@ /** * struct ad5686_chip_info - chip specific information * @int_vref_mv: AD5620/40/60: the internal reference voltage + * @num_channels: number of channels * @channel: channel specification */ struct ad5686_chip_info { u16 int_vref_mv; - struct iio_chan_spec channel[AD5686_DAC_CHANNELS]; + unsigned int num_channels; + struct iio_chan_spec *channels; }; /** @@ -268,14 +268,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), \ @@ -285,31 +285,36 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .ext_info = ad5686_ext_info, \ } +#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), \ +} + +DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4); +DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); +DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0); + static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { [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, .int_vref_mv = 2500, }, [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), + .channels = ad5685r_channels, .int_vref_mv = 2500, + .num_channels = 4, }, [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, .int_vref_mv = 2500, }, }; - static int ad5686_probe(struct spi_device *spi) { struct ad5686_state *st; @@ -353,8 +358,8 @@ static int ad5686_probe(struct spi_device *spi) indio_dev->name = spi_get_device_id(spi)->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; ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, !!voltage_uv, 0); -- cgit From fe642e2dfc5494d3977041136ef16e0aaaf3ada0 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Wed, 11 Apr 2018 14:52:48 +0300 Subject: iio:dac:ad5686: Add support for AD5685R This driver acctually supports the AD5685R device and NOT the AD5685, which does not exist. Maintain old id entry in case we have devicetrees out there which are using the wrong name. More information can be found in the datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/AD5686_5684.pdf Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/iio/dac') diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 87d6e6e3d298..7a01a09e9d90 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -88,7 +88,7 @@ struct ad5686_state { enum ad5686_supported_device_ids { ID_AD5684, - ID_AD5685, + ID_AD5685R, ID_AD5686, }; static int ad5686_spi_write(struct ad5686_state *st, @@ -303,7 +303,7 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .num_channels = 4, .int_vref_mv = 2500, }, - [ID_AD5685] = { + [ID_AD5685R] = { .channels = ad5685r_channels, .int_vref_mv = 2500, .num_channels = 4, @@ -392,7 +392,8 @@ static int ad5686_remove(struct spi_device *spi) static const struct spi_device_id ad5686_id[] = { {"ad5684", ID_AD5684}, - {"ad5685", ID_AD5685}, + {"ad5685", ID_AD5685R}, /* Does not exist */ + {"ad5685r", ID_AD5685R}, {"ad5686", ID_AD5686}, {} }; -- cgit From 98baf78e6dbee226e6490deafb0e5aca65b861ee Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Wed, 11 Apr 2018 14:53:01 +0300 Subject: iio:dac:ad5686: Add AD5672R/76/76R/84R/86R support The AD5684R/AD5686R are a family of 4 channel DACs with 12-bit, 14-bit and 16-bit precision respectively. The devices come either with a built-in reference or no built-in reference. The AD5672R/AD5676/AD5676R are similar, except that they have 8 channels instead of 4. Datasheets: http://www.analog.com/media/en/technical-documentation/data-sheets/AD5672R_5676R.pdf http://www.analog.com/media/en/technical-documentation/data-sheets/AD5686R_5685R_5684R.pdf Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers/iio/dac') diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 7a01a09e9d90..9e1a6ba32138 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -87,9 +87,14 @@ struct ad5686_state { */ enum ad5686_supported_device_ids { + ID_AD5672R, + ID_AD5676, + ID_AD5676R, ID_AD5684, + ID_AD5684R, ID_AD5685R, ID_AD5686, + ID_AD5686R }; static int ad5686_spi_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val, u8 shift) @@ -293,15 +298,47 @@ static struct iio_chan_spec name[] = { \ 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); static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { + [ID_AD5672R] = { + .channels = ad5672_channels, + .int_vref_mv = 2500, + .num_channels = 8, + }, + [ID_AD5676] = { + .channels = ad5676_channels, + .num_channels = 8, + }, + [ID_AD5676R] = { + .channels = ad5676_channels, + .int_vref_mv = 2500, + .num_channels = 8, + }, [ID_AD5684] = { .channels = ad5684_channels, .num_channels = 4, + }, + [ID_AD5684R] = { + .channels = ad5684_channels, .int_vref_mv = 2500, + .num_channels = 4, }, [ID_AD5685R] = { .channels = ad5685r_channels, @@ -311,7 +348,11 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { [ID_AD5686] = { .channels = ad5686_channels, .num_channels = 4, + }, + [ID_AD5686R] = { + .channels = ad5686_channels, .int_vref_mv = 2500, + .num_channels = 4, }, }; @@ -391,10 +432,15 @@ static int ad5686_remove(struct spi_device *spi) } static const struct spi_device_id ad5686_id[] = { + {"ad5672r", ID_AD5672R}, + {"ad5676", ID_AD5676}, + {"ad5676r", ID_AD5676R}, {"ad5684", ID_AD5684}, + {"ad5684r", ID_AD5684R}, {"ad5685", ID_AD5685R}, /* Does not exist */ {"ad5685r", ID_AD5685R}, {"ad5686", ID_AD5686}, + {"ad5686r", ID_AD5686R}, {} }; MODULE_DEVICE_TABLE(spi, ad5686_id); -- cgit From 0357e488b825313db3d574137337557f404e59ed Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Wed, 11 Apr 2018 14:53:17 +0300 Subject: iio:dac:ad5686: Refactor the driver In this patch restructures the existing ad5686 driver by adding a module for SPI and a header file, while the baseline module deals with the chip-logic. This is a necessary step, as this driver should support in the future similar devices which differ only in the type of interface used (I2C instead of SPI). Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 ++ drivers/iio/dac/Kconfig | 13 ++- drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad5686-spi.c | 93 +++++++++++++++++++++ drivers/iio/dac/ad5686.c | 191 +++++++------------------------------------ drivers/iio/dac/ad5686.h | 114 ++++++++++++++++++++++++++ 6 files changed, 253 insertions(+), 166 deletions(-) create mode 100644 drivers/iio/dac/ad5686-spi.c create mode 100644 drivers/iio/dac/ad5686.h (limited to 'drivers/iio/dac') diff --git a/MAINTAINERS b/MAINTAINERS index 473ac00dcfb4..637e62d5f7ee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -791,6 +791,13 @@ M: Michael Hanselmann S: Supported F: drivers/macintosh/ams/ +ANALOG DEVICES INC AD5686 DRIVER +M: Stefan Popa +L: linux-pm@vger.kernel.org +W: http://ez.analog.com/community/linux-device-drivers +S: Supported +F: drivers/iio/dac/ad5686* + ANALOG DEVICES INC AD9389B DRIVER M: Hans Verkuil L: linux-media@vger.kernel.org diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 965d5c0d2468..7a81f1e11b22 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -131,16 +131,21 @@ config LTC2632 module will be called ltc2632. config AD5686 - tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver" + tristate + +config AD5686_SPI + tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)" depends on SPI + select AD5686 help - Say yes here to build support for Analog Devices AD5686R, AD5685R, - AD5684R, AD5791 Voltage Output Digital to - Analog Converter. + Say yes here to build support for Analog Devices AD5672R, AD5676, + AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R. + Voltage Output Digital to Analog Converter. To compile this driver as a module, choose M here: the module will be called ad5686. + config AD5755 tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver" depends on SPI_MASTER diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 81e710ed7491..07db92e19490 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_AD5761) += ad5761.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o +obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_CIO_DAC) += cio-dac.o diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c new file mode 100644 index 000000000000..6bb09e9259e6 --- /dev/null +++ b/drivers/iio/dac/ad5686-spi.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD5672R, AD5676, AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R + * Digital to analog converters driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include "ad5686.h" + +#include +#include + +static int ad5686_spi_write(struct ad5686_state *st, + u8 cmd, u8 addr, u16 val) +{ + struct spi_device *spi = to_spi_device(st->dev); + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | + AD5686_ADDR(addr) | + val); + + return spi_write(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, + }, + }; + struct spi_device *spi = to_spi_device(st->dev); + 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(spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + return be32_to_cpu(st->data[2].d32); +} + +static int ad5686_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + + return ad5686_probe(&spi->dev, id->driver_data, id->name, + ad5686_spi_write, ad5686_spi_read); +} + +static int ad5686_spi_remove(struct spi_device *spi) +{ + return ad5686_remove(&spi->dev); +} + +static const struct spi_device_id ad5686_spi_id[] = { + {"ad5672r", ID_AD5672R}, + {"ad5676", ID_AD5676}, + {"ad5676r", ID_AD5676R}, + {"ad5684", ID_AD5684}, + {"ad5684r", ID_AD5684R}, + {"ad5685", ID_AD5685R}, /* Does not exist */ + {"ad5685r", ID_AD5685R}, + {"ad5686", ID_AD5686}, + {"ad5686r", ID_AD5686R}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5686_spi_id); + +static struct spi_driver ad5686_spi_driver = { + .driver = { + .name = "ad5686", + }, + .probe = ad5686_spi_probe, + .remove = ad5686_spi_remove, + .id_table = ad5686_spi_id, +}; + +module_spi_driver(ad5686_spi_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 9e1a6ba32138..79abff55a702 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -18,121 +17,7 @@ #include #include -#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 - * @num_channels: number of channels - * @channel: channel specification -*/ - -struct ad5686_chip_info { - u16 int_vref_mv; - unsigned int num_channels; - struct iio_chan_spec *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_AD5672R, - ID_AD5676, - ID_AD5676R, - ID_AD5684, - ID_AD5684R, - ID_AD5685R, - ID_AD5686, - ID_AD5686R -}; -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", @@ -195,8 +80,9 @@ 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); + ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0, + st->pwr_down_mask & st->pwr_down_mode); + return ret ? ret : len; } @@ -213,7 +99,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; @@ -242,11 +128,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: @@ -356,20 +241,27 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { }, }; -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; - 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) @@ -382,28 +274,25 @@ 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; - 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->channels; indio_dev->num_channels = st->chip_info->num_channels; - ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, - !!voltage_uv, 0); + ret = st->write(st, AD5686_CMD_INTERNAL_REFER_SETUP, + 0, !!voltage_uv); if (ret) goto error_disable_reg; @@ -418,10 +307,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); @@ -430,30 +320,7 @@ static int ad5686_remove(struct spi_device *spi) return 0; } - -static const struct spi_device_id ad5686_id[] = { - {"ad5672r", ID_AD5672R}, - {"ad5676", ID_AD5676}, - {"ad5676r", ID_AD5676R}, - {"ad5684", ID_AD5684}, - {"ad5684r", ID_AD5684R}, - {"ad5685", ID_AD5685R}, /* Does not exist */ - {"ad5685r", ID_AD5685R}, - {"ad5686", ID_AD5686}, - {"ad5686r", ID_AD5686R}, - {} -}; -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 "); MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h new file mode 100644 index 000000000000..c8e1565391ca --- /dev/null +++ b/drivers/iio/dac/ad5686.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * This file is part of AD5686 DAC driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#ifndef __DRIVERS_IIO_DAC_AD5686_H__ +#define __DRIVERS_IIO_DAC_AD5686_H__ + +#include +#include +#include +#include + +#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 + +/** + * ad5686_supported_device_ids: + */ +enum ad5686_supported_device_ids { + ID_AD5672R, + ID_AD5676, + ID_AD5676R, + ID_AD5684, + ID_AD5684R, + ID_AD5685R, + ID_AD5686, + ID_AD5686R, +}; + +struct ad5686_state; + +typedef int (*ad5686_write_func)(struct ad5686_state *st, + u8 cmd, u8 addr, u16 val); + +typedef int (*ad5686_read_func)(struct ad5686_state *st, u8 addr); + +/** + * struct ad5686_chip_info - chip specific information + * @int_vref_mv: AD5620/40/60: the internal reference voltage + * @num_channels: number of channels + * @channel: channel specification + */ + +struct ad5686_chip_info { + u16 int_vref_mv; + unsigned int num_channels; + struct iio_chan_spec *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 device *dev; + const struct ad5686_chip_info *chip_info; + struct regulator *reg; + unsigned short vref_mv; + unsigned int pwr_down_mask; + unsigned int pwr_down_mode; + ad5686_write_func write; + ad5686_read_func read; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + + union { + __be32 d32; + __be16 d16; + u8 d8[4]; + } data[3] ____cacheline_aligned; +}; + + +int ad5686_probe(struct device *dev, + enum ad5686_supported_device_ids chip_type, + const char *name, ad5686_write_func write, + ad5686_read_func read); + +int ad5686_remove(struct device *dev); + + +#endif /* __DRIVERS_IIO_DAC_AD5686_H__ */ -- cgit From 4177381b440130ccb686712aaa09b45539114698 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Wed, 11 Apr 2018 14:53:39 +0300 Subject: iio:dac:ad5686: Add AD5671R/75R/94/94R/95R/96/96R support The AD5694/AD5694R/AD5695R/AD5696/AD5696R are a family of 4 channel DACs with 12-bit, 14-bit and 16-bit precision respectively. The devices have either no built-in reference, or built-in 2.5V reference. The AD5671R/AD5675R are similar, except that they have 8 instead of 4 channels. These devices are similar to AD5672R/AD5676/AD5676R and AD5684/AD5684R/AD5684/AD5685R/AD5686/AD5686R, except that they use i2c instead of spi. Datasheets: http://www.analog.com/media/en/technical-documentation/data-sheets/AD5671R_5675R.pdf http://www.analog.com/media/en/technical-documentation/data-sheets/AD5696R_5695R_5694R.pdf Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/dac/Kconfig | 10 +++++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad5686.c | 28 +++++++++++++ drivers/iio/dac/ad5686.h | 7 ++++ drivers/iio/dac/ad5696-i2c.c | 97 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 144 insertions(+) create mode 100644 drivers/iio/dac/ad5696-i2c.c (limited to 'drivers/iio/dac') diff --git a/MAINTAINERS b/MAINTAINERS index 637e62d5f7ee..002cb013b000 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -797,6 +797,7 @@ L: linux-pm@vger.kernel.org W: http://ez.analog.com/community/linux-device-drivers S: Supported F: drivers/iio/dac/ad5686* +F: drivers/iio/dac/ad5696* ANALOG DEVICES INC AD9389B DRIVER M: Hans Verkuil diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 7a81f1e11b22..3ff8a32f1385 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -145,6 +145,16 @@ config AD5686_SPI To compile this driver as a module, choose M here: the module will be called ad5686. +config AD5696_I2C + tristate "Analog Devices AD5696 and similar multi-channel DACs (I2C)" + depends on I2C + select AD5686 + help + Say yes here to build support for Analog Devices AD5671R, AD5675R, + AD5694, AD5694R, AD5695R, AD5696, AD5696R Voltage Output Digital to + Analog Converter. + To compile this driver as a module, choose M here: the module will be + called ad5696. config AD5755 tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver" diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 07db92e19490..4397e2114344 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o +obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_CIO_DAC) += cio-dac.o diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 79abff55a702..89c5f089ae7f 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -202,11 +202,21 @@ DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0); static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { + [ID_AD5671R] = { + .channels = ad5672_channels, + .int_vref_mv = 2500, + .num_channels = 8, + }, [ID_AD5672R] = { .channels = ad5672_channels, .int_vref_mv = 2500, .num_channels = 8, }, + [ID_AD5675R] = { + .channels = ad5676_channels, + .int_vref_mv = 2500, + .num_channels = 8, + }, [ID_AD5676] = { .channels = ad5676_channels, .num_channels = 8, @@ -239,6 +249,24 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .int_vref_mv = 2500, .num_channels = 4, }, + [ID_AD5694] = { + .channels = ad5684_channels, + .num_channels = 4, + }, + [ID_AD5694R] = { + .channels = ad5684_channels, + .int_vref_mv = 2500, + .num_channels = 4, + }, + [ID_AD5696] = { + .channels = ad5686_channels, + .num_channels = 4, + }, + [ID_AD5696R] = { + .channels = ad5686_channels, + .int_vref_mv = 2500, + .num_channels = 4, + }, }; int ad5686_probe(struct device *dev, diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index c8e1565391ca..05f0ce9d2de1 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -39,7 +39,9 @@ * ad5686_supported_device_ids: */ enum ad5686_supported_device_ids { + ID_AD5671R, ID_AD5672R, + ID_AD5675R, ID_AD5676, ID_AD5676R, ID_AD5684, @@ -47,6 +49,11 @@ enum ad5686_supported_device_ids { ID_AD5685R, ID_AD5686, ID_AD5686R, + ID_AD5694, + ID_AD5694R, + ID_AD5695R, + ID_AD5696, + ID_AD5696R, }; struct ad5686_state; diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c new file mode 100644 index 000000000000..275e0321bcf8 --- /dev/null +++ b/drivers/iio/dac/ad5696-i2c.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD5671R, AD5675R, AD5694, AD5694R, AD5695R, AD5696, AD5696R + * Digital to analog converters driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include "ad5686.h" + +#include +#include + +static int ad5686_i2c_read(struct ad5686_state *st, u8 addr) +{ + struct i2c_client *i2c = to_i2c_client(st->dev); + struct i2c_msg msg[2] = { + { + .addr = i2c->addr, + .flags = i2c->flags, + .len = 3, + .buf = &st->data[0].d8[1], + }, + { + .addr = i2c->addr, + .flags = i2c->flags | I2C_M_RD, + .len = 2, + .buf = (char *)&st->data[0].d16, + }, + }; + int ret; + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP) | + AD5686_ADDR(addr) | + 0x00); + + ret = i2c_transfer(i2c->adapter, msg, 2); + if (ret < 0) + return ret; + + return be16_to_cpu(st->data[0].d16); +} + +static int ad5686_i2c_write(struct ad5686_state *st, + u8 cmd, u8 addr, u16 val) +{ + struct i2c_client *i2c = to_i2c_client(st->dev); + int ret; + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr) + | val); + + ret = i2c_master_send(i2c, &st->data[0].d8[1], 3); + if (ret < 0) + return ret; + + return (ret != 3) ? -EIO : 0; +} + +static int ad5686_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + return ad5686_probe(&i2c->dev, id->driver_data, id->name, + ad5686_i2c_write, ad5686_i2c_read); +} + +static int ad5686_i2c_remove(struct i2c_client *i2c) +{ + return ad5686_remove(&i2c->dev); +} + +static const struct i2c_device_id ad5686_i2c_id[] = { + {"ad5671r", ID_AD5671R}, + {"ad5675r", ID_AD5675R}, + {"ad5694", ID_AD5694}, + {"ad5694r", ID_AD5694R}, + {"ad5695r", ID_AD5695R}, + {"ad5696", ID_AD5696}, + {"ad5696r", ID_AD5696R}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); + +static struct i2c_driver ad5686_i2c_driver = { + .driver = { + .name = "ad5696", + }, + .probe = ad5686_i2c_probe, + .remove = ad5686_i2c_remove, + .id_table = ad5686_i2c_id, +}; + +module_i2c_driver(ad5686_i2c_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs"); +MODULE_LICENSE("GPL v2"); -- cgit From df38a4a72a3b9a67118b6143ddc0bc002f430430 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Tue, 1 May 2018 10:15:53 +0200 Subject: iio: dac: add TI DAC5571 family support This patch adds support for the Texas Intruments DAC5571 Family. Signed-off-by: Sean Nyekjaer Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 10 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ti-dac5571.c | 439 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 450 insertions(+) create mode 100644 drivers/iio/dac/ti-dac5571.c (limited to 'drivers/iio/dac') diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 3ff8a32f1385..c77c6cf1d2ba 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -335,6 +335,16 @@ config TI_DAC082S085 If compiled as a module, it will be called ti-dac082s085. +config TI_DAC5571 + tristate "Texas Instruments 8/10/12/16-bit 1/2/4-channel DAC driver" + depends on I2C + help + Driver for the Texas Instruments + DAC5571, DAC6571, DAC7571, DAC5574, DAC6574, DAC7574, DAC5573, + DAC6573, DAC7573, DAC8571, DAC8574. + + If compiled as a module, it will be called ti-dac5571. + config VF610_DAC tristate "Vybrid vf610 DAC driver" depends on OF diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 4397e2114344..57aa230d34ab 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -37,4 +37,5 @@ obj-$(CONFIG_MCP4922) += mcp4922.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o obj-$(CONFIG_STM32_DAC) += stm32-dac.o obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o +obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o obj-$(CONFIG_VF610_DAC) += vf610_dac.o diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c new file mode 100644 index 000000000000..dd21eebed6a8 --- /dev/null +++ b/drivers/iio/dac/ti-dac5571.c @@ -0,0 +1,439 @@ +/* + * ti-dac5571.c - Texas Instruments 8/10/12-bit 1/4-channel DAC driver + * + * Copyright (C) 2018 Prevas A/S + * + * http://www.ti.com/lit/ds/symlink/dac5571.pdf + * http://www.ti.com/lit/ds/symlink/dac6571.pdf + * http://www.ti.com/lit/ds/symlink/dac7571.pdf + * http://www.ti.com/lit/ds/symlink/dac5574.pdf + * http://www.ti.com/lit/ds/symlink/dac6574.pdf + * http://www.ti.com/lit/ds/symlink/dac7574.pdf + * http://www.ti.com/lit/ds/symlink/dac5573.pdf + * http://www.ti.com/lit/ds/symlink/dac6573.pdf + * http://www.ti.com/lit/ds/symlink/dac7573.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2) as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +enum chip_id { + single_8bit, single_10bit, single_12bit, + quad_8bit, quad_10bit, quad_12bit +}; + +struct dac5571_spec { + u8 num_channels; + u8 resolution; +}; + +static const struct dac5571_spec dac5571_spec[] = { + [single_8bit] = {.num_channels = 1, .resolution = 8}, + [single_10bit] = {.num_channels = 1, .resolution = 10}, + [single_12bit] = {.num_channels = 1, .resolution = 12}, + [quad_8bit] = {.num_channels = 4, .resolution = 8}, + [quad_10bit] = {.num_channels = 4, .resolution = 10}, + [quad_12bit] = {.num_channels = 4, .resolution = 12}, +}; + +struct dac5571_data { + struct i2c_client *client; + int id; + struct mutex lock; + struct regulator *vref; + u16 val[4]; + bool powerdown; + u8 powerdown_mode; + struct dac5571_spec const *spec; + int (*dac5571_cmd)(struct dac5571_data *data, int channel, u16 val); + int (*dac5571_pwrdwn)(struct dac5571_data *data, int channel, u8 pwrdwn); + u8 buf[3] ____cacheline_aligned; +}; + +#define DAC5571_POWERDOWN(mode) ((mode) + 1) +#define DAC5571_POWERDOWN_FLAG BIT(0) +#define DAC5571_CHANNEL_SELECT 1 +#define DAC5571_LOADMODE_DIRECT BIT(4) +#define DAC5571_SINGLE_PWRDWN_BITS 4 +#define DAC5571_QUAD_PWRDWN_BITS 6 + +static int dac5571_cmd_single(struct dac5571_data *data, int channel, u16 val) +{ + unsigned int shift; + + shift = 12 - data->spec->resolution; + data->buf[1] = val << shift; + data->buf[0] = val >> (8 - shift); + + if (i2c_master_send(data->client, data->buf, 2) != 2) + return -EIO; + + return 0; +} + +static int dac5571_cmd_quad(struct dac5571_data *data, int channel, u16 val) +{ + unsigned int shift; + + shift = 16 - data->spec->resolution; + data->buf[2] = val << shift; + data->buf[1] = (val >> (8 - shift)); + data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) | + DAC5571_LOADMODE_DIRECT; + + if (i2c_master_send(data->client, data->buf, 3) != 3) + return -EIO; + + return 0; +} + +static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrdwn) +{ + unsigned int shift; + + shift = 12 - data->spec->resolution; + data->buf[1] = 0; + data->buf[0] = pwrdwn << DAC5571_SINGLE_PWRDWN_BITS; + + if (i2c_master_send(data->client, data->buf, 2) != 2) + return -EIO; + + return 0; +} + +static int dac5571_pwrdwn_quad(struct dac5571_data *data, int channel, u8 pwrdwn) +{ + unsigned int shift; + + shift = 16 - data->spec->resolution; + data->buf[2] = 0; + data->buf[1] = pwrdwn << DAC5571_QUAD_PWRDWN_BITS; + data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) | + DAC5571_LOADMODE_DIRECT | DAC5571_POWERDOWN_FLAG; + + if (i2c_master_send(data->client, data->buf, 3) != 3) + return -EIO; + + return 0; +} + +static const char *const dac5571_powerdown_modes[] = { + "1kohm_to_gnd", "100kohm_to_gnd", "three_state", +}; + +static int dac5571_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct dac5571_data *data = iio_priv(indio_dev); + + return data->powerdown_mode; +} + +static int dac5571_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct dac5571_data *data = iio_priv(indio_dev); + int ret = 0; + + if (data->powerdown_mode == mode) + return 0; + + mutex_lock(&data->lock); + if (data->powerdown) { + ret = data->dac5571_pwrdwn(data, chan->channel, + DAC5571_POWERDOWN(mode)); + if (ret) + goto out; + } + data->powerdown_mode = mode; + + out: + mutex_unlock(&data->lock); + + return ret; +} + +static const struct iio_enum dac5571_powerdown_mode = { + .items = dac5571_powerdown_modes, + .num_items = ARRAY_SIZE(dac5571_powerdown_modes), + .get = dac5571_get_powerdown_mode, + .set = dac5571_set_powerdown_mode, +}; + +static ssize_t dac5571_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct dac5571_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", data->powerdown); +} + +static ssize_t dac5571_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct dac5571_data *data = iio_priv(indio_dev); + bool powerdown; + int ret; + + ret = strtobool(buf, &powerdown); + if (ret) + return ret; + + if (data->powerdown == powerdown) + return len; + + mutex_lock(&data->lock); + if (powerdown) + ret = data->dac5571_pwrdwn(data, chan->channel, + DAC5571_POWERDOWN(data->powerdown_mode)); + else + ret = data->dac5571_cmd(data, chan->channel, data->val[0]); + if (ret) + goto out; + + data->powerdown = powerdown; + + out: + mutex_unlock(&data->lock); + + return ret ? ret : len; +} + + +static const struct iio_chan_spec_ext_info dac5571_ext_info[] = { + { + .name = "powerdown", + .read = dac5571_read_powerdown, + .write = dac5571_write_powerdown, + .shared = IIO_SHARED_BY_TYPE, + }, + IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &dac5571_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", &dac5571_powerdown_mode), + {}, +}; + +#define dac5571_CHANNEL(chan, name) { \ + .type = IIO_VOLTAGE, \ + .channel = (chan), \ + .address = (chan), \ + .indexed = true, \ + .output = true, \ + .datasheet_name = name, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = dac5571_ext_info, \ +} + +static const struct iio_chan_spec dac5571_channels[] = { + dac5571_CHANNEL(0, "A"), + dac5571_CHANNEL(1, "B"), + dac5571_CHANNEL(2, "C"), + dac5571_CHANNEL(3, "D"), +}; + +static int dac5571_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dac5571_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = data->val[chan->channel]; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(data->vref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = data->spec->resolution; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static int dac5571_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct dac5571_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (data->val[chan->channel] == val) + return 0; + + if (val >= (1 << data->spec->resolution) || val < 0) + return -EINVAL; + + if (data->powerdown) + return -EBUSY; + + mutex_lock(&data->lock); + ret = data->dac5571_cmd(data, chan->channel, val); + if (ret == 0) + data->val[chan->channel] = val; + mutex_unlock(&data->lock); + return ret; + + default: + return -EINVAL; + } +} + +static int dac5571_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT; +} + +static const struct iio_info dac5571_info = { + .read_raw = dac5571_read_raw, + .write_raw = dac5571_write_raw, + .write_raw_get_fmt = dac5571_write_raw_get_fmt, +}; + +static int dac5571_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct dac5571_spec *spec; + struct dac5571_data *data; + struct iio_dev *indio_dev; + int ret, i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = client->dev.of_node; + indio_dev->info = &dac5571_info; + indio_dev->name = id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dac5571_channels; + + spec = &dac5571_spec[id->driver_data]; + indio_dev->num_channels = spec->num_channels; + data->spec = spec; + + data->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(data->vref)) + return PTR_ERR(data->vref); + + ret = regulator_enable(data->vref); + if (ret < 0) + return ret; + + mutex_init(&data->lock); + + switch (spec->num_channels) { + case 1: + data->dac5571_cmd = dac5571_cmd_single; + data->dac5571_pwrdwn = dac5571_pwrdwn_single; + break; + case 4: + data->dac5571_cmd = dac5571_cmd_quad; + data->dac5571_pwrdwn = dac5571_pwrdwn_quad; + break; + default: + goto err; + } + + for (i = 0; i < spec->num_channels; i++) { + ret = data->dac5571_cmd(data, i, 0); + if (ret) { + dev_err(dev, "failed to initialize channel %d to 0\n", i); + goto err; + } + } + + ret = iio_device_register(indio_dev); + if (ret) + goto err; + + return 0; + + err: + regulator_disable(data->vref); + return ret; +} + +static int dac5571_remove(struct i2c_client *i2c) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(i2c); + struct dac5571_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(data->vref); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id dac5571_of_id[] = { + {.compatible = "ti,dac5571"}, + {.compatible = "ti,dac6571"}, + {.compatible = "ti,dac7571"}, + {.compatible = "ti,dac5574"}, + {.compatible = "ti,dac6574"}, + {.compatible = "ti,dac7574"}, + {.compatible = "ti,dac5573"}, + {.compatible = "ti,dac6573"}, + {.compatible = "ti,dac7573"}, + {} +}; +MODULE_DEVICE_TABLE(of, dac5571_of_id); +#endif + +static const struct i2c_device_id dac5571_id[] = { + {"dac5571", single_8bit}, + {"dac6571", single_10bit}, + {"dac7571", single_12bit}, + {"dac5574", quad_8bit}, + {"dac6574", quad_10bit}, + {"dac7574", quad_12bit}, + {"dac5573", quad_8bit}, + {"dac6573", quad_10bit}, + {"dac7573", quad_12bit}, + {} +}; +MODULE_DEVICE_TABLE(i2c, dac5571_id); + +static struct i2c_driver dac5571_driver = { + .driver = { + .name = "ti-dac5571", + }, + .probe = dac5571_probe, + .remove = dac5571_remove, + .id_table = dac5571_id, +}; +module_i2c_driver(dac5571_driver); + +MODULE_AUTHOR("Sean Nyekjaer "); +MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1/4-channel DAC driver"); +MODULE_LICENSE("GPL v2"); -- cgit