Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 317720
b: refs/heads/master
c: 6a17a07
h: refs/heads/master
v: v3
  • Loading branch information
Lars-Peter Clausen authored and Jonathan Cameron committed Jun 30, 2012
1 parent 92b743b commit 4e41d61
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 36 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 8ec4cf5303e03941fa5fd91bbb9c85bd4ae88c47
refs/heads/master: 6a17a0768f77626046aa441843b318a00bac3800
8 changes: 4 additions & 4 deletions trunk/drivers/iio/dac/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
menu "Digital to analog converters"

config AD5064
tristate "Analog Devices AD5064/64-1/65/44/45/24/25, AD5628/48/66/68 DAC driver"
depends on SPI
tristate "Analog Devices AD5064 and similar multi-channel DAC driver"
depends on (SPI_MASTER || I2C)
help
Say yes here to build support for Analog Devices AD5024, AD5025, AD5044,
AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, AD5666, AD5668 Digital
to Analog Converter.
AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R, AD5648, AD5666, AD5668,
AD5669R Digital to Analog Converter.

To compile this driver as a module, choose M here: the
module will be called ad5064.
Expand Down
200 changes: 169 additions & 31 deletions trunk/drivers/iio/dac/ad5064.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648,
* AD5666, AD5668 Digital to analog converters driver
* AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R,
* AD5648, AD5666, AD5668, AD5669R Digital to analog converters driver
*
* Copyright 2011 Analog Devices Inc.
*
Expand All @@ -12,9 +12,11 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/regulator/consumer.h>
#include <asm/unaligned.h>

#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
Expand Down Expand Up @@ -62,33 +64,44 @@ struct ad5064_chip_info {
unsigned int num_channels;
};

struct ad5064_state;

typedef int (*ad5064_write_func)(struct ad5064_state *st, unsigned int cmd,
unsigned int addr, unsigned int val);

/**
* struct ad5064_state - driver instance specific data
* @spi: spi_device
* @dev: the device for this driver instance
* @chip_info: chip model specific constants, available modes etc
* @vref_reg: vref supply regulators
* @pwr_down: whether channel is powered down
* @pwr_down_mode: channel's current power down mode
* @dac_cache: current DAC raw value (chip does not support readback)
* @use_internal_vref: set to true if the internal reference voltage should be
* used.
* @data: spi transfer buffers
* @write: register write callback
* @data: i2c/spi transfer buffers
*/

struct ad5064_state {
struct spi_device *spi;
struct device *dev;
const struct ad5064_chip_info *chip_info;
struct regulator_bulk_data vref_reg[AD5064_MAX_VREFS];
bool pwr_down[AD5064_MAX_DAC_CHANNELS];
u8 pwr_down_mode[AD5064_MAX_DAC_CHANNELS];
unsigned int dac_cache[AD5064_MAX_DAC_CHANNELS];
bool use_internal_vref;

ad5064_write_func write;

/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
__be32 data ____cacheline_aligned;
union {
u8 i2c[3];
__be32 spi;
} data ____cacheline_aligned;
};

enum ad5064_type {
Expand All @@ -109,14 +122,31 @@ enum ad5064_type {
ID_AD5668_2,
};

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);

st->data.i2c[0] = (cmd << 4) | addr;
put_unaligned_be16(val, &st->data.i2c[1]);
return i2c_master_send(i2c, st->data.i2c, 3);
}

static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd,
unsigned int addr, unsigned int val)
{
struct spi_device *spi = to_spi_device(st->dev);

st->data.spi = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val);
return spi_write(spi, &st->data.spi, sizeof(st->data.spi));
}

static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
unsigned int addr, unsigned int val, unsigned int shift)
{
val <<= shift;

st->data = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val);

return spi_write(st->spi, &st->data, sizeof(st->data));
return st->write(st, cmd, addr, val);
}

static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
Expand All @@ -130,7 +160,7 @@ static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
if (st->pwr_down[channel])
val |= st->pwr_down_mode[channel] << 8;

ret = ad5064_spi_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0);
ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0);

return ret;
}
Expand Down Expand Up @@ -251,7 +281,7 @@ static int ad5064_write_raw(struct iio_dev *indio_dev,
return -EINVAL;

mutex_lock(&indio_dev->mlock);
ret = ad5064_spi_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N,
ret = ad5064_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N,
chan->address, val, chan->scan_type.shift);
if (ret == 0)
st->dac_cache[chan->channel] = val;
Expand Down Expand Up @@ -413,9 +443,9 @@ static const char * const ad5064_vref_name(struct ad5064_state *st,
return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref];
}

