Commit 3ec36a2c authored by Jean-Francois Dagenais's avatar Jean-Francois Dagenais Committed by Jonathan Cameron

iio:ad5446: Add support for I2C based DACs

This patch adds support for I2C based single channel DACs to the ad5446
driver. Specifically AD5602, AD5612 and AD5622.

V1: from Lars-Peter Clausen <lars@metafoo.de>
V2: Split the device IDs into two enums and move them to the c file.
Signed-off-by: default avatarJean-Francois Dagenais <jeff.dagenais@gmail.com>
Acked-by: default avatarLars-Peter Clausen <lars@metafoo.de>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent e58bf533
...@@ -57,11 +57,12 @@ config AD5624R_SPI ...@@ -57,11 +57,12 @@ config AD5624R_SPI
config AD5446 config AD5446
tristate "Analog Devices AD5446 and similar single channel DACs driver" tristate "Analog Devices AD5446 and similar single channel DACs driver"
depends on SPI depends on (SPI_MASTER || I2C)
help help
Say yes here to build support for Analog Devices AD5444, AD5446, AD5450, Say yes here to build support for Analog Devices AD5602, AD5612, AD5622,
AD5451, AD5452, AD5453, AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453, AD5512A, AD5541A, AD5542A,
AD5611, AD5620, AD5621, AD5640, AD5660, AD5662 DACs. AD5543, AD5553, AD5601, AD5611, AD5620, AD5621, AD5640, AD5660, AD5662
DACs.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called ad5446. module will be called ad5446.
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/i2c.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -23,23 +24,6 @@ ...@@ -23,23 +24,6 @@
#include "ad5446.h" #include "ad5446.h"
static int ad5446_write(struct ad5446_state *st, unsigned val)
{
__be16 data = cpu_to_be16(val);
return spi_write(st->spi, &data, sizeof(data));
}
static int ad5660_write(struct ad5446_state *st, unsigned val)
{
uint8_t data[3];
data[0] = (val >> 16) & 0xFF;
data[1] = (val >> 8) & 0xFF;
data[2] = val & 0xFF;
return spi_write(st->spi, data, sizeof(data));
}
static const char * const ad5446_powerdown_modes[] = { static const char * const ad5446_powerdown_modes[] = {
"1kohm_to_gnd", "100kohm_to_gnd", "three_state" "1kohm_to_gnd", "100kohm_to_gnd", "three_state"
}; };
...@@ -110,7 +94,7 @@ static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, ...@@ -110,7 +94,7 @@ static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev,
return ret ? ret : len; 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", .name = "powerdown",
.read = ad5446_read_dac_powerdown, .read = ad5446_read_dac_powerdown,
...@@ -136,84 +120,7 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = { ...@@ -136,84 +120,7 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = {
_AD5446_CHANNEL(bits, storage, shift, NULL) _AD5446_CHANNEL(bits, storage, shift, NULL)
#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \ #define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \
_AD5446_CHANNEL(bits, storage, shift, ad5064_ext_info_powerdown) _AD5446_CHANNEL(bits, storage, shift, ad5446_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,
},
};
static int ad5446_read_raw(struct iio_dev *indio_dev, static int ad5446_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, struct iio_chan_spec const *chan,
...@@ -272,14 +179,15 @@ static const struct iio_info ad5446_info = { ...@@ -272,14 +179,15 @@ static const struct iio_info ad5446_info = {
.driver_module = THIS_MODULE, .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 ad5446_state *st;
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
struct regulator *reg; struct regulator *reg;
int ret, voltage_uv = 0; int ret, voltage_uv = 0;
reg = regulator_get(&spi->dev, "vcc"); reg = regulator_get(dev, "vcc");
if (!IS_ERR(reg)) { if (!IS_ERR(reg)) {
ret = regulator_enable(reg); ret = regulator_enable(reg);
if (ret) if (ret)
...@@ -294,16 +202,15 @@ static int __devinit ad5446_probe(struct spi_device *spi) ...@@ -294,16 +202,15 @@ static int __devinit ad5446_probe(struct spi_device *spi)
goto error_disable_reg; goto error_disable_reg;
} }
st = iio_priv(indio_dev); st = iio_priv(indio_dev);
st->chip_info = st->chip_info = chip_info;
&ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data];
spi_set_drvdata(spi, indio_dev); dev_set_drvdata(dev, indio_dev);
st->reg = reg; st->reg = reg;
st->spi = spi; st->dev = dev;
/* Establish that the iio_dev is a child of the spi device */ /* Establish that the iio_dev is a child of the device */
indio_dev->dev.parent = &spi->dev; indio_dev->dev.parent = dev;
indio_dev->name = spi_get_device_id(spi)->name; indio_dev->name = name;
indio_dev->info = &ad5446_info; indio_dev->info = &ad5446_info;
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = &st->chip_info->channel; indio_dev->channels = &st->chip_info->channel;
...@@ -316,7 +223,7 @@ static int __devinit ad5446_probe(struct spi_device *spi) ...@@ -316,7 +223,7 @@ static int __devinit ad5446_probe(struct spi_device *spi)
else if (voltage_uv) else if (voltage_uv)
st->vref_mv = voltage_uv / 1000; st->vref_mv = voltage_uv / 1000;
else else
dev_warn(&spi->dev, "reference voltage unspecified\n"); dev_warn(dev, "reference voltage unspecified\n");
ret = iio_device_register(indio_dev); ret = iio_device_register(indio_dev);
if (ret) if (ret)
...@@ -336,9 +243,9 @@ static int __devinit ad5446_probe(struct spi_device *spi) ...@@ -336,9 +243,9 @@ static int __devinit ad5446_probe(struct spi_device *spi)
return ret; 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); struct ad5446_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev); iio_device_unregister(indio_dev);
...@@ -351,7 +258,133 @@ static int ad5446_remove(struct spi_device *spi) ...@@ -351,7 +258,133 @@ static int ad5446_remove(struct spi_device *spi)
return 0; 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_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_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[] = {
{"ad5444", ID_AD5444}, {"ad5444", ID_AD5444},
{"ad5446", ID_AD5446}, {"ad5446", ID_AD5446},
{"ad5450", ID_AD5450}, {"ad5450", ID_AD5450},
...@@ -375,18 +408,157 @@ static const struct spi_device_id ad5446_id[] = { ...@@ -375,18 +408,157 @@ static const struct spi_device_id ad5446_id[] = {
{"ad5662", ID_AD5662}, {"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);
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_spi_probe,
.remove = __devexit_p(ad5446_spi_remove),
.id_table = ad5446_spi_ids,
};
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[] = {
{"ad5602", ID_AD5602},
{"ad5612", ID_AD5612},
{"ad5622", ID_AD5622},
{}
};
MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids);
static struct spi_driver ad5446_driver = { static struct i2c_driver ad5446_i2c_driver = {
.driver = { .driver = {
.name = "ad5446", .name = "ad5446",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = ad5446_probe, .probe = ad5446_i2c_probe,
.remove = __devexit_p(ad5446_remove), .remove = __devexit_p(ad5446_i2c_remove),
.id_table = ad5446_id, .id_table = ad5446_i2c_ids,
}; };
module_spi_driver(ad5446_driver);
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_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
*/ */
struct ad5446_state { struct ad5446_state {
struct spi_device *spi; struct device *dev;
const struct ad5446_chip_info *chip_info; const struct ad5446_chip_info *chip_info;
struct regulator *reg; struct regulator *reg;
unsigned short vref_mv; unsigned short vref_mv;
...@@ -60,32 +60,5 @@ struct ad5446_chip_info { ...@@ -60,32 +60,5 @@ struct ad5446_chip_info {
int (*write)(struct ad5446_state *st, unsigned val); 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_ */ #endif /* IIO_DAC_AD5446_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment