Commit 0357e488 authored by Stefan Popa's avatar Stefan Popa Committed by Jonathan Cameron

iio:dac:ad5686: Refactor the driver

In this patch restructures the existing ad5686 driver by adding a module
for SPI and a header file, while the baseline module deals with the
chip-logic.

This is a necessary step, as this driver should support in the future
similar devices which differ only in the type of interface used (I2C
instead of SPI).
Signed-off-by: default avatarStefan Popa <stefan.popa@analog.com>
Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 98baf78e
......@@ -791,6 +791,13 @@ M: Michael Hanselmann <linux-kernel@hansmi.ch>
S: Supported
F: drivers/macintosh/ams/
ANALOG DEVICES INC AD5686 DRIVER
M: Stefan Popa <stefan.popa@analog.com>
L: linux-pm@vger.kernel.org
W: http://ez.analog.com/community/linux-device-drivers
S: Supported
F: drivers/iio/dac/ad5686*
ANALOG DEVICES INC AD9389B DRIVER
M: Hans Verkuil <hans.verkuil@cisco.com>
L: linux-media@vger.kernel.org
......
......@@ -131,16 +131,21 @@ config LTC2632
module will be called ltc2632.
config AD5686
tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver"
tristate
config AD5686_SPI
tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)"
depends on SPI
select AD5686
help
Say yes here to build support for Analog Devices AD5686R, AD5685R,
AD5684R, AD5791 Voltage Output Digital to
Analog Converter.
Say yes here to build support for Analog Devices AD5672R, AD5676,
AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R.
Voltage Output Digital to Analog Converter.
To compile this driver as a module, choose M here: the
module will be called ad5686.
config AD5755
tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver"
depends on SPI_MASTER
......
......@@ -20,6 +20,7 @@ obj-$(CONFIG_AD5761) += ad5761.o
obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o
obj-$(CONFIG_AD7303) += ad7303.o
obj-$(CONFIG_AD8801) += ad8801.o
obj-$(CONFIG_CIO_DAC) += cio-dac.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* AD5672R, AD5676, AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R
* Digital to analog converters driver
*
* Copyright 2018 Analog Devices Inc.
*/
#include "ad5686.h"
#include <linux/module.h>
#include <linux/spi/spi.h>
static int ad5686_spi_write(struct ad5686_state *st,
u8 cmd, u8 addr, u16 val)
{
struct spi_device *spi = to_spi_device(st->dev);
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
AD5686_ADDR(addr) |
val);
return spi_write(spi, &st->data[0].d8[1], 3);
}
static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
{
struct spi_transfer t[] = {
{
.tx_buf = &st->data[0].d8[1],
.len = 3,
.cs_change = 1,
}, {
.tx_buf = &st->data[1].d8[1],
.rx_buf = &st->data[2].d8[1],
.len = 3,
},
};
struct spi_device *spi = to_spi_device(st->dev);
int ret;
st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) |
AD5686_ADDR(addr));
st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
if (ret < 0)
return ret;
return be32_to_cpu(st->data[2].d32);
}
static int ad5686_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
return ad5686_probe(&spi->dev, id->driver_data, id->name,
ad5686_spi_write, ad5686_spi_read);
}
static int ad5686_spi_remove(struct spi_device *spi)
{
return ad5686_remove(&spi->dev);
}
static const struct spi_device_id ad5686_spi_id[] = {
{"ad5672r", ID_AD5672R},
{"ad5676", ID_AD5676},
{"ad5676r", ID_AD5676R},
{"ad5684", ID_AD5684},
{"ad5684r", ID_AD5684R},
{"ad5685", ID_AD5685R}, /* Does not exist */
{"ad5685r", ID_AD5685R},
{"ad5686", ID_AD5686},
{"ad5686r", ID_AD5686R},
{}
};
MODULE_DEVICE_TABLE(spi, ad5686_spi_id);
static struct spi_driver ad5686_spi_driver = {
.driver = {
.name = "ad5686",
},
.probe = ad5686_spi_probe,
.remove = ad5686_spi_remove,
.id_table = ad5686_spi_id,
};
module_spi_driver(ad5686_spi_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs");
MODULE_LICENSE("GPL v2");
......@@ -10,7 +10,6 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/regulator/consumer.h>
......@@ -18,121 +17,7 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define AD5686_ADDR(x) ((x) << 16)
#define AD5686_CMD(x) ((x) << 20)
#define AD5686_ADDR_DAC(chan) (0x1 << (chan))
#define AD5686_ADDR_ALL_DAC 0xF
#define AD5686_CMD_NOOP 0x0
#define AD5686_CMD_WRITE_INPUT_N 0x1
#define AD5686_CMD_UPDATE_DAC_N 0x2
#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3
#define AD5686_CMD_POWERDOWN_DAC 0x4
#define AD5686_CMD_LDAC_MASK 0x5
#define AD5686_CMD_RESET 0x6
#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7
#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8
#define AD5686_CMD_READBACK_ENABLE 0x9
#define AD5686_LDAC_PWRDN_NONE 0x0
#define AD5686_LDAC_PWRDN_1K 0x1
#define AD5686_LDAC_PWRDN_100K 0x2
#define AD5686_LDAC_PWRDN_3STATE 0x3
/**
* struct ad5686_chip_info - chip specific information
* @int_vref_mv: AD5620/40/60: the internal reference voltage
* @num_channels: number of channels
* @channel: channel specification
*/
struct ad5686_chip_info {
u16 int_vref_mv;
unsigned int num_channels;
struct iio_chan_spec *channels;
};
/**
* struct ad5446_state - driver instance specific data
* @spi: spi_device
* @chip_info: chip model specific constants, available modes etc
* @reg: supply regulator
* @vref_mv: actual reference voltage used
* @pwr_down_mask: power down mask
* @pwr_down_mode: current power down mode
* @data: spi transfer buffers
*/
struct ad5686_state {
struct spi_device *spi;
const struct ad5686_chip_info *chip_info;
struct regulator *reg;
unsigned short vref_mv;
unsigned pwr_down_mask;
unsigned pwr_down_mode;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
union {
__be32 d32;
u8 d8[4];
} data[3] ____cacheline_aligned;
};
/**
* ad5686_supported_device_ids:
*/
enum ad5686_supported_device_ids {
ID_AD5672R,
ID_AD5676,
ID_AD5676R,
ID_AD5684,
ID_AD5684R,
ID_AD5685R,
ID_AD5686,
ID_AD5686R
};
static int ad5686_spi_write(struct ad5686_state *st,
u8 cmd, u8 addr, u16 val, u8 shift)
{
val <<= shift;
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
AD5686_ADDR(addr) |
val);
return spi_write(st->spi, &st->data[0].d8[1], 3);
}
static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
{
struct spi_transfer t[] = {
{
.tx_buf = &st->data[0].d8[1],
.len = 3,
.cs_change = 1,
}, {
.tx_buf = &st->data[1].d8[1],
.rx_buf = &st->data[2].d8[1],
.len = 3,
},
};
int ret;
st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) |
AD5686_ADDR(addr));
st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
if (ret < 0)
return ret;
return be32_to_cpu(st->data[2].d32);
}
#include "ad5686.h"
static const char * const ad5686_powerdown_modes[] = {
"1kohm_to_gnd",
......@@ -195,8 +80,9 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
else
st->pwr_down_mask &= ~(0x3 << (chan->channel * 2));
ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0,
st->pwr_down_mask & st->pwr_down_mode, 0);
ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0,
st->pwr_down_mask & st->pwr_down_mode);
return ret ? ret : len;
}
......@@ -213,7 +99,7 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
ret = ad5686_spi_read(st, chan->address);
ret = st->read(st, chan->address);
mutex_unlock(&indio_dev->mlock);
if (ret < 0)
return ret;
......@@ -242,11 +128,10 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
mutex_lock(&indio_dev->mlock);
ret = ad5686_spi_write(st,
AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
chan->address,
val,
chan->scan_type.shift);
ret = st->write(st,
AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
chan->address,
val << chan->scan_type.shift);
mutex_unlock(&indio_dev->mlock);
break;
default:
......@@ -356,20 +241,27 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
},
};
static int ad5686_probe(struct spi_device *spi)
int ad5686_probe(struct device *dev,
enum ad5686_supported_device_ids chip_type,
const char *name, ad5686_write_func write,
ad5686_read_func read)
{
struct ad5686_state *st;
struct iio_dev *indio_dev;
int ret, voltage_uv = 0;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (indio_dev == NULL)
return -ENOMEM;
st = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
dev_set_drvdata(dev, indio_dev);
st->dev = dev;
st->write = write;
st->read = read;
st->reg = devm_regulator_get_optional(&spi->dev, "vcc");
st->reg = devm_regulator_get_optional(dev, "vcc");
if (!IS_ERR(st->reg)) {
ret = regulator_enable(st->reg);
if (ret)
......@@ -382,28 +274,25 @@ static int ad5686_probe(struct spi_device *spi)
voltage_uv = ret;
}
st->chip_info =
&ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data];
st->chip_info = &ad5686_chip_info_tbl[chip_type];
if (voltage_uv)
st->vref_mv = voltage_uv / 1000;
else
st->vref_mv = st->chip_info->int_vref_mv;
st->spi = spi;
/* Set all the power down mode for all channels to 1K pulldown */
st->pwr_down_mode = 0x55;
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->dev.parent = dev;
indio_dev->name = name;
indio_dev->info = &ad5686_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0,
!!voltage_uv, 0);
ret = st->write(st, AD5686_CMD_INTERNAL_REFER_SETUP,
0, !!voltage_uv);
if (ret)
goto error_disable_reg;
......@@ -418,10 +307,11 @@ static int ad5686_probe(struct spi_device *spi)
regulator_disable(st->reg);
return ret;
}
EXPORT_SYMBOL_GPL(ad5686_probe);
static int ad5686_remove(struct spi_device *spi)
int ad5686_remove(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ad5686_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
......@@ -430,30 +320,7 @@ static int ad5686_remove(struct spi_device *spi)
return 0;
}
static const struct spi_device_id ad5686_id[] = {
{"ad5672r", ID_AD5672R},
{"ad5676", ID_AD5676},
{"ad5676r", ID_AD5676R},
{"ad5684", ID_AD5684},
{"ad5684r", ID_AD5684R},
{"ad5685", ID_AD5685R}, /* Does not exist */
{"ad5685r", ID_AD5685R},
{"ad5686", ID_AD5686},
{"ad5686r", ID_AD5686R},
{}
};
MODULE_DEVICE_TABLE(spi, ad5686_id);
static struct spi_driver ad5686_driver = {
.driver = {
.name = "ad5686",
},
.probe = ad5686_probe,
.remove = ad5686_remove,
.id_table = ad5686_id,
};
module_spi_driver(ad5686_driver);
EXPORT_SYMBOL_GPL(ad5686_remove);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* This file is part of AD5686 DAC driver
*
* Copyright 2018 Analog Devices Inc.
*/
#ifndef __DRIVERS_IIO_DAC_AD5686_H__
#define __DRIVERS_IIO_DAC_AD5686_H__
#include <linux/types.h>
#include <linux/cache.h>
#include <linux/mutex.h>
#include <linux/kernel.h>
#define AD5686_ADDR(x) ((x) << 16)
#define AD5686_CMD(x) ((x) << 20)
#define AD5686_ADDR_DAC(chan) (0x1 << (chan))
#define AD5686_ADDR_ALL_DAC 0xF
#define AD5686_CMD_NOOP 0x0
#define AD5686_CMD_WRITE_INPUT_N 0x1
#define AD5686_CMD_UPDATE_DAC_N 0x2
#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3
#define AD5686_CMD_POWERDOWN_DAC 0x4
#define AD5686_CMD_LDAC_MASK 0x5
#define AD5686_CMD_RESET 0x6
#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7
#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8
#define AD5686_CMD_READBACK_ENABLE 0x9
#define AD5686_LDAC_PWRDN_NONE 0x0
#define AD5686_LDAC_PWRDN_1K 0x1
#define AD5686_LDAC_PWRDN_100K 0x2
#define AD5686_LDAC_PWRDN_3STATE 0x3
/**
* ad5686_supported_device_ids:
*/
enum ad5686_supported_device_ids {
ID_AD5672R,
ID_AD5676,
ID_AD5676R,
ID_AD5684,
ID_AD5684R,
ID_AD5685R,
ID_AD5686,
ID_AD5686R,
};
struct ad5686_state;
typedef int (*ad5686_write_func)(struct ad5686_state *st,
u8 cmd, u8 addr, u16 val);
typedef int (*ad5686_read_func)(struct ad5686_state *st, u8 addr);
/**
* struct ad5686_chip_info - chip specific information
* @int_vref_mv: AD5620/40/60: the internal reference voltage
* @num_channels: number of channels
* @channel: channel specification
*/
struct ad5686_chip_info {
u16 int_vref_mv;
unsigned int num_channels;
struct iio_chan_spec *channels;
};
/**
* struct ad5446_state - driver instance specific data
* @spi: spi_device
* @chip_info: chip model specific constants, available modes etc
* @reg: supply regulator
* @vref_mv: actual reference voltage used
* @pwr_down_mask: power down mask
* @pwr_down_mode: current power down mode
* @data: spi transfer buffers
*/
struct ad5686_state {
struct device *dev;
const struct ad5686_chip_info *chip_info;
struct regulator *reg;
unsigned short vref_mv;
unsigned int pwr_down_mask;
unsigned int pwr_down_mode;
ad5686_write_func write;
ad5686_read_func read;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
union {
__be32 d32;
__be16 d16;
u8 d8[4];
} data[3] ____cacheline_aligned;
};
int ad5686_probe(struct device *dev,
enum ad5686_supported_device_ids chip_type,
const char *name, ad5686_write_func write,
ad5686_read_func read);
int ad5686_remove(struct device *dev);
#endif /* __DRIVERS_IIO_DAC_AD5686_H__ */
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