summaryrefslogtreecommitdiff
path: root/drivers/iio/dac
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-03-18 05:13:41 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-18 05:13:41 (GMT)
commit9dffdb38d864ae89e16ff7b3a09451270736e35b (patch)
tree709caf4ff2790a993ca11a40233ab241475451be /drivers/iio/dac
parent10fdfee7f7fd8d4a6a8455ac4c9fbbc51d79b9f7 (diff)
parent2df122bdebaba1daad3a8087b89a5e3456474538 (diff)
downloadlinux-9dffdb38d864ae89e16ff7b3a09451270736e35b.tar.xz
Merge tag 'staging-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging driver updates from Greg KH: "Here is the big staging driver pull request for 4.6-rc1. Lots of little things here, over 1600 patches or so. Notable is all of the good Lustre work happening, those developers have finally woken up and are cleaning up their code greatly. The Outreachy intern application process is also happening, which brought in another 400 or so patches. Full details are in the very long shortlog. All of these have been in linux-next with no reported issues" * tag 'staging-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1673 commits) staging: lustre: fix aligments in lnet selftest staging: lustre: report minimum of two buffers for LNet selftest load test staging: lustre: test for proper errno code in lstcon_rpc_trans_abort staging: lustre: filter remaining extra spacing for lnet selftest staging: lustre: remove extra spacing when setting variable for lnet selftest staging: lustre: remove extra spacing of variable declartions for lnet selftest staging: lustre: fix spacing issues checkpatch reported in lnet selftest staging: lustre: remove returns in void function for lnet selftest staging: lustre: fix bogus lst errors for lnet selftest staging: netlogic: Replacing pr_err with dev_err after the call to devm_kzalloc staging: mt29f_spinand: Replacing pr_info with dev_info after the call to devm_kzalloc staging: android: ion: fix up file mode staging: ion: debugfs invalid gfp mask staging: rts5208: Replace pci_enable_device with pcim_enable_device Staging: ieee80211: Place constant on right side of the test. staging: speakup: Replace del_timer with del_timer_sync staging: lowmemorykiller: fix 2 checks that checkpatch complained staging: mt29f_spinand: Drop void pointer cast staging: rdma: hfi1: file_ops: Replace ALIGN with PAGE_ALIGN staging: rdma: hfi1: driver: Replace IS_ALIGNED with PAGE_ALIGNED ...
Diffstat (limited to 'drivers/iio/dac')
-rw-r--r--drivers/iio/dac/Kconfig39
-rw-r--r--drivers/iio/dac/Makefile3
-rw-r--r--drivers/iio/dac/ad5064.c391
-rw-r--r--drivers/iio/dac/ad5761.c430
-rw-r--r--drivers/iio/dac/mcp4725.c87
-rw-r--r--drivers/iio/dac/stx104.c152
-rw-r--r--drivers/iio/dac/vf610_dac.c298
7 files changed, 1342 insertions, 58 deletions
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index e701e28..a995139 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -10,8 +10,10 @@ config AD5064
depends on (SPI_MASTER && I2C!=m) || I2C
help
Say yes here to build support for Analog Devices AD5024, AD5025, AD5044,
- AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R, AD5648, AD5666, AD5668,
- AD5669R Digital to Analog Converter.
+ AD5045, AD5064, AD5064-1, AD5065, AD5625, AD5625R, AD5627, AD5627R,
+ AD5628, AD5629R, AD5645R, AD5647R, AD5648, AD5665, AD5665R, AD5666,
+ AD5667, AD5667R, AD5668, AD5669R, LTC2606, LTC2607, LTC2609, LTC2616,
+ LTC2617, LTC2619, LTC2626, LTC2627, LTC2629 Digital to Analog Converter.
To compile this driver as a module, choose M here: the
module will be called ad5064.
@@ -111,6 +113,16 @@ config AD5755
To compile this driver as a module, choose M here: the
module will be called ad5755.
+config AD5761
+ tristate "Analog Devices AD5761/61R/21/21R DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5761, AD5761R, AD5721,
+ AD5721R Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5761.
+
config AD5764
tristate "Analog Devices AD5764/64R/44/44R DAC driver"
depends on SPI_MASTER
@@ -176,11 +188,11 @@ config MAX5821
10 bits DAC.
config MCP4725
- tristate "MCP4725 DAC driver"
+ tristate "MCP4725/6 DAC driver"
depends on I2C
---help---
Say Y here if you want to build a driver for the Microchip
- MCP 4725 12-bit digital-to-analog converter (DAC) with I2C
+ MCP 4725/6 12-bit digital-to-analog converter (DAC) with I2C
interface.
To compile this driver as a module, choose M here: the module
@@ -196,4 +208,23 @@ config MCP4922
To compile this driver as a module, choose M here: the module
will be called mcp4922.
+config STX104
+ tristate "Apex Embedded Systems STX104 DAC driver"
+ depends on ISA
+ help
+ Say yes here to build support for the 2-channel DAC on the Apex
+ Embedded Systems STX104 integrated analog PC/104 card. The base port
+ addresses for the devices may be configured via the "base" module
+ parameter array.
+
+config VF610_DAC
+ tristate "Vybrid vf610 DAC driver"
+ depends on OF
+ depends on HAS_IOMEM
+ help
+ Say yes here to support Vybrid board digital-to-analog converter.
+
+ This driver can also be built as a module. If so, the module will
+ be called vf610_dac.
+
endmenu
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 63ae056..67b4842 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_AD5504) += ad5504.o
obj-$(CONFIG_AD5446) += ad5446.o
obj-$(CONFIG_AD5449) += ad5449.o
obj-$(CONFIG_AD5755) += ad5755.o
+obj-$(CONFIG_AD5761) += ad5761.o
obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
@@ -21,3 +22,5 @@ obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4922) += mcp4922.o
+obj-$(CONFIG_STX104) += stx104.o
+obj-$(CONFIG_VF610_DAC) += vf610_dac.o
diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c
index 81ca008..6803e4a 100644
--- a/drivers/iio/dac/ad5064.c
+++ b/drivers/iio/dac/ad5064.c
@@ -1,6 +1,9 @@
/*
- * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R,
- * AD5648, AD5666, AD5668, AD5669R Digital to analog converters driver
+ * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5625, AD5625R,
+ * AD5627, AD5627R, AD5628, AD5629R, AD5645R, AD5647R, AD5648, AD5665, AD5665R,
+ * AD5666, AD5667, AD5667R, AD5668, AD5669R, LTC2606, LTC2607, LTC2609, LTC2616,
+ * LTC2617, LTC2619, LTC2626, LTC2627, LTC2629 Digital to analog converters
+ * driver
*
* Copyright 2011 Analog Devices Inc.
*
@@ -39,6 +42,9 @@
#define AD5064_CMD_RESET 0x7
#define AD5064_CMD_CONFIG 0x8
+#define AD5064_CMD_RESET_V2 0x5
+#define AD5064_CMD_CONFIG_V2 0x7
+
#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1)
#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0)
@@ -48,12 +54,25 @@
#define AD5064_LDAC_PWRDN_3STATE 0x3
/**
+ * enum ad5064_regmap_type - Register layout variant
+ * @AD5064_REGMAP_ADI: Old Analog Devices register map layout
+ * @AD5064_REGMAP_ADI2: New Analog Devices register map layout
+ * @AD5064_REGMAP_LTC: LTC register map layout
+ */
+enum ad5064_regmap_type {
+ AD5064_REGMAP_ADI,
+ AD5064_REGMAP_ADI2,
+ AD5064_REGMAP_LTC,
+};
+
+/**
* struct ad5064_chip_info - chip specific information
* @shared_vref: whether the vref supply is shared between channels
- * @internal_vref: internal reference voltage. 0 if the chip has no internal
- * vref.
+ * @internal_vref: internal reference voltage. 0 if the chip has no
+ internal vref.
* @channel: channel specification
* @num_channels: number of channels
+ * @regmap_type: register map layout variant
*/
struct ad5064_chip_info {
@@ -61,6 +80,7 @@ struct ad5064_chip_info {
unsigned long internal_vref;
const struct iio_chan_spec *channels;
unsigned int num_channels;
+ enum ad5064_regmap_type regmap_type;
};
struct ad5064_state;
@@ -111,18 +131,43 @@ enum ad5064_type {
ID_AD5064,
ID_AD5064_1,
ID_AD5065,
+ ID_AD5625,
+ ID_AD5625R_1V25,
+ ID_AD5625R_2V5,
+ ID_AD5627,
+ ID_AD5627R_1V25,
+ ID_AD5627R_2V5,
ID_AD5628_1,
ID_AD5628_2,
ID_AD5629_1,
ID_AD5629_2,
+ ID_AD5645R_1V25,
+ ID_AD5645R_2V5,
+ ID_AD5647R_1V25,
+ ID_AD5647R_2V5,
ID_AD5648_1,
ID_AD5648_2,
+ ID_AD5665,
+ ID_AD5665R_1V25,
+ ID_AD5665R_2V5,
ID_AD5666_1,
ID_AD5666_2,
+ ID_AD5667,
+ ID_AD5667R_1V25,
+ ID_AD5667R_2V5,
ID_AD5668_1,
ID_AD5668_2,
ID_AD5669_1,
ID_AD5669_2,
+ ID_LTC2606,
+ ID_LTC2607,
+ ID_LTC2609,
+ ID_LTC2616,
+ ID_LTC2617,
+ ID_LTC2619,
+ ID_LTC2626,
+ ID_LTC2627,
+ ID_LTC2629,
};
static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
@@ -136,15 +181,27 @@ static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
const struct iio_chan_spec *chan)
{
- unsigned int val;
+ unsigned int val, address;
+ unsigned int shift;
int ret;
- val = (0x1 << chan->address);
+ if (st->chip_info->regmap_type == AD5064_REGMAP_LTC) {
+ val = 0;
+ address = chan->address;
+ } else {
+ if (st->chip_info->regmap_type == AD5064_REGMAP_ADI2)
+ shift = 4;
+ else
+ shift = 8;
+
+ val = (0x1 << chan->address);
+ address = 0;
- if (st->pwr_down[chan->channel])
- val |= st->pwr_down_mode[chan->channel] << 8;
+ if (st->pwr_down[chan->channel])
+ val |= st->pwr_down_mode[chan->channel] << shift;
+ }
- ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0);
+ ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, address, val, 0);
return ret;
}
@@ -155,6 +212,10 @@ static const char * const ad5064_powerdown_modes[] = {
"three_state",
};
+static const char * const ltc2617_powerdown_modes[] = {
+ "90kohm_to_gnd",
+};
+
static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
@@ -185,6 +246,13 @@ static const struct iio_enum ad5064_powerdown_mode_enum = {
.set = ad5064_set_powerdown_mode,
};
+static const struct iio_enum ltc2617_powerdown_mode_enum = {
+ .items = ltc2617_powerdown_modes,
+ .num_items = ARRAY_SIZE(ltc2617_powerdown_modes),
+ .get = ad5064_get_powerdown_mode,
+ .set = ad5064_set_powerdown_mode,
+};
+
static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
{
@@ -295,7 +363,19 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
{ },
};
-#define AD5064_CHANNEL(chan, addr, bits, _shift) { \
+static const struct iio_chan_spec_ext_info ltc2617_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5064_read_dac_powerdown,
+ .write = ad5064_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ltc2617_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ltc2617_powerdown_mode_enum),
+ { },
+};
+
+#define AD5064_CHANNEL(chan, addr, bits, _shift, _ext_info) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.output = 1, \
@@ -309,145 +389,340 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
.storagebits = 16, \
.shift = (_shift), \
}, \
- .ext_info = ad5064_ext_info, \
+ .ext_info = (_ext_info), \
}
-#define DECLARE_AD5064_CHANNELS(name, bits, shift) \
+#define DECLARE_AD5064_CHANNELS(name, bits, shift, ext_info) \
const struct iio_chan_spec name[] = { \
- AD5064_CHANNEL(0, 0, bits, shift), \
- AD5064_CHANNEL(1, 1, bits, shift), \
- AD5064_CHANNEL(2, 2, bits, shift), \
- AD5064_CHANNEL(3, 3, bits, shift), \
- AD5064_CHANNEL(4, 4, bits, shift), \
- AD5064_CHANNEL(5, 5, bits, shift), \
- AD5064_CHANNEL(6, 6, bits, shift), \
- AD5064_CHANNEL(7, 7, bits, shift), \
+ AD5064_CHANNEL(0, 0, bits, shift, ext_info), \
+ AD5064_CHANNEL(1, 1, bits, shift, ext_info), \
+ AD5064_CHANNEL(2, 2, bits, shift, ext_info), \
+ AD5064_CHANNEL(3, 3, bits, shift, ext_info), \
+ AD5064_CHANNEL(4, 4, bits, shift, ext_info), \
+ AD5064_CHANNEL(5, 5, bits, shift, ext_info), \
+ AD5064_CHANNEL(6, 6, bits, shift, ext_info), \
+ AD5064_CHANNEL(7, 7, bits, shift, ext_info), \
}
-#define DECLARE_AD5065_CHANNELS(name, bits, shift) \
+#define DECLARE_AD5065_CHANNELS(name, bits, shift, ext_info) \
const struct iio_chan_spec name[] = { \
- AD5064_CHANNEL(0, 0, bits, shift), \
- AD5064_CHANNEL(1, 3, bits, shift), \
+ AD5064_CHANNEL(0, 0, bits, shift, ext_info), \
+ AD5064_CHANNEL(1, 3, bits, shift, ext_info), \
}
-static DECLARE_AD5064_CHANNELS(ad5024_channels, 12, 8);
-static DECLARE_AD5064_CHANNELS(ad5044_channels, 14, 6);
-static DECLARE_AD5064_CHANNELS(ad5064_channels, 16, 4);
+static DECLARE_AD5064_CHANNELS(ad5024_channels, 12, 8, ad5064_ext_info);
+static DECLARE_AD5064_CHANNELS(ad5044_channels, 14, 6, ad5064_ext_info);
+static DECLARE_AD5064_CHANNELS(ad5064_channels, 16, 4, ad5064_ext_info);
+
+static DECLARE_AD5065_CHANNELS(ad5025_channels, 12, 8, ad5064_ext_info);
+static DECLARE_AD5065_CHANNELS(ad5045_channels, 14, 6, ad5064_ext_info);
+static DECLARE_AD5065_CHANNELS(ad5065_channels, 16, 4, ad5064_ext_info);
-static DECLARE_AD5065_CHANNELS(ad5025_channels, 12, 8);
-static DECLARE_AD5065_CHANNELS(ad5045_channels, 14, 6);
-static DECLARE_AD5065_CHANNELS(ad5065_channels, 16, 4);
+static DECLARE_AD5064_CHANNELS(ad5629_channels, 12, 4, ad5064_ext_info);
+static DECLARE_AD5064_CHANNELS(ad5645_channels, 14, 2, ad5064_ext_info);
+static DECLARE_AD5064_CHANNELS(ad5669_channels, 16, 0, ad5064_ext_info);
-static DECLARE_AD5064_CHANNELS(ad5629_channels, 12, 4);
-static DECLARE_AD5064_CHANNELS(ad5669_channels, 16, 0);
+static DECLARE_AD5064_CHANNELS(ltc2607_channels, 16, 0, ltc2617_ext_info);
+static DECLARE_AD5064_CHANNELS(ltc2617_channels, 14, 2, ltc2617_ext_info);
+static DECLARE_AD5064_CHANNELS(ltc2627_channels, 12, 4, ltc2617_ext_info);
static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {
[ID_AD5024] = {
.shared_vref = false,
.channels = ad5024_channels,
.num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5025] = {
.shared_vref = false,
.channels = ad5025_channels,
.num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5044] = {
.shared_vref = false,
.channels = ad5044_channels,
.num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5045] = {
.shared_vref = false,
.channels = ad5045_channels,
.num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5064] = {
.shared_vref = false,
.channels = ad5064_channels,
.num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5064_1] = {
.shared_vref = true,
.channels = ad5064_channels,
.num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5065] = {
.shared_vref = false,
.channels = ad5065_channels,
.num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5625] = {
+ .shared_vref = true,
+ .channels = ad5629_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5625R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5629_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5625R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5629_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5627] = {
+ .shared_vref = true,
+ .channels = ad5629_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5627R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5629_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5627R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5629_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5628_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5024_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5628_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5024_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5629_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5629_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5629_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5629_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5645R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5645_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5645R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5645_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5647R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5645_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5647R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5645_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5648_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5044_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5648_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5044_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5665] = {
+ .shared_vref = true,
+ .channels = ad5669_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5665R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5669_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5665R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5669_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5666_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5064_channels,
.num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5666_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5064_channels,
.num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5667] = {
+ .shared_vref = true,
+ .channels = ad5669_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5667R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5669_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5667R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5669_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5668_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5064_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5668_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5064_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5669_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5669_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5669_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5669_channels,
.num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_LTC2606] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2607_channels,
+ .num_channels = 1,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2607] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2607_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2609] = {
+ .shared_vref = false,
+ .internal_vref = 0,
+ .channels = ltc2607_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2616] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2617_channels,
+ .num_channels = 1,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2617] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2617_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2619] = {
+ .shared_vref = false,
+ .internal_vref = 0,
+ .channels = ltc2617_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2626] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2627_channels,
+ .num_channels = 1,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2627] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2627_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2629] = {
+ .shared_vref = false,
+ .internal_vref = 0,
+ .channels = ltc2627_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_LTC,
},
};
@@ -469,6 +744,22 @@ static const char * const ad5064_vref_name(struct ad5064_state *st,
return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref];
}
+static int ad5064_set_config(struct ad5064_state *st, unsigned int val)
+{
+ unsigned int cmd;
+
+ switch (st->chip_info->regmap_type) {
+ case AD5064_REGMAP_ADI2:
+ cmd = AD5064_CMD_CONFIG_V2;
+ break;
+ default:
+ cmd = AD5064_CMD_CONFIG;
+ break;
+ }
+
+ return ad5064_write(st, cmd, 0, val, 0);
+}
+
static int ad5064_probe(struct device *dev, enum ad5064_type type,
const char *name, ad5064_write_func write)
{
@@ -498,8 +789,7 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type,
if (!st->chip_info->internal_vref)
return ret;
st->use_internal_vref = true;
- ret = ad5064_write(st, AD5064_CMD_CONFIG, 0,
- AD5064_CONFIG_INT_VREF_ENABLE, 0);
+ ret = ad5064_set_config(st, AD5064_CONFIG_INT_VREF_ENABLE);
if (ret) {
dev_err(dev, "Failed to enable internal vref: %d\n",
ret);
@@ -628,9 +918,19 @@ static int ad5064_i2c_write(struct ad5064_state *st, unsigned int cmd,
unsigned int addr, unsigned int val)
{
struct i2c_client *i2c = to_i2c_client(st->dev);
+ unsigned int cmd_shift;
int ret;
- st->data.i2c[0] = (cmd << 4) | addr;
+ switch (st->chip_info->regmap_type) {
+ case AD5064_REGMAP_ADI2:
+ cmd_shift = 3;
+ break;
+ default:
+ cmd_shift = 4;
+ break;
+ }
+
+ st->data.i2c[0] = (cmd << cmd_shift) | addr;
put_unaligned_be16(val, &st->data.i2c[1]);
ret = i2c_master_send(i2c, st->data.i2c, 3);
@@ -653,12 +953,35 @@ static int ad5064_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id ad5064_i2c_ids[] = {
+ {"ad5625", ID_AD5625 },
+ {"ad5625r-1v25", ID_AD5625R_1V25 },
+ {"ad5625r-2v5", ID_AD5625R_2V5 },
+ {"ad5627", ID_AD5627 },
+ {"ad5627r-1v25", ID_AD5627R_1V25 },
+ {"ad5627r-2v5", ID_AD5627R_2V5 },
{"ad5629-1", ID_AD5629_1},
{"ad5629-2", ID_AD5629_2},
{"ad5629-3", ID_AD5629_2}, /* similar enough to ad5629-2 */
+ {"ad5645r-1v25", ID_AD5645R_1V25 },
+ {"ad5645r-2v5", ID_AD5645R_2V5 },
+ {"ad5665", ID_AD5665 },
+ {"ad5665r-1v25", ID_AD5665R_1V25 },
+ {"ad5665r-2v5", ID_AD5665R_2V5 },
+ {"ad5667", ID_AD5667 },
+ {"ad5667r-1v25", ID_AD5667R_1V25 },
+ {"ad5667r-2v5", ID_AD5667R_2V5 },
{"ad5669-1", ID_AD5669_1},
{"ad5669-2", ID_AD5669_2},
{"ad5669-3", ID_AD5669_2}, /* similar enough to ad5669-2 */
+ {"ltc2606", ID_LTC2606},
+ {"ltc2607", ID_LTC2607},
+ {"ltc2609", ID_LTC2609},
+ {"ltc2616", ID_LTC2616},
+ {"ltc2617", ID_LTC2617},
+ {"ltc2619", ID_LTC2619},
+ {"ltc2626", ID_LTC2626},
+ {"ltc2627", ID_LTC2627},
+ {"ltc2629", ID_LTC2629},
{}
};
MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids);
diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c
new file mode 100644
index 0000000..d6510d6
--- /dev/null
+++ b/drivers/iio/dac/ad5761.c
@@ -0,0 +1,430 @@
+/*
+ * AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
+ *
+ * Copyright 2016 Qtechnology A/S
+ * 2016 Ricardo Ribalda <ricardo.ribalda@gmail.com>
+ *
+ * Licensed under the GPL-2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/bitops.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_data/ad5761.h>
+
+#define AD5761_ADDR(addr) ((addr & 0xf) << 16)
+#define AD5761_ADDR_NOOP 0x0
+#define AD5761_ADDR_DAC_WRITE 0x3
+#define AD5761_ADDR_CTRL_WRITE_REG 0x4
+#define AD5761_ADDR_SW_DATA_RESET 0x7
+#define AD5761_ADDR_DAC_READ 0xb
+#define AD5761_ADDR_CTRL_READ_REG 0xc
+#define AD5761_ADDR_SW_FULL_RESET 0xf
+
+#define AD5761_CTRL_USE_INTVREF BIT(5)
+#define AD5761_CTRL_ETS BIT(6)
+
+/**
+ * struct ad5761_chip_info - chip specific information
+ * @int_vref: Value of the internal reference voltage in mV - 0 if external
+ * reference voltage is used
+ * @channel: channel specification
+*/
+
+struct ad5761_chip_info {
+ unsigned long int_vref;
+ const struct iio_chan_spec channel;
+};
+
+struct ad5761_range_params {
+ int m;
+ int c;
+};
+
+enum ad5761_supported_device_ids {
+ ID_AD5721,
+ ID_AD5721R,
+ ID_AD5761,
+ ID_AD5761R,
+};
+
+/**
+ * struct ad5761_state - driver instance specific data
+ * @spi: spi_device
+ * @vref_reg: reference voltage regulator
+ * @use_intref: true when the internal voltage reference is used
+ * @vref: actual voltage reference in mVolts
+ * @range: output range mode used
+ * @data: cache aligned spi buffer
+ */
+struct ad5761_state {
+ struct spi_device *spi;
+ struct regulator *vref_reg;
+
+ bool use_intref;
+ int vref;
+ enum ad5761_voltage_range range;
+
+ /*
+ * 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;
+};
+
+static const struct ad5761_range_params ad5761_range_params[] = {
+ [AD5761_VOLTAGE_RANGE_M10V_10V] = {
+ .m = 80,
+ .c = 40,
+ },
+ [AD5761_VOLTAGE_RANGE_0V_10V] = {
+ .m = 40,
+ .c = 0,
+ },
+ [AD5761_VOLTAGE_RANGE_M5V_5V] = {
+ .m = 40,
+ .c = 20,
+ },
+ [AD5761_VOLTAGE_RANGE_0V_5V] = {
+ .m = 20,
+ .c = 0,
+ },
+ [AD5761_VOLTAGE_RANGE_M2V5_7V5] = {
+ .m = 40,
+ .c = 10,
+ },
+ [AD5761_VOLTAGE_RANGE_M3V_3V] = {
+ .m = 24,
+ .c = 12,
+ },
+ [AD5761_VOLTAGE_RANGE_0V_16V] = {
+ .m = 64,
+ .c = 0,
+ },
+ [AD5761_VOLTAGE_RANGE_0V_20V] = {
+ .m = 80,
+ .c = 0,
+ },
+};
+
+static int _ad5761_spi_write(struct ad5761_state *st, u8 addr, u16 val)
+{
+ st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr) | val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5761_spi_write(struct iio_dev *indio_dev, u8 addr, u16 val)
+{
+ struct ad5761_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = _ad5761_spi_write(st, addr, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int _ad5761_spi_read(struct ad5761_state *st, u8 addr, u16 *val)
+{
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .bits_per_word = 8,
+ .len = 3,
+ .cs_change = true,
+ }, {
+ .tx_buf = &st->data[1].d8[1],
+ .rx_buf = &st->data[2].d8[1],
+ .bits_per_word = 8,
+ .len = 3,
+ },
+ };
+
+ st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr));
+ st->data[1].d32 = cpu_to_be32(AD5761_ADDR(AD5761_ADDR_NOOP));
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+
+ *val = be32_to_cpu(st->data[2].d32);
+
+ return ret;
+}
+
+static int ad5761_spi_read(struct iio_dev *indio_dev, u8 addr, u16 *val)
+{
+ struct ad5761_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = _ad5761_spi_read(st, addr, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5761_spi_set_range(struct ad5761_state *st,
+ enum ad5761_voltage_range range)
+{
+ u16 aux;
+ int ret;
+
+ aux = (range & 0x7) | AD5761_CTRL_ETS;
+
+ if (st->use_intref)
+ aux |= AD5761_CTRL_USE_INTVREF;
+
+ ret = _ad5761_spi_write(st, AD5761_ADDR_SW_FULL_RESET, 0);
+ if (ret)
+ return ret;
+
+ ret = _ad5761_spi_write(st, AD5761_ADDR_CTRL_WRITE_REG, aux);
+ if (ret)
+ return ret;
+
+ st->range = range;
+
+ return 0;
+}
+
+static int ad5761_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct ad5761_state *st;
+ int ret;
+ u16 aux;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5761_spi_read(indio_dev, AD5761_ADDR_DAC_READ, &aux);
+ if (ret)
+ return ret;
+ *val = aux >> chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ st = iio_priv(indio_dev);
+ *val = st->vref * ad5761_range_params[st->range].m;
+ *val /= 10;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ st = iio_priv(indio_dev);
+ *val = -(1 << chan->scan_type.realbits);
+ *val *= ad5761_range_params[st->range].c;
+ *val /= ad5761_range_params[st->range].m;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5761_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ u16 aux;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ if (val2 || (val << chan->scan_type.shift) > 0xffff || val < 0)
+ return -EINVAL;
+
+ aux = val << chan->scan_type.shift;
+
+ return ad5761_spi_write(indio_dev, AD5761_ADDR_DAC_WRITE, aux);
+}
+
+static const struct iio_info ad5761_info = {
+ .read_raw = &ad5761_read_raw,
+ .write_raw = &ad5761_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define AD5761_CHAN(_bits) { \
+ .type = IIO_VOLTAGE, \
+ .output = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 16 - (_bits), \
+ }, \
+}
+
+static const struct ad5761_chip_info ad5761_chip_infos[] = {
+ [ID_AD5721] = {
+ .int_vref = 0,
+ .channel = AD5761_CHAN(12),
+ },
+ [ID_AD5721R] = {
+ .int_vref = 2500,
+ .channel = AD5761_CHAN(12),
+ },
+ [ID_AD5761] = {
+ .int_vref = 0,
+ .channel = AD5761_CHAN(16),
+ },
+ [ID_AD5761R] = {
+ .int_vref = 2500,
+ .channel = AD5761_CHAN(16),
+ },
+};
+
+static int ad5761_get_vref(struct ad5761_state *st,
+ const struct ad5761_chip_info *chip_info)
+{
+ int ret;
+
+ st->vref_reg = devm_regulator_get_optional(&st->spi->dev, "vref");
+ if (PTR_ERR(st->vref_reg) == -ENODEV) {
+ /* Use Internal regulator */
+ if (!chip_info->int_vref) {
+ dev_err(&st->spi->dev,
+ "Voltage reference not found\n");
+ return -EIO;
+ }
+
+ st->use_intref = true;
+ st->vref = chip_info->int_vref;
+ return 0;
+ }
+
+ if (IS_ERR(st->vref_reg)) {
+ dev_err(&st->spi->dev,
+ "Error getting voltage reference regulator\n");
+ return PTR_ERR(st->vref_reg);
+ }
+
+ ret = regulator_enable(st->vref_reg);
+ if (ret) {
+ dev_err(&st->spi->dev,
+ "Failed to enable voltage reference\n");
+ return ret;
+ }
+
+ ret = regulator_get_voltage(st->vref_reg);
+ if (ret < 0) {
+ dev_err(&st->spi->dev,
+ "Failed to get voltage reference value\n");
+ goto disable_regulator_vref;
+ }
+
+ if (ret < 2000000 || ret > 3000000) {
+ dev_warn(&st->spi->dev,
+ "Invalid external voltage ref. value %d uV\n", ret);
+ ret = -EIO;
+ goto disable_regulator_vref;
+ }
+
+ st->vref = ret / 1000;
+ st->use_intref = false;
+
+ return 0;
+
+disable_regulator_vref:
+ regulator_disable(st->vref_reg);
+ st->vref_reg = NULL;
+ return ret;
+}
+
+static int ad5761_probe(struct spi_device *spi)
+{
+ struct iio_dev *iio_dev;
+ struct ad5761_state *st;
+ int ret;
+ const struct ad5761_chip_info *chip_info =
+ &ad5761_chip_infos[spi_get_device_id(spi)->driver_data];
+ enum ad5761_voltage_range voltage_range = AD5761_VOLTAGE_RANGE_0V_5V;
+ struct ad5761_platform_data *pdata = dev_get_platdata(&spi->dev);
+
+ iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(iio_dev);
+
+ st->spi = spi;
+ spi_set_drvdata(spi, iio_dev);
+
+ ret = ad5761_get_vref(st, chip_info);
+ if (ret)
+ return ret;
+
+ if (pdata)
+ voltage_range = pdata->voltage_range;
+
+ ret = ad5761_spi_set_range(st, voltage_range);
+ if (ret)
+ goto disable_regulator_err;
+
+ iio_dev->dev.parent = &spi->dev;
+ iio_dev->info = &ad5761_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->channels = &chip_info->channel;
+ iio_dev->num_channels = 1;
+ iio_dev->name = spi_get_device_id(st->spi)->name;
+ ret = iio_device_register(iio_dev);
+ if (ret)
+ goto disable_regulator_err;
+
+ return 0;
+
+disable_regulator_err:
+ if (!IS_ERR_OR_NULL(st->vref_reg))
+ regulator_disable(st->vref_reg);
+
+ return ret;
+}
+
+static int ad5761_remove(struct spi_device *spi)
+{
+ struct iio_dev *iio_dev = spi_get_drvdata(spi);
+ struct ad5761_state *st = iio_priv(iio_dev);
+
+ iio_device_unregister(iio_dev);
+
+ if (!IS_ERR_OR_NULL(st->vref_reg))
+ regulator_disable(st->vref_reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5761_id[] = {
+ {"ad5721", ID_AD5721},
+ {"ad5721r", ID_AD5721R},
+ {"ad5761", ID_AD5761},
+ {"ad5761r", ID_AD5761R},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5761_id);
+
+static struct spi_driver ad5761_driver = {
+ .driver = {
+ .name = "ad5761",
+ },
+ .probe = ad5761_probe,
+ .remove = ad5761_remove,
+ .id_table = ad5761_id,
+};
+module_spi_driver(ad5761_driver);
+
+MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD5721, AD5721R, AD5761, AD5761R driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
index b4dde83..cca935c 100644
--- a/drivers/iio/dac/mcp4725.c
+++ b/drivers/iio/dac/mcp4725.c
@@ -1,5 +1,5 @@
/*
- * mcp4725.c - Support for Microchip MCP4725
+ * mcp4725.c - Support for Microchip MCP4725/6
*
* Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
*
@@ -134,6 +134,12 @@ static const char * const mcp4725_powerdown_modes[] = {
"500kohm_to_gnd"
};
+static const char * const mcp4726_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "125kohm_to_gnd",
+ "640kohm_to_gnd"
+};
+
static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
@@ -182,11 +188,24 @@ static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
return len;
}
-static const struct iio_enum mcp4725_powerdown_mode_enum = {
- .items = mcp4725_powerdown_modes,
- .num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
- .get = mcp4725_get_powerdown_mode,
- .set = mcp4725_set_powerdown_mode,
+enum {
+ MCP4725,
+ MCP4726,
+};
+
+static const struct iio_enum mcp472x_powerdown_mode_enum[] = {
+ [MCP4725] = {
+ .items = mcp4725_powerdown_modes,
+ .num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
+ .get = mcp4725_get_powerdown_mode,
+ .set = mcp4725_set_powerdown_mode,
+ },
+ [MCP4726] = {
+ .items = mcp4726_powerdown_modes,
+ .num_items = ARRAY_SIZE(mcp4726_powerdown_modes),
+ .get = mcp4725_get_powerdown_mode,
+ .set = mcp4725_set_powerdown_mode,
+ },
};
static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
@@ -196,19 +215,46 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
.write = mcp4725_write_powerdown,
.shared = IIO_SEPARATE,
},
- IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4725_powerdown_mode_enum),
- IIO_ENUM_AVAILABLE("powerdown_mode", &mcp4725_powerdown_mode_enum),
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE,
+ &mcp472x_powerdown_mode_enum[MCP4725]),
+ IIO_ENUM_AVAILABLE("powerdown_mode",
+ &mcp472x_powerdown_mode_enum[MCP4725]),
+ { },
+};
+
+static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = mcp4725_read_powerdown,
+ .write = mcp4725_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE,
+ &mcp472x_powerdown_mode_enum[MCP4726]),
+ IIO_ENUM_AVAILABLE("powerdown_mode",
+ &mcp472x_powerdown_mode_enum[MCP4726]),
{ },
};
-static const struct iio_chan_spec mcp4725_channel = {
- .type = IIO_VOLTAGE,
- .indexed = 1,
- .output = 1,
- .channel = 0,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
- .ext_info = mcp4725_ext_info,
+static const struct iio_chan_spec mcp472x_channel[] = {
+ [MCP4725] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = mcp4725_ext_info,
+ },
+ [MCP4726] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = mcp4726_ext_info,
+ },
};
static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
@@ -302,7 +348,7 @@ static int mcp4725_probe(struct i2c_client *client,
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
indio_dev->info = &mcp4725_info;
- indio_dev->channels = &mcp4725_channel;
+ indio_dev->channels = &mcp472x_channel[id->driver_data];
indio_dev->num_channels = 1;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -316,7 +362,7 @@ static int mcp4725_probe(struct i2c_client *client,
}
pd = (inbuf[0] >> 1) & 0x3;
data->powerdown = pd > 0 ? true : false;
- data->powerdown_mode = pd ? pd-1 : 2; /* 500kohm_to_gnd */
+ data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
return iio_device_register(indio_dev);
@@ -329,7 +375,8 @@ static int mcp4725_remove(struct i2c_client *client)
}
static const struct i2c_device_id mcp4725_id[] = {
- { "mcp4725", 0 },
+ { "mcp4725", MCP4725 },
+ { "mcp4726", MCP4726 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mcp4725_id);
@@ -346,5 +393,5 @@ static struct i2c_driver mcp4725_driver = {
module_i2c_driver(mcp4725_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
-MODULE_DESCRIPTION("MCP4725 12-bit DAC");
+MODULE_DESCRIPTION("MCP4725/6 12-bit DAC");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/stx104.c b/drivers/iio/dac/stx104.c
new file mode 100644
index 0000000..174f4b7
--- /dev/null
+++ b/drivers/iio/dac/stx104.c
@@ -0,0 +1,152 @@
+/*
+ * DAC driver for the Apex Embedded Systems STX104
+ * Copyright (C) 2016 William Breathitt Gray
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/isa.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#define STX104_NUM_CHAN 2
+
+#define STX104_CHAN(chan) { \
+ .type = IIO_VOLTAGE, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .indexed = 1, \
+ .output = 1 \
+}
+
+#define STX104_EXTENT 16
+/**
+ * The highest base address possible for an ISA device is 0x3FF; this results in
+ * 1024 possible base addresses. Dividing the number of possible base addresses
+ * by the address extent taken by each device results in the maximum number of
+ * devices on a system.
+ */
+#define MAX_NUM_STX104 (1024 / STX104_EXTENT)
+
+static unsigned base[MAX_NUM_STX104];
+static unsigned num_stx104;
+module_param_array(base, uint, &num_stx104, 0);
+MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
+
+/**
+ * struct stx104_iio - IIO device private data structure
+ * @chan_out_states: channels' output states
+ * @base: base port address of the IIO device
+ */
+struct stx104_iio {
+ unsigned chan_out_states[STX104_NUM_CHAN];
+ unsigned base;
+};
+
+static int stx104_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ struct stx104_iio *const priv = iio_priv(indio_dev);
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ *val = priv->chan_out_states[chan->channel];
+
+ return IIO_VAL_INT;
+}
+
+static int stx104_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct stx104_iio *const priv = iio_priv(indio_dev);
+ const unsigned chan_addr_offset = 2 * chan->channel;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ priv->chan_out_states[chan->channel] = val;
+ outw(val, priv->base + 4 + chan_addr_offset);
+
+ return 0;
+}
+
+static const struct iio_info stx104_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = stx104_read_raw,
+ .write_raw = stx104_write_raw
+};
+
+static const struct iio_chan_spec stx104_channels[STX104_NUM_CHAN] = {
+ STX104_CHAN(0),
+ STX104_CHAN(1)
+};
+
+static int stx104_probe(struct device *dev, unsigned int id)
+{
+ struct iio_dev *indio_dev;
+ struct stx104_iio *priv;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ if (!devm_request_region(dev, base[id], STX104_EXTENT,
+ dev_name(dev))) {
+ dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
+ base[id], base[id] + STX104_EXTENT);
+ return -EBUSY;
+ }
+
+ indio_dev->info = &stx104_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = stx104_channels;
+ indio_dev->num_channels = STX104_NUM_CHAN;
+ indio_dev->name = dev_name(dev);
+
+ priv = iio_priv(indio_dev);
+ priv->base = base[id];
+
+ /* initialize DAC output to 0V */
+ outw(0, base[id] + 4);
+ outw(0, base[id] + 6);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct isa_driver stx104_driver = {
+ .probe = stx104_probe,
+ .driver = {
+ .name = "stx104"
+ }
+};
+
+static void __exit stx104_exit(void)
+{
+ isa_unregister_driver(&stx104_driver);
+}
+
+static int __init stx104_init(void)
+{
+ return isa_register_driver(&stx104_driver, num_stx104);
+}
+
+module_init(stx104_init);
+module_exit(stx104_exit);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("Apex Embedded Systems STX104 DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c
new file mode 100644
index 0000000..c4ec777
--- /dev/null
+++ b/drivers/iio/dac/vf610_dac.c
@@ -0,0 +1,298 @@
+/*
+ * Freescale Vybrid vf610 DAC driver
+ *
+ * Copyright 2016 Toradex AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define VF610_DACx_STATCTRL 0x20
+
+#define VF610_DAC_DACEN BIT(15)
+#define VF610_DAC_DACRFS BIT(14)
+#define VF610_DAC_LPEN BIT(11)
+
+#define VF610_DAC_DAT0(x) ((x) & 0xFFF)
+
+enum vf610_conversion_mode_sel {
+ VF610_DAC_CONV_HIGH_POWER,
+ VF610_DAC_CONV_LOW_POWER,
+};
+
+struct vf610_dac {
+ struct clk *clk;
+ struct device *dev;
+ enum vf610_conversion_mode_sel conv_mode;
+ void __iomem *regs;
+};
+
+static void vf610_dac_init(struct vf610_dac *info)
+{
+ int val;
+
+ info->conv_mode = VF610_DAC_CONV_LOW_POWER;
+ val = VF610_DAC_DACEN | VF610_DAC_DACRFS |
+ VF610_DAC_LPEN;
+ writel(val, info->regs + VF610_DACx_STATCTRL);
+}
+
+static void vf610_dac_exit(struct vf610_dac *info)
+{
+ int val;
+
+ val = readl(info->regs + VF610_DACx_STATCTRL);
+ val &= ~VF610_DAC_DACEN;
+ writel(val, info->regs + VF610_DACx_STATCTRL);
+}
+
+static int vf610_set_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct vf610_dac *info = iio_priv(indio_dev);
+ int val;
+
+ mutex_lock(&indio_dev->mlock);
+ info->conv_mode = mode;
+ val = readl(info->regs + VF610_DACx_STATCTRL);
+ if (mode)
+ val |= VF610_DAC_LPEN;
+ else
+ val &= ~VF610_DAC_LPEN;
+ writel(val, info->regs + VF610_DACx_STATCTRL);
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+
+static int vf610_get_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ return info->conv_mode;
+}
+
+static const char * const vf610_conv_modes[] = { "high-power", "low-power" };
+
+static const struct iio_enum vf610_conversion_mode = {
+ .items = vf610_conv_modes,
+ .num_items = ARRAY_SIZE(vf610_conv_modes),
+ .get = vf610_get_conversion_mode,
+ .set = vf610_set_conversion_mode,
+};
+
+static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
+ IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR,
+ &vf610_conversion_mode),
+ {},
+};
+
+#define VF610_DAC_CHAN(_chan_type) { \
+ .type = (_chan_type), \
+ .output = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .ext_info = vf610_ext_info, \
+}
+
+static const struct iio_chan_spec vf610_dac_iio_channels[] = {
+ VF610_DAC_CHAN(IIO_VOLTAGE),
+};
+
+static int vf610_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = VF610_DAC_DAT0(readl(info->regs));
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * DACRFS is always 1 for valid reference and typical
+ * reference voltage as per Vybrid datasheet is 3.3V
+ * from section 9.1.2.1 of Vybrid datasheet
+ */
+ *val = 3300 /* mV */;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vf610_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2,
+ long mask)
+{
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ writel(VF610_DAC_DAT0(val), info->regs);
+ mutex_unlock(&indio_dev->mlock);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info vf610_dac_iio_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &vf610_read_raw,
+ .write_raw = &vf610_write_raw,
+};
+
+static const struct of_device_id vf610_dac_match[] = {
+ { .compatible = "fsl,vf610-dac", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vf610_dac_match);
+
+static int vf610_dac_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct vf610_dac *info;
+ struct resource *mem;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev,
+ sizeof(struct vf610_dac));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ info->dev = &pdev->dev;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ info->clk = devm_clk_get(&pdev->dev, "dac");
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "Failed getting clock, err = %ld\n",
+ PTR_ERR(info->clk));
+ return PTR_ERR(info->clk);
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &vf610_dac_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = vf610_dac_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels);
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the clock\n");
+ return ret;
+ }
+
+ vf610_dac_init(info);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device\n");
+ goto error_iio_device_register;
+ }
+
+ return 0;
+
+error_iio_device_register:
+ clk_disable_unprepare(info->clk);
+
+ return ret;
+}
+
+static int vf610_dac_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ vf610_dac_exit(info);
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vf610_dac_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ vf610_dac_exit(info);
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+static int vf610_dac_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct vf610_dac *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ return ret;
+
+ vf610_dac_init(info);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume);
+
+static struct platform_driver vf610_dac_driver = {
+ .probe = vf610_dac_probe,
+ .remove = vf610_dac_remove,
+ .driver = {
+ .name = "vf610-dac",
+ .of_match_table = vf610_dac_match,
+ .pm = &vf610_dac_pm_ops,
+ },
+};
+module_platform_driver(vf610_dac_driver);
+
+MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
+MODULE_DESCRIPTION("Freescale VF610 DAC driver");
+MODULE_LICENSE("GPL v2");