static int __devinit ad5064_probe(struct spi_device *spi)
static int __devinit ad5064_probe(struct device *dev, enum ad5064_type type,
const char *name, ad5064_write_func write)
{
enum ad5064_type type = spi_get_device_id(spi)->driver_data;
struct iio_dev *indio_dev;
struct ad5064_state *st;
unsigned int i;
Expand All @@ -426,24 +456,25 @@ static int __devinit ad5064_probe(struct spi_device *spi)
return -ENOMEM;

st = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
dev_set_drvdata(dev, indio_dev);

st->chip_info = &ad5064_chip_info_tbl[type];
st->spi = spi;
st->dev = dev;
st->write = write;

for (i = 0; i < ad5064_num_vref(st); ++i)
st->vref_reg[i].supply = ad5064_vref_name(st, i);

ret = regulator_bulk_get(&st->spi->dev, ad5064_num_vref(st),
ret = regulator_bulk_get(dev, ad5064_num_vref(st),
st->vref_reg);
if (ret) {
if (!st->chip_info->internal_vref)
goto error_free;
st->use_internal_vref = true;
ret = ad5064_spi_write(st, AD5064_CMD_CONFIG, 0,
ret = ad5064_write(st, AD5064_CMD_CONFIG, 0,
AD5064_CONFIG_INT_VREF_ENABLE, 0);
if (ret) {
dev_err(&spi->dev, "Failed to enable internal vref: %d\n",
dev_err(dev, "Failed to enable internal vref: %d\n",
ret);
goto error_free;
}
Expand All @@ -458,8 +489,8 @@ static int __devinit ad5064_probe(struct spi_device *spi)
st->dac_cache[i] = 0x8000;
}

indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->dev.parent = dev;
indio_dev->name = name;
indio_dev->info = &ad5064_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->chip_info->channels;
Expand All @@ -483,10 +514,9 @@ static int __devinit ad5064_probe(struct spi_device *spi)
return ret;
}


static int __devexit ad5064_remove(struct spi_device *spi)
static int __devexit ad5064_remove(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ad5064_state *st = iio_priv(indio_dev);

iio_device_unregister(indio_dev);
Expand All @@ -501,7 +531,22 @@ static int __devexit ad5064_remove(struct spi_device *spi)
return 0;
}

static const struct spi_device_id ad5064_id[] = {
#if IS_ENABLED(CONFIG_SPI_MASTER)

static int __devinit ad5064_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);

return ad5064_probe(&spi->dev, id->driver_data, id->name,
ad5064_spi_write);
}

static int __devexit ad5064_spi_remove(struct spi_device *spi)
{
return ad5064_remove(&spi->dev);
}

static const struct spi_device_id ad5064_spi_ids[] = {
{"ad5024", ID_AD5024},
{"ad5025", ID_AD5025},
{"ad5044", ID_AD5044},
Expand All @@ -520,19 +565,112 @@ static const struct spi_device_id ad5064_id[] = {
{"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */
{}
};
MODULE_DEVICE_TABLE(spi, ad5064_id);
MODULE_DEVICE_TABLE(spi, ad5064_spi_ids);

static struct spi_driver ad5064_driver = {
static struct spi_driver ad5064_spi_driver = {
.driver = {
.name = "ad5064",
.owner = THIS_MODULE,
},
.probe = ad5064_probe,
.remove = __devexit_p(ad5064_remove),
.id_table = ad5064_id,
.probe = ad5064_spi_probe,
.remove = __devexit_p(ad5064_spi_remove),
.id_table = ad5064_spi_ids,
};
module_spi_driver(ad5064_driver);

static int __init ad5064_spi_register_driver(void)
{
return spi_register_driver(&ad5064_spi_driver);
}

static void __exit ad5064_spi_unregister_driver(void)
{
spi_unregister_driver(&ad5064_spi_driver);
}

#else

static inline int ad5064_spi_register_driver(void) { return 0; }
static inline void ad5064_spi_unregister_driver(void) { }

#endif

#if IS_ENABLED(CONFIG_I2C)

static int __devinit ad5064_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
return ad5064_probe(&i2c->dev, id->driver_data, id->name,
ad5064_i2c_write);
}

static int __devexit ad5064_i2c_remove(struct i2c_client *i2c)
{
return ad5064_remove(&i2c->dev);
}

static const struct i2c_device_id ad5064_i2c_ids[] = {
{"ad5629-1", ID_AD5628_1},
{"ad5629-2", ID_AD5628_2},
{"ad5629-3", ID_AD5628_2}, /* similar enough to ad5629-2 */
{"ad5669-1", ID_AD5668_1},
{"ad5669-2", ID_AD5668_2},
{"ad5669-3", ID_AD5668_2}, /* similar enough to ad5669-2 */
{}
};
MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids);

static struct i2c_driver ad5064_i2c_driver = {
.driver = {
.name = "ad5064",
.owner = THIS_MODULE,
},
.probe = ad5064_i2c_probe,
.remove = __devexit_p(ad5064_i2c_remove),
.id_table = ad5064_i2c_ids,
};

static int __init ad5064_i2c_register_driver(void)
{
return i2c_add_driver(&ad5064_i2c_driver);
}

static void __exit ad5064_i2c_unregister_driver(void)
{
i2c_del_driver(&ad5064_i2c_driver);
}

#else

static inline int ad5064_i2c_register_driver(void) { return 0; }
static inline void ad5064_i2c_unregister_driver(void) { }

#endif

static int __init ad5064_init(void)
{
int ret;

ret = ad5064_spi_register_driver();
if (ret)
return ret;

ret = ad5064_i2c_register_driver();
if (ret) {
ad5064_spi_unregister_driver();
return ret;
}

return 0;
}
module_init(ad5064_init);

static void __exit ad5064_exit(void)
{
ad5064_i2c_unregister_driver();
ad5064_spi_unregister_driver();
}
module_exit(ad5064_exit);

MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Analog Devices AD5024/25/44/45/64/64-1/65, AD5628/48/66/68 DAC");
MODULE_DESCRIPTION("Analog Devices AD5024 and similar multi-channel DACs");
MODULE_LICENSE("GPL v2");

0 comments on commit 4e41d61

Please sign in to comment.