diff options
Diffstat (limited to 'drivers/iio/dac')
-rw-r--r-- | drivers/iio/dac/Kconfig | 20 | ||||
-rw-r--r-- | drivers/iio/dac/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/dac/ad5446.c | 450 | ||||
-rw-r--r-- | drivers/iio/dac/ad5446.h | 91 | ||||
-rw-r--r-- | drivers/iio/dac/ad5755.c | 650 |
5 files changed, 1005 insertions, 207 deletions
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 1be15fa9d61..b1c0ee5294c 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -57,11 +57,12 @@ config AD5624R_SPI config AD5446 tristate "Analog Devices AD5446 and similar single channel DACs driver" - depends on SPI + depends on (SPI_MASTER || I2C) help - Say yes here to build support for Analog Devices AD5444, AD5446, AD5450, - AD5451, AD5452, AD5453, AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, - AD5611, AD5620, AD5621, AD5640, AD5660, AD5662 DACs. + Say yes here to build support for Analog Devices AD5300, AD5301, AD5310, + AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453, + AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612, + AD5620, AD5621, AD5622, AD5640, AD5660, AD5662 DACs. To compile this driver as a module, choose M here: the module will be called ad5446. @@ -76,6 +77,17 @@ config AD5504 To compile this driver as a module, choose M here: the module will be called ad5504. +config AD5755 + tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD5755, AD5755-1, + AD5757, AD5735, AD5737 quad channel Digital to + Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5755. + config AD5764 tristate "Analog Devices AD5764/64R/44/44R DAC driver" depends on SPI_MASTER diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 9ea3ceeefc0..c0d333b23ba 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o obj-$(CONFIG_AD5064) += ad5064.o obj-$(CONFIG_AD5504) += ad5504.o obj-$(CONFIG_AD5446) += ad5446.o +obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 2ca5059ef89..3310cbbd41e 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -14,6 +14,7 @@ #include <linux/sysfs.h> #include <linux/list.h> #include <linux/spi/spi.h> +#include <linux/i2c.h> #include <linux/regulator/consumer.h> #include <linux/err.h> #include <linux/module.h> @@ -21,24 +22,40 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include "ad5446.h" +#define MODE_PWRDWN_1k 0x1 +#define MODE_PWRDWN_100k 0x2 +#define MODE_PWRDWN_TRISTATE 0x3 -static int ad5446_write(struct ad5446_state *st, unsigned val) -{ - __be16 data = cpu_to_be16(val); - return spi_write(st->spi, &data, sizeof(data)); -} +/** + * 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 + */ -static int ad5660_write(struct ad5446_state *st, unsigned val) -{ - uint8_t data[3]; +struct ad5446_state { + struct device *dev; + const struct ad5446_chip_info *chip_info; + struct regulator *reg; + unsigned short vref_mv; + unsigned cached_val; + unsigned pwr_down_mode; + unsigned pwr_down; +}; - data[0] = (val >> 16) & 0xFF; - data[1] = (val >> 8) & 0xFF; - data[2] = val & 0xFF; +/** + * struct ad5446_chip_info - chip specific information + * @channel: channel spec for the DAC + * @int_vref_mv: AD5620/40/60: the internal reference voltage + * @write: chip specific helper function to write to the register + */ - return spi_write(st->spi, data, sizeof(data)); -} +struct ad5446_chip_info { + struct iio_chan_spec channel; + u16 int_vref_mv; + int (*write)(struct ad5446_state *st, unsigned val); +}; static const char * const ad5446_powerdown_modes[] = { "1kohm_to_gnd", "100kohm_to_gnd", "three_state" @@ -110,7 +127,7 @@ static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, return ret ? ret : len; } -static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = { +static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = { { .name = "powerdown", .read = ad5446_read_dac_powerdown, @@ -136,84 +153,7 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = { _AD5446_CHANNEL(bits, storage, shift, NULL) #define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \ - _AD5446_CHANNEL(bits, storage, shift, ad5064_ext_info_powerdown) - -static const struct ad5446_chip_info ad5446_chip_info_tbl[] = { - [ID_AD5444] = { - .channel = AD5446_CHANNEL(12, 16, 2), - .write = ad5446_write, - }, - [ID_AD5446] = { - .channel = AD5446_CHANNEL(14, 16, 0), - .write = ad5446_write, - }, - [ID_AD5450] = { - .channel = AD5446_CHANNEL(8, 16, 6), - .write = ad5446_write, - }, - [ID_AD5451] = { - .channel = AD5446_CHANNEL(10, 16, 4), - .write = ad5446_write, - }, - [ID_AD5541A] = { - .channel = AD5446_CHANNEL(16, 16, 0), - .write = ad5446_write, - }, - [ID_AD5512A] = { - .channel = AD5446_CHANNEL(12, 16, 4), - .write = ad5446_write, - }, - [ID_AD5553] = { - .channel = AD5446_CHANNEL(14, 16, 0), - .write = ad5446_write, - }, - [ID_AD5601] = { - .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6), - .write = ad5446_write, - }, - [ID_AD5611] = { - .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4), - .write = ad5446_write, - }, - [ID_AD5621] = { - .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), - .write = ad5446_write, - }, - [ID_AD5620_2500] = { - .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), - .int_vref_mv = 2500, - .write = ad5446_write, - }, - [ID_AD5620_1250] = { - .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), - .int_vref_mv = 1250, - .write = ad5446_write, - }, - [ID_AD5640_2500] = { - .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), - .int_vref_mv = 2500, - .write = ad5446_write, - }, - [ID_AD5640_1250] = { - .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), - .int_vref_mv = 1250, - .write = ad5446_write, - }, - [ID_AD5660_2500] = { - .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), - .int_vref_mv = 2500, - .write = ad5660_write, - }, - [ID_AD5660_1250] = { - .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), - .int_vref_mv = 1250, - .write = ad5660_write, - }, - [ID_AD5662] = { - .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), - .write = ad5660_write, - }, -}; + _AD5446_CHANNEL(bits, storage, shift, ad5446_ext_info_powerdown) static int ad5446_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -272,14 +212,15 @@ static const struct iio_info ad5446_info = { .driver_module = THIS_MODULE, }; -static int __devinit ad5446_probe(struct spi_device *spi) +static int __devinit ad5446_probe(struct device *dev, const char *name, + const struct ad5446_chip_info *chip_info) { struct ad5446_state *st; struct iio_dev *indio_dev; struct regulator *reg; int ret, voltage_uv = 0; - reg = regulator_get(&spi->dev, "vcc"); + reg = regulator_get(dev, "vcc"); if (!IS_ERR(reg)) { ret = regulator_enable(reg); if (ret) @@ -294,16 +235,15 @@ static int __devinit ad5446_probe(struct spi_device *spi) goto error_disable_reg; } st = iio_priv(indio_dev); - st->chip_info = - &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + st->chip_info = chip_info; - spi_set_drvdata(spi, indio_dev); + dev_set_drvdata(dev, indio_dev); st->reg = reg; - st->spi = spi; + st->dev = dev; - /* Establish that the iio_dev is a child of the spi device */ - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; + /* Establish that the iio_dev is a child of the device */ + indio_dev->dev.parent = dev; + indio_dev->name = name; indio_dev->info = &ad5446_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = &st->chip_info->channel; @@ -316,7 +256,7 @@ static int __devinit ad5446_probe(struct spi_device *spi) else if (voltage_uv) st->vref_mv = voltage_uv / 1000; else - dev_warn(&spi->dev, "reference voltage unspecified\n"); + dev_warn(dev, "reference voltage unspecified\n"); ret = iio_device_register(indio_dev); if (ret) @@ -336,9 +276,9 @@ error_put_reg: return ret; } -static int ad5446_remove(struct spi_device *spi) +static int ad5446_remove(struct device *dev) { - struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5446_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); @@ -351,7 +291,151 @@ static int ad5446_remove(struct spi_device *spi) return 0; } -static const struct spi_device_id ad5446_id[] = { +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static int ad5446_write(struct ad5446_state *st, unsigned val) +{ + struct spi_device *spi = to_spi_device(st->dev); + __be16 data = cpu_to_be16(val); + + return spi_write(spi, &data, sizeof(data)); +} + +static int ad5660_write(struct ad5446_state *st, unsigned val) +{ + struct spi_device *spi = to_spi_device(st->dev); + uint8_t data[3]; + + data[0] = (val >> 16) & 0xFF; + data[1] = (val >> 8) & 0xFF; + data[2] = val & 0xFF; + + return spi_write(spi, data, sizeof(data)); +} + +/** + * ad5446_supported_spi_device_ids: + * The AD5620/40/60 parts are available in different fixed internal reference + * voltage options. The actual part numbers may look differently + * (and a bit cryptic), however this style is used to make clear which + * parts are supported here. + */ +enum ad5446_supported_spi_device_ids { + ID_AD5300, + ID_AD5310, + ID_AD5320, + ID_AD5444, + ID_AD5446, + ID_AD5450, + ID_AD5451, + ID_AD5541A, + ID_AD5512A, + ID_AD5553, + ID_AD5601, + ID_AD5611, + ID_AD5621, + ID_AD5620_2500, + ID_AD5620_1250, + ID_AD5640_2500, + ID_AD5640_1250, + ID_AD5660_2500, + ID_AD5660_1250, + ID_AD5662, +}; + +static const struct ad5446_chip_info ad5446_spi_chip_info[] = { + [ID_AD5300] = { + .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4), + .write = ad5446_write, + }, + [ID_AD5310] = { + .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2), + .write = ad5446_write, + }, + [ID_AD5320] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0), + .write = ad5446_write, + }, + [ID_AD5444] = { + .channel = AD5446_CHANNEL(12, 16, 2), + .write = ad5446_write, + }, + [ID_AD5446] = { + .channel = AD5446_CHANNEL(14, 16, 0), + .write = ad5446_write, + }, + [ID_AD5450] = { + .channel = AD5446_CHANNEL(8, 16, 6), + .write = ad5446_write, + }, + [ID_AD5451] = { + .channel = AD5446_CHANNEL(10, 16, 4), + .write = ad5446_write, + }, + [ID_AD5541A] = { + .channel = AD5446_CHANNEL(16, 16, 0), + .write = ad5446_write, + }, + [ID_AD5512A] = { + .channel = AD5446_CHANNEL(12, 16, 4), + .write = ad5446_write, + }, + [ID_AD5553] = { + .channel = AD5446_CHANNEL(14, 16, 0), + .write = ad5446_write, + }, + [ID_AD5601] = { + .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6), + .write = ad5446_write, + }, + [ID_AD5611] = { + .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4), + .write = ad5446_write, + }, + [ID_AD5621] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), + .write = ad5446_write, + }, + [ID_AD5620_2500] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), + .int_vref_mv = 2500, + .write = ad5446_write, + }, + [ID_AD5620_1250] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), + .int_vref_mv = 1250, + .write = ad5446_write, + }, + [ID_AD5640_2500] = { + .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), + .int_vref_mv = 2500, + .write = ad5446_write, + }, + [ID_AD5640_1250] = { + .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), + .int_vref_mv = 1250, + .write = ad5446_write, + }, + [ID_AD5660_2500] = { + .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), + .int_vref_mv = 2500, + .write = ad5660_write, + }, + [ID_AD5660_1250] = { + .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), + .int_vref_mv = 1250, + .write = ad5660_write, + }, + [ID_AD5662] = { + .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), + .write = ad5660_write, + }, +}; + +static const struct spi_device_id ad5446_spi_ids[] = { + {"ad5300", ID_AD5300}, + {"ad5310", ID_AD5310}, + {"ad5320", ID_AD5320}, {"ad5444", ID_AD5444}, {"ad5446", ID_AD5446}, {"ad5450", ID_AD5450}, @@ -375,18 +459,160 @@ static const struct spi_device_id ad5446_id[] = { {"ad5662", ID_AD5662}, {} }; -MODULE_DEVICE_TABLE(spi, ad5446_id); +MODULE_DEVICE_TABLE(spi, ad5446_spi_ids); + +static int __devinit ad5446_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); -static struct spi_driver ad5446_driver = { + return ad5446_probe(&spi->dev, id->name, + &ad5446_spi_chip_info[id->driver_data]); +} + +static int __devexit ad5446_spi_remove(struct spi_device *spi) +{ + return ad5446_remove(&spi->dev); +} + +static struct spi_driver ad5446_spi_driver = { .driver = { .name = "ad5446", .owner = THIS_MODULE, }, - .probe = ad5446_probe, - .remove = __devexit_p(ad5446_remove), - .id_table = ad5446_id, + .probe = ad5446_spi_probe, + .remove = __devexit_p(ad5446_spi_remove), + .id_table = ad5446_spi_ids, }; -module_spi_driver(ad5446_driver); + +static int __init ad5446_spi_register_driver(void) +{ + return spi_register_driver(&ad5446_spi_driver); +} + +static void ad5446_spi_unregister_driver(void) +{ + spi_unregister_driver(&ad5446_spi_driver); +} + +#else + +static inline int ad5446_spi_register_driver(void) { return 0; } +static inline void ad5446_spi_unregister_driver(void) { } + +#endif + +#if IS_ENABLED(CONFIG_I2C) + +static int ad5622_write(struct ad5446_state *st, unsigned val) +{ + struct i2c_client *client = to_i2c_client(st->dev); + __be16 data = cpu_to_be16(val); + + return i2c_master_send(client, (char *)&data, sizeof(data)); +} + +/** + * ad5446_supported_i2c_device_ids: + * The AD5620/40/60 parts are available in different fixed internal reference + * voltage options. The actual part numbers may look differently + * (and a bit cryptic), however this style is used to make clear which + * parts are supported here. + */ +enum ad5446_supported_i2c_device_ids { + ID_AD5602, + ID_AD5612, + ID_AD5622, +}; + +static const struct ad5446_chip_info ad5446_i2c_chip_info[] = { + [ID_AD5602] = { + .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4), + .write = ad5622_write, + }, + [ID_AD5612] = { + .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2), + .write = ad5622_write, + }, + [ID_AD5622] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0), + .write = ad5622_write, + }, +}; + +static int __devinit ad5446_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + return ad5446_probe(&i2c->dev, id->name, + &ad5446_i2c_chip_info[id->driver_data]); +} + +static int __devexit ad5446_i2c_remove(struct i2c_client *i2c) +{ + return ad5446_remove(&i2c->dev); +} + +static const struct i2c_device_id ad5446_i2c_ids[] = { + {"ad5301", ID_AD5602}, + {"ad5311", ID_AD5612}, + {"ad5321", ID_AD5622}, + {"ad5602", ID_AD5602}, + {"ad5612", ID_AD5612}, + {"ad5622", ID_AD5622}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids); + +static struct i2c_driver ad5446_i2c_driver = { + .driver = { + .name = "ad5446", + .owner = THIS_MODULE, + }, + .probe = ad5446_i2c_probe, + .remove = __devexit_p(ad5446_i2c_remove), + .id_table = ad5446_i2c_ids, +}; + +static int __init ad5446_i2c_register_driver(void) +{ + return i2c_add_driver(&ad5446_i2c_driver); +} + +static void __exit ad5446_i2c_unregister_driver(void) +{ + i2c_del_driver(&ad5446_i2c_driver); +} + +#else + +static inline int ad5446_i2c_register_driver(void) { return 0; } +static inline void ad5446_i2c_unregister_driver(void) { } + +#endif + +static int __init ad5446_init(void) +{ + int ret; + + ret = ad5446_spi_register_driver(); + if (ret) + return ret; + + ret = ad5446_i2c_register_driver(); + if (ret) { + ad5446_spi_unregister_driver(); + return ret; + } + + return 0; +} +module_init(ad5446_init); + +static void __exit ad5446_exit(void) +{ + ad5446_i2c_unregister_driver(); + ad5446_spi_unregister_driver(); +} +module_exit(ad5446_exit); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); diff --git a/drivers/iio/dac/ad5446.h b/drivers/iio/dac/ad5446.h deleted file mode 100644 index 2934269a56d..00000000000 --- a/drivers/iio/dac/ad5446.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * AD5446 SPI DAC driver - * - * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ -#ifndef IIO_DAC_AD5446_H_ -#define IIO_DAC_AD5446_H_ - -/* DAC Control Bits */ - -#define AD5446_LOAD (0x0 << 14) /* Load and update */ -#define AD5446_SDO_DIS (0x1 << 14) /* Disable SDO */ -#define AD5446_NOP (0x2 << 14) /* No operation */ -#define AD5446_CLK_RISING (0x3 << 14) /* Clock data on rising edge */ - -#define AD5620_LOAD (0x0 << 14) /* Load and update Norm Operation*/ -#define AD5620_PWRDWN_1k (0x1 << 14) /* Power-down: 1kOhm to GND */ -#define AD5620_PWRDWN_100k (0x2 << 14) /* Power-down: 100kOhm to GND */ -#define AD5620_PWRDWN_TRISTATE (0x3 << 14) /* Power-down: Three-state */ - -#define AD5660_LOAD (0x0 << 16) /* Load and update Norm Operation*/ -#define AD5660_PWRDWN_1k (0x1 << 16) /* Power-down: 1kOhm to GND */ -#define AD5660_PWRDWN_100k (0x2 << 16) /* Power-down: 100kOhm to GND */ -#define AD5660_PWRDWN_TRISTATE (0x3 << 16) /* Power-down: Three-state */ - -#define MODE_PWRDWN_1k 0x1 -#define MODE_PWRDWN_100k 0x2 -#define MODE_PWRDWN_TRISTATE 0x3 - -/** - * 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 - */ - -struct ad5446_state { - struct spi_device *spi; - const struct ad5446_chip_info *chip_info; - struct regulator *reg; - unsigned short vref_mv; - unsigned cached_val; - unsigned pwr_down_mode; - unsigned pwr_down; -}; - -/** - * struct ad5446_chip_info - chip specific information - * @channel: channel spec for the DAC - * @int_vref_mv: AD5620/40/60: the internal reference voltage - * @write: chip specific helper function to write to the register - */ - -struct ad5446_chip_info { - struct iio_chan_spec channel; - u16 int_vref_mv; - int (*write)(struct ad5446_state *st, unsigned val); -}; - -/** - * ad5446_supported_device_ids: - * The AD5620/40/60 parts are available in different fixed internal reference - * voltage options. The actual part numbers may look differently - * (and a bit cryptic), however this style is used to make clear which - * parts are supported here. - */ - -enum ad5446_supported_device_ids { - ID_AD5444, - ID_AD5446, - ID_AD5450, - ID_AD5451, - ID_AD5541A, - ID_AD5512A, - ID_AD5553, - ID_AD5601, - ID_AD5611, - ID_AD5621, - ID_AD5620_2500, - ID_AD5620_1250, - ID_AD5640_2500, - ID_AD5640_1250, - ID_AD5660_2500, - ID_AD5660_1250, - ID_AD5662, -}; - -#endif /* IIO_DAC_AD5446_H_ */ diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c new file mode 100644 index 00000000000..5db3506034c --- /dev/null +++ b/drivers/iio/dac/ad5755.c @@ -0,0 +1,650 @@ +/* + * AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/platform_data/ad5755.h> + +#define AD5755_NUM_CHANNELS 4 + +#define AD5755_ADDR(x) ((x) << 16) + +#define AD5755_WRITE_REG_DATA(chan) (chan) +#define AD5755_WRITE_REG_GAIN(chan) (0x08 | (chan)) +#define AD5755_WRITE_REG_OFFSET(chan) (0x10 | (chan)) +#define AD5755_WRITE_REG_CTRL(chan) (0x1c | (chan)) + +#define AD5755_READ_REG_DATA(chan) (chan) +#define AD5755_READ_REG_CTRL(chan) (0x4 | (chan)) +#define AD5755_READ_REG_GAIN(chan) (0x8 | (chan)) +#define AD5755_READ_REG_OFFSET(chan) (0xc | (chan)) +#define AD5755_READ_REG_CLEAR(chan) (0x10 | (chan)) +#define AD5755_READ_REG_SLEW(chan) (0x14 | (chan)) +#define AD5755_READ_REG_STATUS 0x18 +#define AD5755_READ_REG_MAIN 0x19 +#define AD5755_READ_REG_DC_DC 0x1a + +#define AD5755_CTRL_REG_SLEW 0x0 +#define AD5755_CTRL_REG_MAIN 0x1 +#define AD5755_CTRL_REG_DAC 0x2 +#define AD5755_CTRL_REG_DC_DC 0x3 +#define AD5755_CTRL_REG_SW 0x4 + +#define AD5755_READ_FLAG 0x800000 + +#define AD5755_NOOP 0x1CE000 + +#define AD5755_DAC_INT_EN BIT(8) +#define AD5755_DAC_CLR_EN BIT(7) +#define AD5755_DAC_OUT_EN BIT(6) +#define AD5755_DAC_INT_CURRENT_SENSE_RESISTOR BIT(5) +#define AD5755_DAC_DC_DC_EN BIT(4) +#define AD5755_DAC_VOLTAGE_OVERRANGE_EN BIT(3) + +#define AD5755_DC_DC_MAXV 0 +#define AD5755_DC_DC_FREQ_SHIFT 2 +#define AD5755_DC_DC_PHASE_SHIFT 4 +#define AD5755_EXT_DC_DC_COMP_RES BIT(6) + +#define AD5755_SLEW_STEP_SIZE_SHIFT 0 +#define AD5755_SLEW_RATE_SHIFT 3 +#define AD5755_SLEW_ENABLE BIT(12) + +/** + * struct ad5755_chip_info - chip specific information + * @channel_template: channel specification + * @calib_shift: shift for the calibration data registers + * @has_voltage_out: whether the chip has voltage outputs + */ +struct ad5755_chip_info { + const struct iio_chan_spec channel_template; + unsigned int calib_shift; + bool has_voltage_out; +}; + +/** + * struct ad5755_state - driver instance specific data + * @spi: spi device the driver is attached to + * @chip_info: chip model specific constants, available modes etc + * @pwr_down: bitmask which contains hether a channel is powered down or not + * @ctrl: software shadow of the channel ctrl registers + * @channels: iio channel spec for the device + * @data: spi transfer buffers + */ +struct ad5755_state { + struct spi_device *spi; + const struct ad5755_chip_info *chip_info; + unsigned int pwr_down; + unsigned int ctrl[AD5755_NUM_CHANNELS]; + struct iio_chan_spec channels[AD5755_NUM_CHANNELS]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + + union { + u32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +enum ad5755_type { + ID_AD5755, + ID_AD5757, + ID_AD5735, + ID_AD5737, +}; + +static int ad5755_write_unlocked(struct iio_dev *indio_dev, + unsigned int reg, unsigned int val) +{ + struct ad5755_state *st = iio_priv(indio_dev); + + st->data[0].d32 = cpu_to_be32((reg << 16) | val); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ad5755_write_ctrl_unlocked(struct iio_dev *indio_dev, + unsigned int channel, unsigned int reg, unsigned int val) +{ + return ad5755_write_unlocked(indio_dev, + AD5755_WRITE_REG_CTRL(channel), (reg << 13) | val); +} + +static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg, + unsigned int val) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad5755_write_unlocked(indio_dev, reg, val); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel, + unsigned int reg, unsigned int val) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr) +{ + struct ad5755_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .tx_buf = &st->data[1].d8[1], + .rx_buf = &st->data[1].d8[1], + .len = 3, + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + mutex_lock(&indio_dev->mlock); + + st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16)); + st->data[1].d32 = cpu_to_be32(AD5755_NOOP); + + ret = spi_sync(st->spi, &m); + if (ret >= 0) + ret = be32_to_cpu(st->data[1].d32) & 0xffff; + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5755_update_dac_ctrl(struct iio_dev *indio_dev, + unsigned int channel, unsigned int set, unsigned int clr) +{ + struct ad5755_state *st = iio_priv(indio_dev); + int ret; + + st->ctrl[channel] |= set; + st->ctrl[channel] &= ~clr; + + ret = ad5755_write_ctrl_unlocked(indio_dev, channel, + AD5755_CTRL_REG_DAC, st->ctrl[channel]); + + return ret; +} + +static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev, + unsigned int channel, bool pwr_down) +{ + struct ad5755_state *st = iio_priv(indio_dev); + unsigned int mask = BIT(channel); + + mutex_lock(&indio_dev->mlock); + + if ((bool)(st->pwr_down & mask) == pwr_down) + goto out_unlock; + + if (!pwr_down) { + st->pwr_down &= ~mask; + ad5755_update_dac_ctrl(indio_dev, channel, + AD5755_DAC_INT_EN | AD5755_DAC_DC_DC_EN, 0); + udelay(200); + ad5755_update_dac_ctrl(indio_dev, channel, + AD5755_DAC_OUT_EN, 0); + } else { + st->pwr_down |= mask; + ad5755_update_dac_ctrl(indio_dev, channel, + 0, AD5755_DAC_INT_EN | AD5755_DAC_OUT_EN | + AD5755_DAC_DC_DC_EN); + } + +out_unlock: + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static const int ad5755_min_max_table[][2] = { + [AD5755_MODE_VOLTAGE_0V_5V] = { 0, 5000 }, + [AD5755_MODE_VOLTAGE_0V_10V] = { 0, 10000 }, + [AD5755_MODE_VOLTAGE_PLUSMINUS_5V] = { -5000, 5000 }, + [AD5755_MODE_VOLTAGE_PLUSMINUS_10V] = { -10000, 10000 }, + [AD5755_MODE_CURRENT_4mA_20mA] = { 4, 20 }, + [AD5755_MODE_CURRENT_0mA_20mA] = { 0, 20 }, + [AD5755_MODE_CURRENT_0mA_24mA] = { 0, 24 }, +}; + +static void ad5755_get_min_max(struct ad5755_state *st, + struct iio_chan_spec const *chan, int *min, int *max) +{ + enum ad5755_mode mode = st->ctrl[chan->channel] & 7; + *min = ad5755_min_max_table[mode][0]; + *max = ad5755_min_max_table[mode][1]; +} + +static inline int ad5755_get_offset(struct ad5755_state *st, + struct iio_chan_spec const *chan) +{ + int min, max; + + ad5755_get_min_max(st, chan, &min, &max); + return (min * (1 << chan->scan_type.realbits)) / (max - min); +} + +static inline int ad5755_get_scale(struct ad5755_state *st, + struct iio_chan_spec const *chan) +{ + int min, max; + + ad5755_get_min_max(st, chan, &min, &max); + return ((max - min) * 1000000000ULL) >> chan->scan_type.realbits; +} + +static int ad5755_chan_reg_info(struct ad5755_state *st, + struct iio_chan_spec const *chan, long info, bool write, + unsigned int *reg, unsigned int *shift, unsigned int *offset) +{ + switch (info) { + case IIO_CHAN_INFO_RAW: + if (write) + *reg = AD5755_WRITE_REG_DATA(chan->address); + else + *reg = AD5755_READ_REG_DATA(chan->address); + *shift = chan->scan_type.shift; + *offset = 0; + break; + case IIO_CHAN_INFO_CALIBBIAS: + if (write) + *reg = AD5755_WRITE_REG_OFFSET(chan->address); + else + *reg = AD5755_READ_REG_OFFSET(chan->address); + *shift = st->chip_info->calib_shift; + *offset = 32768; + break; + case IIO_CHAN_INFO_CALIBSCALE: + if (write) + *reg = AD5755_WRITE_REG_GAIN(chan->address); + else + *reg = AD5755_READ_REG_GAIN(chan->address); + *shift = st->chip_info->calib_shift; + *offset = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ad5755_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + struct ad5755_state *st = iio_priv(indio_dev); + unsigned int reg, shift, offset; + int ret; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = ad5755_get_scale(st, chan); + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + *val = ad5755_get_offset(st, chan); + return IIO_VAL_INT; + default: + ret = ad5755_chan_reg_info(st, chan, info, false, + ®, &shift, &offset); + if (ret) + return ret; + + ret = ad5755_read(indio_dev, reg); + if (ret < 0) + return ret; + + *val = (ret - offset) >> shift; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad5755_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int val, int val2, long info) +{ + struct ad5755_state *st = iio_priv(indio_dev); + unsigned int shift, reg, offset; + int ret; + + ret = ad5755_chan_reg_info(st, chan, info, true, + ®, &shift, &offset); + if (ret) + return ret; + + val <<= shift; + val += offset; + + if (val < 0 || val > 0xffff) + return -EINVAL; + + return ad5755_write(indio_dev, reg, val); +} + +static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv, + const struct iio_chan_spec *chan, char *buf) +{ + struct ad5755_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + (bool)(st->pwr_down & (1 << chan->channel))); +} + +static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv, + struct iio_chan_spec const *chan, const char *buf, size_t len) +{ + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + ret = ad5755_set_channel_pwr_down(indio_dev, chan->channel, pwr_down); + return ret ? ret : len; +} + +static const struct iio_info ad5755_info = { + .read_raw = ad5755_read_raw, + .write_raw = ad5755_write_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5755_ext_info[] = { + { + .name = "powerdown", + .read = ad5755_read_powerdown, + .write = ad5755_write_powerdown, + }, + { }, +}; + +#define AD5755_CHANNEL(_bits) { \ + .indexed = 1, \ + .output = 1, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \ + .ext_info = ad5755_ext_info, \ +} + +static const struct ad5755_chip_info ad5755_chip_info_tbl[] = { + [ID_AD5735] = { + .channel_template = AD5755_CHANNEL(14), + .has_voltage_out = true, + .calib_shift = 4, + }, + [ID_AD5737] = { + .channel_template = AD5755_CHANNEL(14), + .has_voltage_out = false, + .calib_shift = 4, + }, + [ID_AD5755] = { + .channel_template = AD5755_CHANNEL(16), + .has_voltage_out = true, + .calib_shift = 0, + }, + [ID_AD5757] = { + .channel_template = AD5755_CHANNEL(16), + .has_voltage_out = false, + .calib_shift = 0, + }, +}; + +static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode) +{ + switch (mode) { + case AD5755_MODE_VOLTAGE_0V_5V: + case AD5755_MODE_VOLTAGE_0V_10V: + case AD5755_MODE_VOLTAGE_PLUSMINUS_5V: + case AD5755_MODE_VOLTAGE_PLUSMINUS_10V: + return st->chip_info->has_voltage_out; + case AD5755_MODE_CURRENT_4mA_20mA: + case AD5755_MODE_CURRENT_0mA_20mA: + case AD5755_MODE_CURRENT_0mA_24mA: + return true; + default: + return false; + } +} + +static int __devinit ad5755_setup_pdata(struct iio_dev *indio_dev, + const struct ad5755_platform_data *pdata) +{ + struct ad5755_state *st = iio_priv(indio_dev); + unsigned int val; + unsigned int i; + int ret; + + if (pdata->dc_dc_phase > AD5755_DC_DC_PHASE_90_DEGREE || + pdata->dc_dc_freq > AD5755_DC_DC_FREQ_650kHZ || + pdata->dc_dc_maxv > AD5755_DC_DC_MAXV_29V5) + return -EINVAL; + + val = pdata->dc_dc_maxv << AD5755_DC_DC_MAXV; + val |= pdata->dc_dc_freq << AD5755_DC_DC_FREQ_SHIFT; + val |= pdata->dc_dc_phase << AD5755_DC_DC_PHASE_SHIFT; + if (pdata->ext_dc_dc_compenstation_resistor) + val |= AD5755_EXT_DC_DC_COMP_RES; + + ret = ad5755_write_ctrl(indio_dev, 0, AD5755_CTRL_REG_DC_DC, val); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) { + val = pdata->dac[i].slew.step_size << + AD5755_SLEW_STEP_SIZE_SHIFT; + val |= pdata->dac[i].slew.rate << + AD5755_SLEW_RATE_SHIFT; + if (pdata->dac[i].slew.enable) + val |= AD5755_SLEW_ENABLE; + + ret = ad5755_write_ctrl(indio_dev, i, + AD5755_CTRL_REG_SLEW, val); + if (ret < 0) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) { + if (!ad5755_is_valid_mode(st, pdata->dac[i].mode)) + return -EINVAL; + + val = 0; + if (!pdata->dac[i].ext_current_sense_resistor) + val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR; + if (pdata->dac[i].enable_voltage_overrange) + val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN; + val |= pdata->dac[i].mode; + + ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static bool __devinit ad5755_is_voltage_mode(enum ad5755_mode mode) +{ + switch (mode) { + case AD5755_MODE_VOLTAGE_0V_5V: + case AD5755_MODE_VOLTAGE_0V_10V: + case AD5755_MODE_VOLTAGE_PLUSMINUS_5V: + case AD5755_MODE_VOLTAGE_PLUSMINUS_10V: + return true; + default: + return false; + } +} + +static int __devinit ad5755_init_channels(struct iio_dev *indio_dev, + const struct ad5755_platform_data *pdata) +{ + struct ad5755_state *st = iio_priv(indio_dev); + struct iio_chan_spec *channels = st->channels; + unsigned int i; + + for (i = 0; i < AD5755_NUM_CHANNELS; ++i) { + channels[i] = st->chip_info->channel_template; + channels[i].channel = i; + channels[i].address = i; + if (pdata && ad5755_is_voltage_mode(pdata->dac[i].mode)) + channels[i].type = IIO_VOLTAGE; + else + channels[i].type = IIO_CURRENT; + } + + indio_dev->channels = channels; + + return 0; +} + +#define AD5755_DEFAULT_DAC_PDATA { \ + .mode = AD5755_MODE_CURRENT_4mA_20mA, \ + .ext_current_sense_resistor = true, \ + .enable_voltage_overrange = false, \ + .slew = { \ + .enable = false, \ + .rate = AD5755_SLEW_RATE_64k, \ + .step_size = AD5755_SLEW_STEP_SIZE_1, \ + }, \ + } + +static const struct ad5755_platform_data ad5755_default_pdata = { + .ext_dc_dc_compenstation_resistor = false, + .dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE, + .dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ, + .dc_dc_maxv = AD5755_DC_DC_MAXV_23V, + .dac = { + [0] = AD5755_DEFAULT_DAC_PDATA, + [1] = AD5755_DEFAULT_DAC_PDATA, + [2] = AD5755_DEFAULT_DAC_PDATA, + [3] = AD5755_DEFAULT_DAC_PDATA, + }, +}; + +static int __devinit ad5755_probe(struct spi_device *spi) +{ + enum ad5755_type type = spi_get_device_id(spi)->driver_data; + const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev); + struct iio_dev *indio_dev; + struct ad5755_state *st; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->chip_info = &ad5755_chip_info_tbl[type]; + st->spi = spi; + st->pwr_down = 0xf; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5755_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = AD5755_NUM_CHANNELS; + + if (!pdata) + pdata = &ad5755_default_pdata; + + ret = ad5755_init_channels(indio_dev, pdata); + if (ret) + goto error_free; + + ret = ad5755_setup_pdata(indio_dev, pdata); + if (ret) + goto error_free; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); + goto error_free; + } + + return 0; + +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5755_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + iio_device_unregister(indio_dev); + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5755_id[] = { + { "ad5755", ID_AD5755 }, + { "ad5755-1", ID_AD5755 }, + { "ad5757", ID_AD5757 }, + { "ad5735", ID_AD5735 }, + { "ad5737", ID_AD5737 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5755_id); + +static struct spi_driver ad5755_driver = { + .driver = { + .name = "ad5755", + .owner = THIS_MODULE, + }, + .probe = ad5755_probe, + .remove = __devexit_p(ad5755_remove), + .id_table = ad5755_id, +}; +module_spi_driver(ad5755_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Analog Devices AD5755/55-1/57/35/37 DAC"); +MODULE_LICENSE("GPL v2"); |