mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
184
drivers/iio/dac/Kconfig
Normal file
184
drivers/iio/dac/Kconfig
Normal file
|
@ -0,0 +1,184 @@
|
|||
#
|
||||
# DAC drivers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Digital to analog converters"
|
||||
|
||||
config AD5064
|
||||
tristate "Analog Devices AD5064 and similar multi-channel DAC driver"
|
||||
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.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5064.
|
||||
|
||||
config AD5360
|
||||
tristate "Analog Devices AD5360/61/62/63/70/71/73 DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5360, AD5361,
|
||||
AD5362, AD5363, AD5370, AD5371, AD5373 multi-channel
|
||||
Digital to Analog Converters (DAC).
|
||||
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad5360.
|
||||
|
||||
config AD5380
|
||||
tristate "Analog Devices AD5380/81/82/83/84/90/91/92 DAC driver"
|
||||
depends on (SPI_MASTER && I2C!=m) || I2C
|
||||
select REGMAP_I2C if I2C
|
||||
select REGMAP_SPI if SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5380, AD5381,
|
||||
AD5382, AD5383, AD5384, AD5390, AD5391, AD5392 multi-channel
|
||||
Digital to Analog Converters (DAC).
|
||||
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad5380.
|
||||
|
||||
config AD5421
|
||||
tristate "Analog Devices AD5421 DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5421 loop-powered
|
||||
digital-to-analog convertors (DAC).
|
||||
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad5421.
|
||||
|
||||
config AD5446
|
||||
tristate "Analog Devices AD5446 and similar single channel DACs driver"
|
||||
depends on (SPI_MASTER && I2C!=m) || I2C
|
||||
help
|
||||
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, AD5641, AD5660, AD5662 DACs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5446.
|
||||
|
||||
config AD5449
|
||||
tristate "Analog Devices AD5449 and similar DACs driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5415, AD5426, AD5429,
|
||||
AD5432, AD5439, AD5443, AD5449 Digital to Analog Converters.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5449.
|
||||
|
||||
config AD5504
|
||||
tristate "Analog Devices AD5504/AD5501 DAC SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5504, AD5501,
|
||||
High Voltage Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5504.
|
||||
|
||||
config AD5624R_SPI
|
||||
tristate "Analog Devices AD5624/44/64R DAC spi driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5624R, AD5644R and
|
||||
AD5664R converters (DAC). This driver uses the common SPI interface.
|
||||
|
||||
config AD5686
|
||||
tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5686R, AD5685R,
|
||||
AD5684R, AD5791 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
|
||||
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
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744,
|
||||
AD5744R Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5764.
|
||||
|
||||
config AD5791
|
||||
tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5760, AD5780,
|
||||
AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to
|
||||
Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5791.
|
||||
|
||||
config AD7303
|
||||
tristate "Analog Devices AD7303 DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7303 Digital to Analog
|
||||
Converters (DAC).
|
||||
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad7303.
|
||||
|
||||
config MAX517
|
||||
tristate "Maxim MAX517/518/519 DAC driver"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Maxim chips MAX517,
|
||||
MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs).
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max517.
|
||||
|
||||
config MAX5821
|
||||
tristate "Maxim MAX5821 DAC driver"
|
||||
depends on I2C
|
||||
depends on OF
|
||||
help
|
||||
Say yes here to build support for Maxim MAX5821
|
||||
10 bits DAC.
|
||||
|
||||
config MCP4725
|
||||
tristate "MCP4725 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
|
||||
interface.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mcp4725.
|
||||
|
||||
config MCP4922
|
||||
tristate "MCP4902, MCP4912, MCP4922 DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build the driver for the Microchip MCP4902
|
||||
MCP4912, and MCP4922 DAC devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mcp4922.
|
||||
|
||||
endmenu
|
22
drivers/iio/dac/Makefile
Normal file
22
drivers/iio/dac/Makefile
Normal file
|
@ -0,0 +1,22 @@
|
|||
#
|
||||
# Makefile for industrial I/O DAC drivers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD5360) += ad5360.o
|
||||
obj-$(CONFIG_AD5380) += ad5380.o
|
||||
obj-$(CONFIG_AD5421) += ad5421.o
|
||||
obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
|
||||
obj-$(CONFIG_AD5064) += ad5064.o
|
||||
obj-$(CONFIG_AD5504) += ad5504.o
|
||||
obj-$(CONFIG_AD5446) += ad5446.o
|
||||
obj-$(CONFIG_AD5449) += ad5449.o
|
||||
obj-$(CONFIG_AD5755) += ad5755.o
|
||||
obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
obj-$(CONFIG_AD7303) += ad7303.o
|
||||
obj-$(CONFIG_MAX517) += max517.o
|
||||
obj-$(CONFIG_MAX5821) += max5821.o
|
||||
obj-$(CONFIG_MCP4725) += mcp4725.o
|
||||
obj-$(CONFIG_MCP4922) += mcp4922.o
|
684
drivers/iio/dac/ad5064.c
Normal file
684
drivers/iio/dac/ad5064.c
Normal file
|
@ -0,0 +1,684 @@
|
|||
/*
|
||||
* AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R,
|
||||
* AD5648, AD5666, AD5668, AD5669R Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2011 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/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>
|
||||
|
||||
#define AD5064_MAX_DAC_CHANNELS 8
|
||||
#define AD5064_MAX_VREFS 4
|
||||
|
||||
#define AD5064_ADDR(x) ((x) << 20)
|
||||
#define AD5064_CMD(x) ((x) << 24)
|
||||
|
||||
#define AD5064_ADDR_ALL_DAC 0xF
|
||||
|
||||
#define AD5064_CMD_WRITE_INPUT_N 0x0
|
||||
#define AD5064_CMD_UPDATE_DAC_N 0x1
|
||||
#define AD5064_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
|
||||
#define AD5064_CMD_WRITE_INPUT_N_UPDATE_N 0x3
|
||||
#define AD5064_CMD_POWERDOWN_DAC 0x4
|
||||
#define AD5064_CMD_CLEAR 0x5
|
||||
#define AD5064_CMD_LDAC_MASK 0x6
|
||||
#define AD5064_CMD_RESET 0x7
|
||||
#define AD5064_CMD_CONFIG 0x8
|
||||
|
||||
#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1)
|
||||
#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0)
|
||||
|
||||
#define AD5064_LDAC_PWRDN_NONE 0x0
|
||||
#define AD5064_LDAC_PWRDN_1K 0x1
|
||||
#define AD5064_LDAC_PWRDN_100K 0x2
|
||||
#define AD5064_LDAC_PWRDN_3STATE 0x3
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @channel: channel specification
|
||||
* @num_channels: number of channels
|
||||
*/
|
||||
|
||||
struct ad5064_chip_info {
|
||||
bool shared_vref;
|
||||
unsigned long internal_vref;
|
||||
const struct iio_chan_spec *channels;
|
||||
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
|
||||
* @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.
|
||||
* @write: register write callback
|
||||
* @data: i2c/spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5064_state {
|
||||
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.
|
||||
*/
|
||||
union {
|
||||
u8 i2c[3];
|
||||
__be32 spi;
|
||||
} data ____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad5064_type {
|
||||
ID_AD5024,
|
||||
ID_AD5025,
|
||||
ID_AD5044,
|
||||
ID_AD5045,
|
||||
ID_AD5064,
|
||||
ID_AD5064_1,
|
||||
ID_AD5065,
|
||||
ID_AD5628_1,
|
||||
ID_AD5628_2,
|
||||
ID_AD5648_1,
|
||||
ID_AD5648_2,
|
||||
ID_AD5666_1,
|
||||
ID_AD5666_2,
|
||||
ID_AD5668_1,
|
||||
ID_AD5668_2,
|
||||
};
|
||||
|
||||
static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
|
||||
unsigned int addr, unsigned int val, unsigned int shift)
|
||||
{
|
||||
val <<= shift;
|
||||
|
||||
return st->write(st, cmd, addr, val);
|
||||
}
|
||||
|
||||
static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
val = (0x1 << chan->address);
|
||||
|
||||
if (st->pwr_down[chan->channel])
|
||||
val |= st->pwr_down_mode[chan->channel] << 8;
|
||||
|
||||
ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const ad5064_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode[chan->channel] - 1;
|
||||
}
|
||||
|
||||
static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->pwr_down_mode[chan->channel] = mode + 1;
|
||||
|
||||
ret = ad5064_sync_powerdown_mode(st, chan);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5064_powerdown_mode_enum = {
|
||||
.items = ad5064_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5064_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)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down[chan->channel]);
|
||||
}
|
||||
|
||||
static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->pwr_down[chan->channel] = pwr_down;
|
||||
|
||||
ret = ad5064_sync_powerdown_mode(st, chan);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static int ad5064_get_vref(struct ad5064_state *st,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (st->use_internal_vref)
|
||||
return st->chip_info->internal_vref;
|
||||
|
||||
i = st->chip_info->shared_vref ? 0 : chan->channel;
|
||||
return regulator_get_voltage(st->vref_reg[i].consumer);
|
||||
}
|
||||
|
||||
static int ad5064_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
int scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = st->dac_cache[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = ad5064_get_vref(st, chan);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5064_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
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;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5064_info = {
|
||||
.read_raw = ad5064_read_raw,
|
||||
.write_raw = ad5064_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5064_read_dac_powerdown,
|
||||
.write = ad5064_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5064_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5064_CHANNEL(chan, addr, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = addr, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 20 - bits, \
|
||||
}, \
|
||||
.ext_info = ad5064_ext_info, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5064_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD5064_CHANNEL(0, 0, bits), \
|
||||
AD5064_CHANNEL(1, 1, bits), \
|
||||
AD5064_CHANNEL(2, 2, bits), \
|
||||
AD5064_CHANNEL(3, 3, bits), \
|
||||
AD5064_CHANNEL(4, 4, bits), \
|
||||
AD5064_CHANNEL(5, 5, bits), \
|
||||
AD5064_CHANNEL(6, 6, bits), \
|
||||
AD5064_CHANNEL(7, 7, bits), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5065_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD5064_CHANNEL(0, 0, bits), \
|
||||
AD5064_CHANNEL(1, 3, bits), \
|
||||
}
|
||||
|
||||
static DECLARE_AD5064_CHANNELS(ad5024_channels, 12);
|
||||
static DECLARE_AD5064_CHANNELS(ad5044_channels, 14);
|
||||
static DECLARE_AD5064_CHANNELS(ad5064_channels, 16);
|
||||
|
||||
static DECLARE_AD5065_CHANNELS(ad5025_channels, 12);
|
||||
static DECLARE_AD5065_CHANNELS(ad5045_channels, 14);
|
||||
static DECLARE_AD5065_CHANNELS(ad5065_channels, 16);
|
||||
|
||||
static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {
|
||||
[ID_AD5024] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5025] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5025_channels,
|
||||
.num_channels = 2,
|
||||
},
|
||||
[ID_AD5044] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5045] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5045_channels,
|
||||
.num_channels = 2,
|
||||
},
|
||||
[ID_AD5064] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5064_1] = {
|
||||
.shared_vref = true,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5065] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5065_channels,
|
||||
.num_channels = 2,
|
||||
},
|
||||
[ID_AD5628_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5628_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5648_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5648_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5666_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5666_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5668_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5668_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static inline unsigned int ad5064_num_vref(struct ad5064_state *st)
|
||||
{
|
||||
return st->chip_info->shared_vref ? 1 : st->chip_info->num_channels;
|
||||
}
|
||||
|
||||
static const char * const ad5064_vref_names[] = {
|
||||
"vrefA",
|
||||
"vrefB",
|
||||
"vrefC",
|
||||
"vrefD",
|
||||
};
|
||||
|
||||
static const char * const ad5064_vref_name(struct ad5064_state *st,
|
||||
unsigned int vref)
|
||||
{
|
||||
return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref];
|
||||
}
|
||||
|
||||
static int ad5064_probe(struct device *dev, enum ad5064_type type,
|
||||
const char *name, ad5064_write_func write)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5064_state *st;
|
||||
unsigned int midscale;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
st->chip_info = &ad5064_chip_info_tbl[type];
|
||||
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 = devm_regulator_bulk_get(dev, ad5064_num_vref(st),
|
||||
st->vref_reg);
|
||||
if (ret) {
|
||||
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);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable internal vref: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
midscale = (1 << indio_dev->channels[0].scan_type.realbits) / 2;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_channels; ++i) {
|
||||
st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K;
|
||||
st->dac_cache[i] = midscale;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!st->use_internal_vref)
|
||||
regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5064_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (!st->use_internal_vref)
|
||||
regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_MASTER)
|
||||
|
||||
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_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 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},
|
||||
{"ad5045", ID_AD5045},
|
||||
{"ad5064", ID_AD5064},
|
||||
{"ad5064-1", ID_AD5064_1},
|
||||
{"ad5065", ID_AD5065},
|
||||
{"ad5628-1", ID_AD5628_1},
|
||||
{"ad5628-2", ID_AD5628_2},
|
||||
{"ad5648-1", ID_AD5648_1},
|
||||
{"ad5648-2", ID_AD5648_2},
|
||||
{"ad5666-1", ID_AD5666_1},
|
||||
{"ad5666-2", ID_AD5666_2},
|
||||
{"ad5668-1", ID_AD5668_1},
|
||||
{"ad5668-2", ID_AD5668_2},
|
||||
{"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5064_spi_ids);
|
||||
|
||||
static struct spi_driver ad5064_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5064",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5064_spi_probe,
|
||||
.remove = ad5064_spi_remove,
|
||||
.id_table = ad5064_spi_ids,
|
||||
};
|
||||
|
||||
static int __init ad5064_spi_register_driver(void)
|
||||
{
|
||||
return spi_register_driver(&ad5064_spi_driver);
|
||||
}
|
||||
|
||||
static void 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 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_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 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 = 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 and similar multi-channel DACs");
|
||||
MODULE_LICENSE("GPL v2");
|
562
drivers/iio/dac/ad5360.c
Normal file
562
drivers/iio/dac/ad5360.c
Normal file
|
@ -0,0 +1,562 @@
|
|||
/*
|
||||
* Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373
|
||||
* multi-channel Digital to Analog Converters driver
|
||||
*
|
||||
* Copyright 2011 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/regulator/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5360_CMD(x) ((x) << 22)
|
||||
#define AD5360_ADDR(x) ((x) << 16)
|
||||
|
||||
#define AD5360_READBACK_TYPE(x) ((x) << 13)
|
||||
#define AD5360_READBACK_ADDR(x) ((x) << 7)
|
||||
|
||||
#define AD5360_CHAN_ADDR(chan) ((chan) + 0x8)
|
||||
|
||||
#define AD5360_CMD_WRITE_DATA 0x3
|
||||
#define AD5360_CMD_WRITE_OFFSET 0x2
|
||||
#define AD5360_CMD_WRITE_GAIN 0x1
|
||||
#define AD5360_CMD_SPECIAL_FUNCTION 0x0
|
||||
|
||||
/* Special function register addresses */
|
||||
#define AD5360_REG_SF_NOP 0x0
|
||||
#define AD5360_REG_SF_CTRL 0x1
|
||||
#define AD5360_REG_SF_OFS(x) (0x2 + (x))
|
||||
#define AD5360_REG_SF_READBACK 0x5
|
||||
|
||||
#define AD5360_SF_CTRL_PWR_DOWN BIT(0)
|
||||
|
||||
#define AD5360_READBACK_X1A 0x0
|
||||
#define AD5360_READBACK_X1B 0x1
|
||||
#define AD5360_READBACK_OFFSET 0x2
|
||||
#define AD5360_READBACK_GAIN 0x3
|
||||
#define AD5360_READBACK_SF 0x4
|
||||
|
||||
|
||||
/**
|
||||
* struct ad5360_chip_info - chip specific information
|
||||
* @channel_template: channel specification template
|
||||
* @num_channels: number of channels
|
||||
* @channels_per_group: number of channels per group
|
||||
* @num_vrefs: number of vref supplies for the chip
|
||||
*/
|
||||
|
||||
struct ad5360_chip_info {
|
||||
struct iio_chan_spec channel_template;
|
||||
unsigned int num_channels;
|
||||
unsigned int channels_per_group;
|
||||
unsigned int num_vrefs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5360_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @vref_reg: vref supply regulators
|
||||
* @ctrl: control register cache
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5360_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad5360_chip_info *chip_info;
|
||||
struct regulator_bulk_data vref_reg[3];
|
||||
unsigned int ctrl;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad5360_type {
|
||||
ID_AD5360,
|
||||
ID_AD5361,
|
||||
ID_AD5362,
|
||||
ID_AD5363,
|
||||
ID_AD5370,
|
||||
ID_AD5371,
|
||||
ID_AD5372,
|
||||
ID_AD5373,
|
||||
};
|
||||
|
||||
#define AD5360_CHANNEL(bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct ad5360_chip_info ad5360_chip_info_tbl[] = {
|
||||
[ID_AD5360] = {
|
||||
.channel_template = AD5360_CHANNEL(16),
|
||||
.num_channels = 16,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5361] = {
|
||||
.channel_template = AD5360_CHANNEL(14),
|
||||
.num_channels = 16,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5362] = {
|
||||
.channel_template = AD5360_CHANNEL(16),
|
||||
.num_channels = 8,
|
||||
.channels_per_group = 4,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5363] = {
|
||||
.channel_template = AD5360_CHANNEL(14),
|
||||
.num_channels = 8,
|
||||
.channels_per_group = 4,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5370] = {
|
||||
.channel_template = AD5360_CHANNEL(16),
|
||||
.num_channels = 40,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5371] = {
|
||||
.channel_template = AD5360_CHANNEL(14),
|
||||
.num_channels = 40,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 3,
|
||||
},
|
||||
[ID_AD5372] = {
|
||||
.channel_template = AD5360_CHANNEL(16),
|
||||
.num_channels = 32,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5373] = {
|
||||
.channel_template = AD5360_CHANNEL(14),
|
||||
.num_channels = 32,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st,
|
||||
unsigned int channel)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* The first groups have their own vref, while the remaining groups
|
||||
* share the last vref */
|
||||
i = channel / st->chip_info->channels_per_group;
|
||||
if (i >= st->chip_info->num_vrefs)
|
||||
i = st->chip_info->num_vrefs - 1;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int ad5360_get_channel_vref(struct ad5360_state *st,
|
||||
unsigned int channel)
|
||||
{
|
||||
unsigned int i = ad5360_get_channel_vref_index(st, channel);
|
||||
|
||||
return regulator_get_voltage(st->vref_reg[i].consumer);
|
||||
}
|
||||
|
||||
|
||||
static int ad5360_write_unlocked(struct iio_dev *indio_dev,
|
||||
unsigned int cmd, unsigned int addr, unsigned int val,
|
||||
unsigned int shift)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
|
||||
val <<= shift;
|
||||
val |= AD5360_CMD(cmd) | AD5360_ADDR(addr);
|
||||
st->data[0].d32 = cpu_to_be32(val);
|
||||
|
||||
return spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
}
|
||||
|
||||
static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd,
|
||||
unsigned int addr, unsigned int val, unsigned int shift)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5360_read(struct iio_dev *indio_dev, unsigned int type,
|
||||
unsigned int addr)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->data[1].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) |
|
||||
AD5360_ADDR(AD5360_REG_SF_READBACK) |
|
||||
AD5360_READBACK_TYPE(type) |
|
||||
AD5360_READBACK_ADDR(addr));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
ret = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad5360_read_dac_powerdown(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN));
|
||||
}
|
||||
|
||||
static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set,
|
||||
unsigned int clr)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
unsigned int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->ctrl |= set;
|
||||
st->ctrl &= ~clr;
|
||||
|
||||
ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION,
|
||||
AD5360_REG_SF_CTRL, st->ctrl, 0);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad5360_write_dac_powerdown(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pwr_down)
|
||||
ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0);
|
||||
else
|
||||
ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(out_voltage_powerdown,
|
||||
S_IRUGO | S_IWUSR,
|
||||
ad5360_read_dac_powerdown,
|
||||
ad5360_write_dac_powerdown, 0);
|
||||
|
||||
static struct attribute *ad5360_attributes[] = {
|
||||
&iio_dev_attr_out_voltage_powerdown.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ad5360_attribute_group = {
|
||||
.attrs = ad5360_attributes,
|
||||
};
|
||||
|
||||
static int ad5360_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
int max_val = (1 << chan->scan_type.realbits);
|
||||
unsigned int ofs_index;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA,
|
||||
chan->address, val, chan->scan_type.shift);
|
||||
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET,
|
||||
chan->address, val, chan->scan_type.shift);
|
||||
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN,
|
||||
chan->address, val, chan->scan_type.shift);
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (val <= -max_val || val > 0)
|
||||
return -EINVAL;
|
||||
|
||||
val = -val;
|
||||
|
||||
/* offset is supposed to have the same scale as raw, but it
|
||||
* is always 14bits wide, so on a chip where the raw value has
|
||||
* more bits, we need to shift offset. */
|
||||
val >>= (chan->scan_type.realbits - 14);
|
||||
|
||||
/* There is one DAC offset register per vref. Changing one
|
||||
* channels offset will also change the offset for all other
|
||||
* channels which share the same vref supply. */
|
||||
ofs_index = ad5360_get_channel_vref_index(st, chan->channel);
|
||||
return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION,
|
||||
AD5360_REG_SF_OFS(ofs_index), val, 0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5360_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
unsigned int ofs_index;
|
||||
int scale_uv;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5360_read(indio_dev, AD5360_READBACK_X1A,
|
||||
chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret >> chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = ad5360_get_channel_vref(st, chan->channel);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
/* vout = 4 * vref * dac_code */
|
||||
*val = scale_uv * 4 / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET,
|
||||
chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN,
|
||||
chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ofs_index = ad5360_get_channel_vref_index(st, chan->channel);
|
||||
ret = ad5360_read(indio_dev, AD5360_READBACK_SF,
|
||||
AD5360_REG_SF_OFS(ofs_index));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret <<= (chan->scan_type.realbits - 14);
|
||||
*val = -ret;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5360_info = {
|
||||
.read_raw = ad5360_read_raw,
|
||||
.write_raw = ad5360_write_raw,
|
||||
.attrs = &ad5360_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const ad5360_vref_name[] = {
|
||||
"vref0", "vref1", "vref2"
|
||||
};
|
||||
|
||||
static int ad5360_alloc_channels(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *channels;
|
||||
unsigned int i;
|
||||
|
||||
channels = kcalloc(st->chip_info->num_channels,
|
||||
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
||||
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_channels; ++i) {
|
||||
channels[i] = st->chip_info->channel_template;
|
||||
channels[i].channel = i;
|
||||
channels[i].address = AD5360_CHAN_ADDR(i);
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5360_probe(struct spi_device *spi)
|
||||
{
|
||||
enum ad5360_type type = spi_get_device_id(spi)->driver_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5360_state *st;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, 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 = &ad5360_chip_info_tbl[type];
|
||||
st->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5360_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
ret = ad5360_alloc_channels(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < st->chip_info->num_vrefs; ++i)
|
||||
st->vref_reg[i].supply = ad5360_vref_name[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs,
|
||||
st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret);
|
||||
goto error_free_channels;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret);
|
||||
goto error_free_channels;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg);
|
||||
error_free_channels:
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5360_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5360_ids[] = {
|
||||
{ "ad5360", ID_AD5360 },
|
||||
{ "ad5361", ID_AD5361 },
|
||||
{ "ad5362", ID_AD5362 },
|
||||
{ "ad5363", ID_AD5363 },
|
||||
{ "ad5370", ID_AD5370 },
|
||||
{ "ad5371", ID_AD5371 },
|
||||
{ "ad5372", ID_AD5372 },
|
||||
{ "ad5373", ID_AD5373 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5360_ids);
|
||||
|
||||
static struct spi_driver ad5360_driver = {
|
||||
.driver = {
|
||||
.name = "ad5360",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5360_probe,
|
||||
.remove = ad5360_remove,
|
||||
.id_table = ad5360_ids,
|
||||
};
|
||||
module_spi_driver(ad5360_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
654
drivers/iio/dac/ad5380.c
Normal file
654
drivers/iio/dac/ad5380.c
Normal file
|
@ -0,0 +1,654 @@
|
|||
/*
|
||||
* Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392
|
||||
* multi-channel Digital to Analog Converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5380_REG_DATA(x) (((x) << 2) | 3)
|
||||
#define AD5380_REG_OFFSET(x) (((x) << 2) | 2)
|
||||
#define AD5380_REG_GAIN(x) (((x) << 2) | 1)
|
||||
#define AD5380_REG_SF_PWR_DOWN (8 << 2)
|
||||
#define AD5380_REG_SF_PWR_UP (9 << 2)
|
||||
#define AD5380_REG_SF_CTRL (12 << 2)
|
||||
|
||||
#define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13
|
||||
#define AD5380_CTRL_INT_VREF_2V5 BIT(12)
|
||||
#define AD5380_CTRL_INT_VREF_EN BIT(10)
|
||||
|
||||
/**
|
||||
* struct ad5380_chip_info - chip specific information
|
||||
* @channel_template: channel specification template
|
||||
* @num_channels: number of channels
|
||||
* @int_vref: internal vref in uV
|
||||
*/
|
||||
|
||||
struct ad5380_chip_info {
|
||||
struct iio_chan_spec channel_template;
|
||||
unsigned int num_channels;
|
||||
unsigned int int_vref;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5380_state - driver instance specific data
|
||||
* @regmap: regmap instance used by the device
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @vref_reg: vref supply regulator
|
||||
* @vref: actual reference voltage used in uA
|
||||
* @pwr_down: whether the chip is currently in power down mode
|
||||
*/
|
||||
|
||||
struct ad5380_state {
|
||||
struct regmap *regmap;
|
||||
const struct ad5380_chip_info *chip_info;
|
||||
struct regulator *vref_reg;
|
||||
int vref;
|
||||
bool pwr_down;
|
||||
};
|
||||
|
||||
enum ad5380_type {
|
||||
ID_AD5380_3,
|
||||
ID_AD5380_5,
|
||||
ID_AD5381_3,
|
||||
ID_AD5381_5,
|
||||
ID_AD5382_3,
|
||||
ID_AD5382_5,
|
||||
ID_AD5383_3,
|
||||
ID_AD5383_5,
|
||||
ID_AD5390_3,
|
||||
ID_AD5390_5,
|
||||
ID_AD5391_3,
|
||||
ID_AD5391_5,
|
||||
ID_AD5392_3,
|
||||
ID_AD5392_5,
|
||||
};
|
||||
|
||||
static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down);
|
||||
}
|
||||
|
||||
static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (pwr_down)
|
||||
ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0);
|
||||
else
|
||||
ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_UP, 0);
|
||||
|
||||
st->pwr_down = pwr_down;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const char * const ad5380_powerdown_modes[] = {
|
||||
"100kohm_to_gnd",
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
unsigned int mode;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(st->regmap, AD5380_REG_SF_CTRL, &mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(st->regmap, AD5380_REG_SF_CTRL,
|
||||
1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET,
|
||||
mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5380_powerdown_mode_enum = {
|
||||
.items = ad5380_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5380_powerdown_modes),
|
||||
.get = ad5380_get_powerdown_mode,
|
||||
.set = ad5380_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan,
|
||||
long info)
|
||||
{
|
||||
switch (info) {
|
||||
case 0:
|
||||
return AD5380_REG_DATA(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return AD5380_REG_OFFSET(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
return AD5380_REG_GAIN(chan->address);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5380_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long info)
|
||||
{
|
||||
const unsigned int max_val = (1 << chan->scan_type.realbits);
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(st->regmap,
|
||||
ad5380_info_to_reg(chan, info),
|
||||
val << chan->scan_type.shift);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
val += (1 << chan->scan_type.realbits) / 2;
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(st->regmap,
|
||||
AD5380_REG_OFFSET(chan->address),
|
||||
val << chan->scan_type.shift);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5380_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
ret = regmap_read(st->regmap, ad5380_info_to_reg(chan, info),
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val >>= chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = regmap_read(st->regmap, AD5380_REG_OFFSET(chan->address),
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val >>= chan->scan_type.shift;
|
||||
val -= (1 << chan->scan_type.realbits) / 2;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 2 * st->vref;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5380_info = {
|
||||
.read_raw = ad5380_read_raw,
|
||||
.write_raw = ad5380_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct iio_chan_spec_ext_info ad5380_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5380_read_dac_powerdown,
|
||||
.write = ad5380_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5380_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5380_CHANNEL(_bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 14 - (_bits), \
|
||||
}, \
|
||||
.ext_info = ad5380_ext_info, \
|
||||
}
|
||||
|
||||
static const struct ad5380_chip_info ad5380_chip_info_tbl[] = {
|
||||
[ID_AD5380_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 40,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5380_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 40,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5381_3] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5381_5] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5382_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 32,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5382_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 32,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5383_3] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 32,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5383_5] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 32,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5390_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 16,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5390_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 16,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5391_3] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5391_5] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5392_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 8,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5392_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 8,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5380_alloc_channels(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *channels;
|
||||
unsigned int i;
|
||||
|
||||
channels = kcalloc(st->chip_info->num_channels,
|
||||
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
||||
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_channels; ++i) {
|
||||
channels[i] = st->chip_info->channel_template;
|
||||
channels[i].channel = i;
|
||||
channels[i].address = i;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5380_probe(struct device *dev, struct regmap *regmap,
|
||||
enum ad5380_type type, const char *name)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5380_state *st;
|
||||
unsigned int ctrl = 0;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
dev_err(dev, "Failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
st->chip_info = &ad5380_chip_info_tbl[type];
|
||||
st->regmap = regmap;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &ad5380_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
ret = ad5380_alloc_channels(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to allocate channel spec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (st->chip_info->int_vref == 2500)
|
||||
ctrl |= AD5380_CTRL_INT_VREF_2V5;
|
||||
|
||||
st->vref_reg = devm_regulator_get(dev, "vref");
|
||||
if (!IS_ERR(st->vref_reg)) {
|
||||
ret = regulator_enable(st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable vref regulators: %d\n",
|
||||
ret);
|
||||
goto error_free_reg;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(st->vref_reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->vref = ret / 1000;
|
||||
} else {
|
||||
st->vref = st->chip_info->int_vref;
|
||||
ctrl |= AD5380_CTRL_INT_VREF_EN;
|
||||
}
|
||||
|
||||
ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to write to device: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register iio device: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->vref_reg))
|
||||
regulator_disable(st->vref_reg);
|
||||
error_free_reg:
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5380_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
if (!IS_ERR(st->vref_reg)) {
|
||||
regulator_disable(st->vref_reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ad5380_reg_false(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config ad5380_regmap_config = {
|
||||
.reg_bits = 10,
|
||||
.val_bits = 14,
|
||||
|
||||
.max_register = AD5380_REG_DATA(40),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
|
||||
.volatile_reg = ad5380_reg_false,
|
||||
.readable_reg = ad5380_reg_false,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_MASTER)
|
||||
|
||||
static int ad5380_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &ad5380_regmap_config);
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name);
|
||||
}
|
||||
|
||||
static int ad5380_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return ad5380_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5380_spi_ids[] = {
|
||||
{ "ad5380-3", ID_AD5380_3 },
|
||||
{ "ad5380-5", ID_AD5380_5 },
|
||||
{ "ad5381-3", ID_AD5381_3 },
|
||||
{ "ad5381-5", ID_AD5381_5 },
|
||||
{ "ad5382-3", ID_AD5382_3 },
|
||||
{ "ad5382-5", ID_AD5382_5 },
|
||||
{ "ad5383-3", ID_AD5383_3 },
|
||||
{ "ad5383-5", ID_AD5383_5 },
|
||||
{ "ad5384-3", ID_AD5380_3 },
|
||||
{ "ad5384-5", ID_AD5380_5 },
|
||||
{ "ad5390-3", ID_AD5390_3 },
|
||||
{ "ad5390-5", ID_AD5390_5 },
|
||||
{ "ad5391-3", ID_AD5391_3 },
|
||||
{ "ad5391-5", ID_AD5391_5 },
|
||||
{ "ad5392-3", ID_AD5392_3 },
|
||||
{ "ad5392-5", ID_AD5392_5 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5380_spi_ids);
|
||||
|
||||
static struct spi_driver ad5380_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5380",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5380_spi_probe,
|
||||
.remove = ad5380_spi_remove,
|
||||
.id_table = ad5380_spi_ids,
|
||||
};
|
||||
|
||||
static inline int ad5380_spi_register_driver(void)
|
||||
{
|
||||
return spi_register_driver(&ad5380_spi_driver);
|
||||
}
|
||||
|
||||
static inline void ad5380_spi_unregister_driver(void)
|
||||
{
|
||||
spi_unregister_driver(&ad5380_spi_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int ad5380_spi_register_driver(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ad5380_spi_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
|
||||
static int ad5380_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &ad5380_regmap_config);
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name);
|
||||
}
|
||||
|
||||
static int ad5380_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return ad5380_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad5380_i2c_ids[] = {
|
||||
{ "ad5380-3", ID_AD5380_3 },
|
||||
{ "ad5380-5", ID_AD5380_5 },
|
||||
{ "ad5381-3", ID_AD5381_3 },
|
||||
{ "ad5381-5", ID_AD5381_5 },
|
||||
{ "ad5382-3", ID_AD5382_3 },
|
||||
{ "ad5382-5", ID_AD5382_5 },
|
||||
{ "ad5383-3", ID_AD5383_3 },
|
||||
{ "ad5383-5", ID_AD5383_5 },
|
||||
{ "ad5384-3", ID_AD5380_3 },
|
||||
{ "ad5384-5", ID_AD5380_5 },
|
||||
{ "ad5390-3", ID_AD5390_3 },
|
||||
{ "ad5390-5", ID_AD5390_5 },
|
||||
{ "ad5391-3", ID_AD5391_3 },
|
||||
{ "ad5391-5", ID_AD5391_5 },
|
||||
{ "ad5392-3", ID_AD5392_3 },
|
||||
{ "ad5392-5", ID_AD5392_5 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids);
|
||||
|
||||
static struct i2c_driver ad5380_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ad5380",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5380_i2c_probe,
|
||||
.remove = ad5380_i2c_remove,
|
||||
.id_table = ad5380_i2c_ids,
|
||||
};
|
||||
|
||||
static inline int ad5380_i2c_register_driver(void)
|
||||
{
|
||||
return i2c_add_driver(&ad5380_i2c_driver);
|
||||
}
|
||||
|
||||
static inline void ad5380_i2c_unregister_driver(void)
|
||||
{
|
||||
i2c_del_driver(&ad5380_i2c_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int ad5380_i2c_register_driver(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ad5380_i2c_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int __init ad5380_spi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5380_spi_register_driver();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5380_i2c_register_driver();
|
||||
if (ret) {
|
||||
ad5380_spi_unregister_driver();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(ad5380_spi_init);
|
||||
|
||||
static void __exit ad5380_spi_exit(void)
|
||||
{
|
||||
ad5380_i2c_unregister_driver();
|
||||
ad5380_spi_unregister_driver();
|
||||
|
||||
}
|
||||
module_exit(ad5380_spi_exit);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
536
drivers/iio/dac/ad5421.c
Normal file
536
drivers/iio/dac/ad5421.c
Normal file
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
* AD5421 Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/dac/ad5421.h>
|
||||
|
||||
|
||||
#define AD5421_REG_DAC_DATA 0x1
|
||||
#define AD5421_REG_CTRL 0x2
|
||||
#define AD5421_REG_OFFSET 0x3
|
||||
#define AD5421_REG_GAIN 0x4
|
||||
/* load dac and fault shared the same register number. Writing to it will cause
|
||||
* a dac load command, reading from it will return the fault status register */
|
||||
#define AD5421_REG_LOAD_DAC 0x5
|
||||
#define AD5421_REG_FAULT 0x5
|
||||
#define AD5421_REG_FORCE_ALARM_CURRENT 0x6
|
||||
#define AD5421_REG_RESET 0x7
|
||||
#define AD5421_REG_START_CONVERSION 0x8
|
||||
#define AD5421_REG_NOOP 0x9
|
||||
|
||||
#define AD5421_CTRL_WATCHDOG_DISABLE BIT(12)
|
||||
#define AD5421_CTRL_AUTO_FAULT_READBACK BIT(11)
|
||||
#define AD5421_CTRL_MIN_CURRENT BIT(9)
|
||||
#define AD5421_CTRL_ADC_SOURCE_TEMP BIT(8)
|
||||
#define AD5421_CTRL_ADC_ENABLE BIT(7)
|
||||
#define AD5421_CTRL_PWR_DOWN_INT_VREF BIT(6)
|
||||
|
||||
#define AD5421_FAULT_SPI BIT(15)
|
||||
#define AD5421_FAULT_PEC BIT(14)
|
||||
#define AD5421_FAULT_OVER_CURRENT BIT(13)
|
||||
#define AD5421_FAULT_UNDER_CURRENT BIT(12)
|
||||
#define AD5421_FAULT_TEMP_OVER_140 BIT(11)
|
||||
#define AD5421_FAULT_TEMP_OVER_100 BIT(10)
|
||||
#define AD5421_FAULT_UNDER_VOLTAGE_6V BIT(9)
|
||||
#define AD5421_FAULT_UNDER_VOLTAGE_12V BIT(8)
|
||||
|
||||
/* These bits will cause the fault pin to go high */
|
||||
#define AD5421_FAULT_TRIGGER_IRQ \
|
||||
(AD5421_FAULT_SPI | AD5421_FAULT_PEC | AD5421_FAULT_OVER_CURRENT | \
|
||||
AD5421_FAULT_UNDER_CURRENT | AD5421_FAULT_TEMP_OVER_140)
|
||||
|
||||
/**
|
||||
* struct ad5421_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @ctrl: control register cache
|
||||
* @current_range: current range which the device is configured for
|
||||
* @data: spi transfer buffers
|
||||
* @fault_mask: software masking of events
|
||||
*/
|
||||
struct ad5421_state {
|
||||
struct spi_device *spi;
|
||||
unsigned int ctrl;
|
||||
enum ad5421_current_range current_range;
|
||||
unsigned int fault_mask;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static const struct iio_event_spec ad5421_current_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_event_spec ad5421_temp_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad5421_channels[] = {
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
},
|
||||
.event_spec = ad5421_current_event,
|
||||
.num_event_specs = ARRAY_SIZE(ad5421_current_event),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel = -1,
|
||||
.event_spec = ad5421_temp_event,
|
||||
.num_event_specs = ARRAY_SIZE(ad5421_temp_event),
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5421_write_unlocked(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct ad5421_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 ad5421_write(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5421_write_unlocked(indio_dev, reg, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->data[1].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
ret = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5421_update_ctrl(struct iio_dev *indio_dev, unsigned int set,
|
||||
unsigned int clr)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->ctrl &= ~clr;
|
||||
st->ctrl |= set;
|
||||
|
||||
ret = ad5421_write_unlocked(indio_dev, AD5421_REG_CTRL, st->ctrl);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t ad5421_fault_handler(int irq, void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int fault;
|
||||
unsigned int old_fault = 0;
|
||||
unsigned int events;
|
||||
|
||||
fault = ad5421_read(indio_dev, AD5421_REG_FAULT);
|
||||
if (!fault)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* If we had a fault, this might mean that the DAC has lost its state
|
||||
* and has been reset. Make sure that the control register actually
|
||||
* contains what we expect it to contain. Otherwise the watchdog might
|
||||
* be enabled and we get watchdog timeout faults, which will render the
|
||||
* DAC unusable. */
|
||||
ad5421_update_ctrl(indio_dev, 0, 0);
|
||||
|
||||
|
||||
/* The fault pin stays high as long as a fault condition is present and
|
||||
* it is not possible to mask fault conditions. For certain fault
|
||||
* conditions for example like over-temperature it takes some time
|
||||
* until the fault condition disappears. If we would exit the interrupt
|
||||
* handler immediately after handling the event it would be entered
|
||||
* again instantly. Thus we fall back to polling in case we detect that
|
||||
* a interrupt condition is still present.
|
||||
*/
|
||||
do {
|
||||
/* 0xffff is a invalid value for the register and will only be
|
||||
* read if there has been a communication error */
|
||||
if (fault == 0xffff)
|
||||
fault = 0;
|
||||
|
||||
/* we are only interested in new events */
|
||||
events = (old_fault ^ fault) & fault;
|
||||
events &= st->fault_mask;
|
||||
|
||||
if (events & AD5421_FAULT_OVER_CURRENT) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
|
||||
if (events & AD5421_FAULT_UNDER_CURRENT) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
|
||||
if (events & AD5421_FAULT_TEMP_OVER_140) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
|
||||
0,
|
||||
IIO_EV_TYPE_MAG,
|
||||
IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
|
||||
old_fault = fault;
|
||||
fault = ad5421_read(indio_dev, AD5421_REG_FAULT);
|
||||
|
||||
/* still active? go to sleep for some time */
|
||||
if (fault & AD5421_FAULT_TRIGGER_IRQ)
|
||||
msleep(1000);
|
||||
|
||||
} while (fault & AD5421_FAULT_TRIGGER_IRQ);
|
||||
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad5421_get_current_min_max(struct ad5421_state *st,
|
||||
unsigned int *min, unsigned int *max)
|
||||
{
|
||||
/* The current range is configured using external pins, which are
|
||||
* usually hard-wired and not run-time switchable. */
|
||||
switch (st->current_range) {
|
||||
case AD5421_CURRENT_RANGE_4mA_20mA:
|
||||
*min = 4000;
|
||||
*max = 20000;
|
||||
break;
|
||||
case AD5421_CURRENT_RANGE_3mA8_21mA:
|
||||
*min = 3800;
|
||||
*max = 21000;
|
||||
break;
|
||||
case AD5421_CURRENT_RANGE_3mA2_24mA:
|
||||
*min = 3200;
|
||||
*max = 24000;
|
||||
break;
|
||||
default:
|
||||
*min = 0;
|
||||
*max = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned int ad5421_get_offset(struct ad5421_state *st)
|
||||
{
|
||||
unsigned int min, max;
|
||||
|
||||
ad5421_get_current_min_max(st, &min, &max);
|
||||
return (min * (1 << 16)) / (max - min);
|
||||
}
|
||||
|
||||
static int ad5421_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long m)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int min, max;
|
||||
int ret;
|
||||
|
||||
if (chan->type != IIO_CURRENT)
|
||||
return -EINVAL;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ad5421_get_current_min_max(st, &min, &max);
|
||||
*val = max - min;
|
||||
*val2 = (1 << 16) * 1000;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = ad5421_get_offset(st);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = ad5421_read(indio_dev, AD5421_REG_OFFSET);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret - 32768;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
ret = ad5421_read(indio_dev, AD5421_REG_GAIN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5421_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
const unsigned int max_val = 1 << 16;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5421_write(indio_dev, AD5421_REG_DAC_DATA, val);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
val += 32768;
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5421_write(indio_dev, AD5421_REG_OFFSET, val);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5421_write(indio_dev, AD5421_REG_GAIN, val);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5421_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
mask = AD5421_FAULT_OVER_CURRENT;
|
||||
else
|
||||
mask = AD5421_FAULT_UNDER_CURRENT;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
mask = AD5421_FAULT_TEMP_OVER_140;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (state)
|
||||
st->fault_mask |= mask;
|
||||
else
|
||||
st->fault_mask &= ~mask;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5421_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
mask = AD5421_FAULT_OVER_CURRENT;
|
||||
else
|
||||
mask = AD5421_FAULT_UNDER_CURRENT;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
mask = AD5421_FAULT_TEMP_OVER_140;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return (bool)(st->fault_mask & mask);
|
||||
}
|
||||
|
||||
static int ad5421_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info, int *val,
|
||||
int *val2)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
*val = 140000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5421_info = {
|
||||
.read_raw = ad5421_read_raw,
|
||||
.write_raw = ad5421_write_raw,
|
||||
.read_event_config = ad5421_read_event_config,
|
||||
.write_event_config = ad5421_write_event_config,
|
||||
.read_event_value = ad5421_read_event_value,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad5421_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5421_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5421_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, 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->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = "ad5421";
|
||||
indio_dev->info = &ad5421_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad5421_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad5421_channels);
|
||||
|
||||
st->ctrl = AD5421_CTRL_WATCHDOG_DISABLE |
|
||||
AD5421_CTRL_AUTO_FAULT_READBACK;
|
||||
|
||||
if (pdata) {
|
||||
st->current_range = pdata->current_range;
|
||||
if (pdata->external_vref)
|
||||
st->ctrl |= AD5421_CTRL_PWR_DOWN_INT_VREF;
|
||||
} else {
|
||||
st->current_range = AD5421_CURRENT_RANGE_4mA_20mA;
|
||||
}
|
||||
|
||||
/* write initial ctrl register value */
|
||||
ad5421_update_ctrl(indio_dev, 0, 0);
|
||||
|
||||
if (spi->irq) {
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
NULL,
|
||||
ad5421_fault_handler,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
"ad5421 fault",
|
||||
indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct spi_driver ad5421_driver = {
|
||||
.driver = {
|
||||
.name = "ad5421",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5421_probe,
|
||||
};
|
||||
module_spi_driver(ad5421_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5421 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:ad5421");
|
623
drivers/iio/dac/ad5446.c
Normal file
623
drivers/iio/dac/ad5446.c
Normal file
|
@ -0,0 +1,623 @@
|
|||
/*
|
||||
* AD5446 SPI DAC driver
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#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>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#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 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
|
||||
static const char * const ad5446_powerdown_modes[] = {
|
||||
"1kohm_to_gnd", "100kohm_to_gnd", "three_state"
|
||||
};
|
||||
|
||||
static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode = mode + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode - 1;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5446_powerdown_mode_enum = {
|
||||
.items = ad5446_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5446_powerdown_modes),
|
||||
.get = ad5446_get_powerdown_mode,
|
||||
.set = ad5446_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down);
|
||||
}
|
||||
|
||||
static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
unsigned int shift;
|
||||
unsigned int val;
|
||||
bool powerdown;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &powerdown);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->pwr_down = powerdown;
|
||||
|
||||
if (st->pwr_down) {
|
||||
shift = chan->scan_type.realbits + chan->scan_type.shift;
|
||||
val = st->pwr_down_mode << shift;
|
||||
} else {
|
||||
val = st->cached_val;
|
||||
}
|
||||
|
||||
ret = st->chip_info->write(st, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5446_read_dac_powerdown,
|
||||
.write = ad5446_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define _AD5446_CHANNEL(bits, storage, _shift, ext) { \
|
||||
.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), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = (storage), \
|
||||
.shift = (_shift), \
|
||||
}, \
|
||||
.ext_info = (ext), \
|
||||
}
|
||||
|
||||
#define AD5446_CHANNEL(bits, storage, shift) \
|
||||
_AD5446_CHANNEL(bits, storage, shift, NULL)
|
||||
|
||||
#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \
|
||||
_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,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = st->cached_val;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5446_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
val <<= chan->scan_type.shift;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->cached_val = val;
|
||||
if (!st->pwr_down)
|
||||
ret = st->chip_info->write(st, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5446_info = {
|
||||
.read_raw = ad5446_read_raw,
|
||||
.write_raw = ad5446_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int 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 = devm_regulator_get(dev, "vcc");
|
||||
if (!IS_ERR(reg)) {
|
||||
ret = regulator_enable(reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
st = iio_priv(indio_dev);
|
||||
st->chip_info = chip_info;
|
||||
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
st->reg = reg;
|
||||
st->dev = dev;
|
||||
|
||||
/* 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;
|
||||
indio_dev->num_channels = 1;
|
||||
|
||||
st->pwr_down_mode = MODE_PWRDWN_1k;
|
||||
|
||||
if (st->chip_info->int_vref_mv)
|
||||
st->vref_mv = st->chip_info->int_vref_mv;
|
||||
else if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
dev_warn(dev, "reference voltage unspecified\n");
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(reg))
|
||||
regulator_disable(reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5446_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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_AD5641,
|
||||
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_AD5641] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
|
||||
.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},
|
||||
{"ad5451", ID_AD5451},
|
||||
{"ad5452", ID_AD5444}, /* ad5452 is compatible to the ad5444 */
|
||||
{"ad5453", ID_AD5446}, /* ad5453 is compatible to the ad5446 */
|
||||
{"ad5512a", ID_AD5512A},
|
||||
{"ad5541a", ID_AD5541A},
|
||||
{"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */
|
||||
{"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */
|
||||
{"ad5553", ID_AD5553},
|
||||
{"ad5601", ID_AD5601},
|
||||
{"ad5611", ID_AD5611},
|
||||
{"ad5621", ID_AD5621},
|
||||
{"ad5641", ID_AD5641},
|
||||
{"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */
|
||||
{"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */
|
||||
{"ad5640-2500", ID_AD5640_2500},
|
||||
{"ad5640-1250", ID_AD5640_1250},
|
||||
{"ad5660-2500", ID_AD5660_2500},
|
||||
{"ad5660-1250", ID_AD5660_1250},
|
||||
{"ad5662", ID_AD5662},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5446_spi_ids);
|
||||
|
||||
static int 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 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 = 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 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 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 = 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");
|
||||
MODULE_LICENSE("GPL v2");
|
369
drivers/iio/dac/ad5449.c
Normal file
369
drivers/iio/dac/ad5449.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog
|
||||
* Converter driver.
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* 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/regulator/consumer.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include <linux/platform_data/ad5449.h>
|
||||
|
||||
#define AD5449_MAX_CHANNELS 2
|
||||
#define AD5449_MAX_VREFS 2
|
||||
|
||||
#define AD5449_CMD_NOOP 0x0
|
||||
#define AD5449_CMD_LOAD_AND_UPDATE(x) (0x1 + (x) * 3)
|
||||
#define AD5449_CMD_READ(x) (0x2 + (x) * 3)
|
||||
#define AD5449_CMD_LOAD(x) (0x3 + (x) * 3)
|
||||
#define AD5449_CMD_CTRL 13
|
||||
|
||||
#define AD5449_CTRL_SDO_OFFSET 10
|
||||
#define AD5449_CTRL_DAISY_CHAIN BIT(9)
|
||||
#define AD5449_CTRL_HCLR_TO_MIDSCALE BIT(8)
|
||||
#define AD5449_CTRL_SAMPLE_RISING BIT(7)
|
||||
|
||||
/**
|
||||
* struct ad5449_chip_info - chip specific information
|
||||
* @channels: Channel specification
|
||||
* @num_channels: Number of channels
|
||||
* @has_ctrl: Chip has a control register
|
||||
*/
|
||||
struct ad5449_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
bool has_ctrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5449 - driver instance specific data
|
||||
* @spi: the SPI device for this driver instance
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @vref_reg: vref supply regulators
|
||||
* @has_sdo: whether the SDO line is connected
|
||||
* @dac_cache: Cache for the DAC values
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
struct ad5449 {
|
||||
struct spi_device *spi;
|
||||
const struct ad5449_chip_info *chip_info;
|
||||
struct regulator_bulk_data vref_reg[AD5449_MAX_VREFS];
|
||||
|
||||
bool has_sdo;
|
||||
uint16_t dac_cache[AD5449_MAX_CHANNELS];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad5449_type {
|
||||
ID_AD5426,
|
||||
ID_AD5429,
|
||||
ID_AD5432,
|
||||
ID_AD5439,
|
||||
ID_AD5443,
|
||||
ID_AD5449,
|
||||
};
|
||||
|
||||
static int ad5449_write(struct iio_dev *indio_dev, unsigned int addr,
|
||||
unsigned int val)
|
||||
{
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->data[0] = cpu_to_be16((addr << 12) | val);
|
||||
ret = spi_write(st->spi, st->data, 2);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0],
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &st->data[1],
|
||||
.rx_buf = &st->data[1],
|
||||
.len = 2,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->data[0] = cpu_to_be16(addr << 12);
|
||||
st->data[1] = cpu_to_be16(AD5449_CMD_NOOP);
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
*val = be16_to_cpu(st->data[1]);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5449_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
struct regulator_bulk_data *reg;
|
||||
int scale_uv;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (st->has_sdo) {
|
||||
ret = ad5449_read(indio_dev,
|
||||
AD5449_CMD_READ(chan->address), val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val &= 0xfff;
|
||||
} else {
|
||||
*val = st->dac_cache[chan->address];
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
reg = &st->vref_reg[chan->channel];
|
||||
scale_uv = regulator_get_voltage(reg->consumer);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5449_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long info)
|
||||
{
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val < 0 || val >= (1 << chan->scan_type.realbits))
|
||||
return -EINVAL;
|
||||
|
||||
ret = ad5449_write(indio_dev,
|
||||
AD5449_CMD_LOAD_AND_UPDATE(chan->address),
|
||||
val << chan->scan_type.shift);
|
||||
if (ret == 0)
|
||||
st->dac_cache[chan->address] = val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5449_info = {
|
||||
.read_raw = ad5449_read_raw,
|
||||
.write_raw = ad5449_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define AD5449_CHANNEL(chan, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5449_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD5449_CHANNEL(0, bits), \
|
||||
AD5449_CHANNEL(1, bits), \
|
||||
}
|
||||
|
||||
static DECLARE_AD5449_CHANNELS(ad5429_channels, 8);
|
||||
static DECLARE_AD5449_CHANNELS(ad5439_channels, 10);
|
||||
static DECLARE_AD5449_CHANNELS(ad5449_channels, 12);
|
||||
|
||||
static const struct ad5449_chip_info ad5449_chip_info[] = {
|
||||
[ID_AD5426] = {
|
||||
.channels = ad5429_channels,
|
||||
.num_channels = 1,
|
||||
.has_ctrl = false,
|
||||
},
|
||||
[ID_AD5429] = {
|
||||
.channels = ad5429_channels,
|
||||
.num_channels = 2,
|
||||
.has_ctrl = true,
|
||||
},
|
||||
[ID_AD5432] = {
|
||||
.channels = ad5439_channels,
|
||||
.num_channels = 1,
|
||||
.has_ctrl = false,
|
||||
},
|
||||
[ID_AD5439] = {
|
||||
.channels = ad5439_channels,
|
||||
.num_channels = 2,
|
||||
.has_ctrl = true,
|
||||
},
|
||||
[ID_AD5443] = {
|
||||
.channels = ad5449_channels,
|
||||
.num_channels = 1,
|
||||
.has_ctrl = false,
|
||||
},
|
||||
[ID_AD5449] = {
|
||||
.channels = ad5449_channels,
|
||||
.num_channels = 2,
|
||||
.has_ctrl = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const char *ad5449_vref_name(struct ad5449 *st, int n)
|
||||
{
|
||||
if (st->chip_info->num_channels == 1)
|
||||
return "VREF";
|
||||
|
||||
if (n == 0)
|
||||
return "VREFA";
|
||||
else
|
||||
return "VREFB";
|
||||
}
|
||||
|
||||
static int ad5449_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5449_platform_data *pdata = spi->dev.platform_data;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5449 *st;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->chip_info = &ad5449_chip_info[id->driver_data];
|
||||
st->spi = spi;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_channels; ++i)
|
||||
st->vref_reg[i].supply = ad5449_vref_name(st, i);
|
||||
|
||||
ret = devm_regulator_bulk_get(&spi->dev, st->chip_info->num_channels,
|
||||
st->vref_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_bulk_enable(st->chip_info->num_channels, st->vref_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = &ad5449_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
if (st->chip_info->has_ctrl) {
|
||||
unsigned int ctrl = 0x00;
|
||||
if (pdata) {
|
||||
if (pdata->hardware_clear_to_midscale)
|
||||
ctrl |= AD5449_CTRL_HCLR_TO_MIDSCALE;
|
||||
ctrl |= pdata->sdo_mode << AD5449_CTRL_SDO_OFFSET;
|
||||
st->has_sdo = pdata->sdo_mode != AD5449_SDO_DISABLED;
|
||||
} else {
|
||||
st->has_sdo = true;
|
||||
}
|
||||
ad5449_write(indio_dev, AD5449_CMD_CTRL, ctrl);
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5449_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5449_spi_ids[] = {
|
||||
{ "ad5415", ID_AD5449 },
|
||||
{ "ad5426", ID_AD5426 },
|
||||
{ "ad5429", ID_AD5429 },
|
||||
{ "ad5432", ID_AD5432 },
|
||||
{ "ad5439", ID_AD5439 },
|
||||
{ "ad5443", ID_AD5443 },
|
||||
{ "ad5449", ID_AD5449 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5449_spi_ids);
|
||||
|
||||
static struct spi_driver ad5449_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5449",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5449_spi_probe,
|
||||
.remove = ad5449_spi_remove,
|
||||
.id_table = ad5449_spi_ids,
|
||||
};
|
||||
module_spi_driver(ad5449_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5449 and similar DACs");
|
||||
MODULE_LICENSE("GPL v2");
|
377
drivers/iio/dac/ad5504.c
Normal file
377
drivers/iio/dac/ad5504.c
Normal file
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* AD5504, AD5501 High Voltage Digital to Analog Converter
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/dac/ad5504.h>
|
||||
|
||||
#define AD5504_RES_MASK GENMASK(11, 0)
|
||||
#define AD5504_CMD_READ BIT(15)
|
||||
#define AD5504_CMD_WRITE 0
|
||||
#define AD5504_ADDR(addr) ((addr) << 12)
|
||||
|
||||
/* Registers */
|
||||
#define AD5504_ADDR_NOOP 0
|
||||
#define AD5504_ADDR_DAC(x) ((x) + 1)
|
||||
#define AD5504_ADDR_ALL_DAC 5
|
||||
#define AD5504_ADDR_CTRL 7
|
||||
|
||||
/* Control Register */
|
||||
#define AD5504_DAC_PWR(ch) ((ch) << 2)
|
||||
#define AD5504_DAC_PWRDWN_MODE(mode) ((mode) << 6)
|
||||
#define AD5504_DAC_PWRDN_20K 0
|
||||
#define AD5504_DAC_PWRDN_3STATE 1
|
||||
|
||||
/**
|
||||
* struct ad5446_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @reg: supply regulator
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @pwr_down_mask power down mask
|
||||
* @pwr_down_mode current power down mode
|
||||
* @data: transfer buffer
|
||||
*/
|
||||
struct ad5504_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned short vref_mv;
|
||||
unsigned pwr_down_mask;
|
||||
unsigned pwr_down_mode;
|
||||
|
||||
__be16 data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/**
|
||||
* ad5504_supported_device_ids:
|
||||
*/
|
||||
|
||||
enum ad5504_supported_device_ids {
|
||||
ID_AD5504,
|
||||
ID_AD5501,
|
||||
};
|
||||
|
||||
static int ad5504_spi_write(struct ad5504_state *st, u8 addr, u16 val)
|
||||
{
|
||||
st->data[0] = cpu_to_be16(AD5504_CMD_WRITE | AD5504_ADDR(addr) |
|
||||
(val & AD5504_RES_MASK));
|
||||
|
||||
return spi_write(st->spi, &st->data[0], 2);
|
||||
}
|
||||
|
||||
static int ad5504_spi_read(struct ad5504_state *st, u8 addr)
|
||||
{
|
||||
int ret;
|
||||
struct spi_transfer t = {
|
||||
.tx_buf = &st->data[0],
|
||||
.rx_buf = &st->data[1],
|
||||
.len = 2,
|
||||
};
|
||||
|
||||
st->data[0] = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr));
|
||||
ret = spi_sync_transfer(st->spi, &t, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->data[1]) & AD5504_RES_MASK;
|
||||
}
|
||||
|
||||
static int ad5504_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5504_spi_read(st, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5504_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5504_spi_write(st, chan->address, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const ad5504_powerdown_modes[] = {
|
||||
"20kohm_to_gnd",
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int ad5504_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode;
|
||||
}
|
||||
|
||||
static int ad5504_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5504_powerdown_mode_enum = {
|
||||
.items = ad5504_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5504_powerdown_modes),
|
||||
.get = ad5504_get_powerdown_mode,
|
||||
.set = ad5504_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
!(st->pwr_down_mask & (1 << chan->channel)));
|
||||
}
|
||||
|
||||
static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pwr_down)
|
||||
st->pwr_down_mask |= (1 << chan->channel);
|
||||
else
|
||||
st->pwr_down_mask &= ~(1 << chan->channel);
|
||||
|
||||
ret = ad5504_spi_write(st, AD5504_ADDR_CTRL,
|
||||
AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) |
|
||||
AD5504_DAC_PWR(st->pwr_down_mask));
|
||||
|
||||
/* writes to the CTRL register must be followed by a NOOP */
|
||||
ad5504_spi_write(st, AD5504_ADDR_NOOP, 0);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(temp0_thresh_rising_value, "110000");
|
||||
static IIO_CONST_ATTR(temp0_thresh_rising_en, "1");
|
||||
|
||||
static struct attribute *ad5504_ev_attributes[] = {
|
||||
&iio_const_attr_temp0_thresh_rising_value.dev_attr.attr,
|
||||
&iio_const_attr_temp0_thresh_rising_en.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ad5504_ev_attribute_group = {
|
||||
.attrs = ad5504_ev_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
static irqreturn_t ad5504_event_handler(int irq, void *private)
|
||||
{
|
||||
iio_push_event(private,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns());
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5504_info = {
|
||||
.write_raw = ad5504_write_raw,
|
||||
.read_raw = ad5504_read_raw,
|
||||
.event_attrs = &ad5504_ev_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5504_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5504_read_dac_powerdown,
|
||||
.write = ad5504_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5504_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5504_CHANNEL(_chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = AD5504_ADDR_DAC(_chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
.ext_info = ad5504_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad5504_channels[] = {
|
||||
AD5504_CHANNEL(0),
|
||||
AD5504_CHANNEL(1),
|
||||
AD5504_CHANNEL(2),
|
||||
AD5504_CHANNEL(3),
|
||||
};
|
||||
|
||||
static int ad5504_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5504_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5504_state *st;
|
||||
struct regulator *reg;
|
||||
int ret, voltage_uv = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(reg)) {
|
||||
ret = regulator_enable(reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st = iio_priv(indio_dev);
|
||||
if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
else if (pdata)
|
||||
st->vref_mv = pdata->vref_mv;
|
||||
else
|
||||
dev_warn(&spi->dev, "reference voltage unspecified\n");
|
||||
|
||||
st->reg = reg;
|
||||
st->spi = spi;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(st->spi)->name;
|
||||
indio_dev->info = &ad5504_info;
|
||||
if (spi_get_device_id(st->spi)->driver_data == ID_AD5501)
|
||||
indio_dev->num_channels = 1;
|
||||
else
|
||||
indio_dev->num_channels = 4;
|
||||
indio_dev->channels = ad5504_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
if (spi->irq) {
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
NULL,
|
||||
&ad5504_event_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
spi_get_device_id(st->spi)->name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(reg))
|
||||
regulator_disable(reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5504_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5504_id[] = {
|
||||
{"ad5504", ID_AD5504},
|
||||
{"ad5501", ID_AD5501},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5504_id);
|
||||
|
||||
static struct spi_driver ad5504_driver = {
|
||||
.driver = {
|
||||
.name = "ad5504",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5504_probe,
|
||||
.remove = ad5504_remove,
|
||||
.id_table = ad5504_id,
|
||||
};
|
||||
module_spi_driver(ad5504_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
79
drivers/iio/dac/ad5624r.h
Normal file
79
drivers/iio/dac/ad5624r.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* AD5624R SPI DAC driver
|
||||
*
|
||||
* Copyright 2010-2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#ifndef SPI_AD5624R_H_
|
||||
#define SPI_AD5624R_H_
|
||||
|
||||
#define AD5624R_DAC_CHANNELS 4
|
||||
|
||||
#define AD5624R_ADDR_DAC0 0x0
|
||||
#define AD5624R_ADDR_DAC1 0x1
|
||||
#define AD5624R_ADDR_DAC2 0x2
|
||||
#define AD5624R_ADDR_DAC3 0x3
|
||||
#define AD5624R_ADDR_ALL_DAC 0x7
|
||||
|
||||
#define AD5624R_CMD_WRITE_INPUT_N 0x0
|
||||
#define AD5624R_CMD_UPDATE_DAC_N 0x1
|
||||
#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
|
||||
#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_N 0x3
|
||||
#define AD5624R_CMD_POWERDOWN_DAC 0x4
|
||||
#define AD5624R_CMD_RESET 0x5
|
||||
#define AD5624R_CMD_LDAC_SETUP 0x6
|
||||
#define AD5624R_CMD_INTERNAL_REFER_SETUP 0x7
|
||||
|
||||
#define AD5624R_LDAC_PWRDN_NONE 0x0
|
||||
#define AD5624R_LDAC_PWRDN_1K 0x1
|
||||
#define AD5624R_LDAC_PWRDN_100K 0x2
|
||||
#define AD5624R_LDAC_PWRDN_3STATE 0x3
|
||||
|
||||
/**
|
||||
* struct ad5624r_chip_info - chip specific information
|
||||
* @channels: channel spec for the DAC
|
||||
* @int_vref_mv: AD5620/40/60: the internal reference voltage
|
||||
*/
|
||||
|
||||
struct ad5624r_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
u16 int_vref_mv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5446_state - driver instance specific data
|
||||
* @indio_dev: the industrial I/O device
|
||||
* @us: 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
|
||||
*/
|
||||
|
||||
struct ad5624r_state {
|
||||
struct spi_device *us;
|
||||
const struct ad5624r_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
unsigned short vref_mv;
|
||||
unsigned pwr_down_mask;
|
||||
unsigned pwr_down_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* ad5624r_supported_device_ids:
|
||||
* The AD5624/44/64 parts are available in different
|
||||
* fixed internal reference voltage options.
|
||||
*/
|
||||
|
||||
enum ad5624r_supported_device_ids {
|
||||
ID_AD5624R3,
|
||||
ID_AD5644R3,
|
||||
ID_AD5664R3,
|
||||
ID_AD5624R5,
|
||||
ID_AD5644R5,
|
||||
ID_AD5664R5,
|
||||
};
|
||||
|
||||
#endif /* SPI_AD5624R_H_ */
|
319
drivers/iio/dac/ad5624r_spi.c
Normal file
319
drivers/iio/dac/ad5624r_spi.c
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver
|
||||
*
|
||||
* Copyright 2010-2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "ad5624r.h"
|
||||
|
||||
static int ad5624r_spi_write(struct spi_device *spi,
|
||||
u8 cmd, u8 addr, u16 val, u8 len)
|
||||
{
|
||||
u32 data;
|
||||
u8 msg[3];
|
||||
|
||||
/*
|
||||
* The input shift register is 24 bits wide. The first two bits are
|
||||
* don't care bits. The next three are the command bits, C2 to C0,
|
||||
* followed by the 3-bit DAC address, A2 to A0, and then the
|
||||
* 16-, 14-, 12-bit data-word. The data-word comprises the 16-,
|
||||
* 14-, 12-bit input code followed by 0, 2, or 4 don't care bits,
|
||||
* for the AD5664R, AD5644R, and AD5624R, respectively.
|
||||
*/
|
||||
data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << (16 - len));
|
||||
msg[0] = data >> 16;
|
||||
msg[1] = data >> 8;
|
||||
msg[2] = data;
|
||||
|
||||
return spi_write(spi, msg, 3);
|
||||
}
|
||||
|
||||
static int ad5624r_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5624r_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5624r_spi_write(st->us,
|
||||
AD5624R_CMD_WRITE_INPUT_N_UPDATE_N,
|
||||
chan->address, val,
|
||||
chan->scan_type.shift);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const ad5624r_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"three_state"
|
||||
};
|
||||
|
||||
static int ad5624r_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode;
|
||||
}
|
||||
|
||||
static int ad5624r_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5624r_powerdown_mode_enum = {
|
||||
.items = ad5624r_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5624r_powerdown_modes),
|
||||
.get = ad5624r_get_powerdown_mode,
|
||||
.set = ad5624r_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5624r_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
!!(st->pwr_down_mask & (1 << chan->channel)));
|
||||
}
|
||||
|
||||
static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pwr_down)
|
||||
st->pwr_down_mask |= (1 << chan->channel);
|
||||
else
|
||||
st->pwr_down_mask &= ~(1 << chan->channel);
|
||||
|
||||
ret = ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0,
|
||||
(st->pwr_down_mode << 4) |
|
||||
st->pwr_down_mask, 16);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5624r_info = {
|
||||
.write_raw = ad5624r_write_raw,
|
||||
.read_raw = ad5624r_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5624r_read_dac_powerdown,
|
||||
.write = ad5624r_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5624r_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5624R_CHANNEL(_chan, _bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (_chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (_bits), \
|
||||
}, \
|
||||
.ext_info = ad5624r_ext_info, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5624R_CHANNELS(_name, _bits) \
|
||||
const struct iio_chan_spec _name##_channels[] = { \
|
||||
AD5624R_CHANNEL(0, _bits), \
|
||||
AD5624R_CHANNEL(1, _bits), \
|
||||
AD5624R_CHANNEL(2, _bits), \
|
||||
AD5624R_CHANNEL(3, _bits), \
|
||||
}
|
||||
|
||||
static DECLARE_AD5624R_CHANNELS(ad5624r, 12);
|
||||
static DECLARE_AD5624R_CHANNELS(ad5644r, 14);
|
||||
static DECLARE_AD5624R_CHANNELS(ad5664r, 16);
|
||||
|
||||
static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = {
|
||||
[ID_AD5624R3] = {
|
||||
.channels = ad5624r_channels,
|
||||
.int_vref_mv = 1250,
|
||||
},
|
||||
[ID_AD5624R5] = {
|
||||
.channels = ad5624r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
[ID_AD5644R3] = {
|
||||
.channels = ad5644r_channels,
|
||||
.int_vref_mv = 1250,
|
||||
},
|
||||
[ID_AD5644R5] = {
|
||||
.channels = ad5644r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
[ID_AD5664R3] = {
|
||||
.channels = ad5664r_channels,
|
||||
.int_vref_mv = 1250,
|
||||
},
|
||||
[ID_AD5664R5] = {
|
||||
.channels = ad5664r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5624r_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5624r_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, voltage_uv = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
st = iio_priv(indio_dev);
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->chip_info =
|
||||
&ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
st->vref_mv = st->chip_info->int_vref_mv;
|
||||
|
||||
st->us = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5624r_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = AD5624R_DAC_CHANNELS;
|
||||
|
||||
ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0,
|
||||
!!voltage_uv, 16);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5624r_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5624r_id[] = {
|
||||
{"ad5624r3", ID_AD5624R3},
|
||||
{"ad5644r3", ID_AD5644R3},
|
||||
{"ad5664r3", ID_AD5664R3},
|
||||
{"ad5624r5", ID_AD5624R5},
|
||||
{"ad5644r5", ID_AD5644R5},
|
||||
{"ad5664r5", ID_AD5664R5},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5624r_id);
|
||||
|
||||
static struct spi_driver ad5624r_driver = {
|
||||
.driver = {
|
||||
.name = "ad5624r",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5624r_probe,
|
||||
.remove = ad5624r_remove,
|
||||
.id_table = ad5624r_id,
|
||||
};
|
||||
module_spi_driver(ad5624r_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
408
drivers/iio/dac/ad5686.c
Normal file
408
drivers/iio/dac/ad5686.c
Normal file
|
@ -0,0 +1,408 @@
|
|||
/*
|
||||
* AD5686R, AD5685R, AD5684R Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#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>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5686_DAC_CHANNELS 4
|
||||
|
||||
#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
|
||||
* @channel: channel specification
|
||||
*/
|
||||
|
||||
struct ad5686_chip_info {
|
||||
u16 int_vref_mv;
|
||||
struct iio_chan_spec channel[AD5686_DAC_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_AD5684,
|
||||
ID_AD5685,
|
||||
ID_AD5686,
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
static const char * const ad5686_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"three_state"
|
||||
};
|
||||
|
||||
static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1;
|
||||
}
|
||||
|
||||
static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode &= ~(0x3 << (chan->channel * 2));
|
||||
st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5686_powerdown_mode_enum = {
|
||||
.items = ad5686_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5686_powerdown_modes),
|
||||
.get = ad5686_get_powerdown_mode,
|
||||
.set = ad5686_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", !!(st->pwr_down_mask &
|
||||
(0x3 << (chan->channel * 2))));
|
||||
}
|
||||
|
||||
static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool readin;
|
||||
int ret;
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = strtobool(buf, &readin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (readin)
|
||||
st->pwr_down_mask |= (0x3 << (chan->channel * 2));
|
||||
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);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static int ad5686_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5686_spi_read(st, chan->address);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5686_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val > (1 << chan->scan_type.realbits) || val < 0)
|
||||
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);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5686_info = {
|
||||
.read_raw = ad5686_read_raw,
|
||||
.write_raw = ad5686_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5686_read_dac_powerdown,
|
||||
.write = ad5686_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5686_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5868_CHANNEL(chan, bits, _shift) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
|
||||
.address = AD5686_ADDR_DAC(chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = (_shift), \
|
||||
}, \
|
||||
.ext_info = ad5686_ext_info, \
|
||||
}
|
||||
|
||||
static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
|
||||
[ID_AD5684] = {
|
||||
.channel[0] = AD5868_CHANNEL(0, 12, 4),
|
||||
.channel[1] = AD5868_CHANNEL(1, 12, 4),
|
||||
.channel[2] = AD5868_CHANNEL(2, 12, 4),
|
||||
.channel[3] = AD5868_CHANNEL(3, 12, 4),
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
[ID_AD5685] = {
|
||||
.channel[0] = AD5868_CHANNEL(0, 14, 2),
|
||||
.channel[1] = AD5868_CHANNEL(1, 14, 2),
|
||||
.channel[2] = AD5868_CHANNEL(2, 14, 2),
|
||||
.channel[3] = AD5868_CHANNEL(3, 14, 2),
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
[ID_AD5686] = {
|
||||
.channel[0] = AD5868_CHANNEL(0, 16, 0),
|
||||
.channel[1] = AD5868_CHANNEL(1, 16, 0),
|
||||
.channel[2] = AD5868_CHANNEL(2, 16, 0),
|
||||
.channel[3] = AD5868_CHANNEL(3, 16, 0),
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static int ad5686_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5686_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, voltage_uv = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get_optional(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->chip_info =
|
||||
&ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
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->info = &ad5686_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channel;
|
||||
indio_dev->num_channels = AD5686_DAC_CHANNELS;
|
||||
|
||||
ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0,
|
||||
!!voltage_uv, 0);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5686_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5686_id[] = {
|
||||
{"ad5684", ID_AD5684},
|
||||
{"ad5685", ID_AD5685},
|
||||
{"ad5686", ID_AD5686},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5686_id);
|
||||
|
||||
static struct spi_driver ad5686_driver = {
|
||||
.driver = {
|
||||
.name = "ad5686",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5686_probe,
|
||||
.remove = ad5686_remove,
|
||||
.id_table = ad5686_id,
|
||||
};
|
||||
module_spi_driver(ad5686_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
622
drivers/iio/dac/ad5755.c
Normal file
622
drivers/iio/dac/ad5755.c
Normal file
|
@ -0,0 +1,622 @@
|
|||
/*
|
||||
* 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 {
|
||||
__be32 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);
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
||||
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_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
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 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 min, max;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ad5755_get_min_max(st, chan, &min, &max);
|
||||
*val = max - min;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
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,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5755_CHANNEL(_bits) { \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 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 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 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 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 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 = devm_iio_device_alloc(&spi->dev, 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)
|
||||
return ret;
|
||||
|
||||
ret = ad5755_setup_pdata(indio_dev, pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
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,
|
||||
.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");
|
370
drivers/iio/dac/ad5764.c
Normal file
370
drivers/iio/dac/ad5764.c
Normal file
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel
|
||||
* Digital to Analog Converters driver
|
||||
*
|
||||
* Copyright 2011 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/regulator/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5764_REG_SF_NOP 0x0
|
||||
#define AD5764_REG_SF_CONFIG 0x1
|
||||
#define AD5764_REG_SF_CLEAR 0x4
|
||||
#define AD5764_REG_SF_LOAD 0x5
|
||||
#define AD5764_REG_DATA(x) ((2 << 3) | (x))
|
||||
#define AD5764_REG_COARSE_GAIN(x) ((3 << 3) | (x))
|
||||
#define AD5764_REG_FINE_GAIN(x) ((4 << 3) | (x))
|
||||
#define AD5764_REG_OFFSET(x) ((5 << 3) | (x))
|
||||
|
||||
#define AD5764_NUM_CHANNELS 4
|
||||
|
||||
/**
|
||||
* struct ad5764_chip_info - chip specific information
|
||||
* @int_vref: Value of the internal reference voltage in uV - 0 if external
|
||||
* reference voltage is used
|
||||
* @channel channel specification
|
||||
*/
|
||||
|
||||
struct ad5764_chip_info {
|
||||
unsigned long int_vref;
|
||||
const struct iio_chan_spec *channels;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5764_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @chip_info: chip info
|
||||
* @vref_reg: vref supply regulators
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5764_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad5764_chip_info *chip_info;
|
||||
struct regulator_bulk_data vref_reg[2];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad5764_type {
|
||||
ID_AD5744,
|
||||
ID_AD5744R,
|
||||
ID_AD5764,
|
||||
ID_AD5764R,
|
||||
};
|
||||
|
||||
#define AD5764_CHANNEL(_chan, _bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (_bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5764_CHANNELS(_name, _bits) \
|
||||
const struct iio_chan_spec _name##_channels[] = { \
|
||||
AD5764_CHANNEL(0, (_bits)), \
|
||||
AD5764_CHANNEL(1, (_bits)), \
|
||||
AD5764_CHANNEL(2, (_bits)), \
|
||||
AD5764_CHANNEL(3, (_bits)), \
|
||||
};
|
||||
|
||||
static DECLARE_AD5764_CHANNELS(ad5764, 16);
|
||||
static DECLARE_AD5764_CHANNELS(ad5744, 14);
|
||||
|
||||
static const struct ad5764_chip_info ad5764_chip_infos[] = {
|
||||
[ID_AD5744] = {
|
||||
.int_vref = 0,
|
||||
.channels = ad5744_channels,
|
||||
},
|
||||
[ID_AD5744R] = {
|
||||
.int_vref = 5000000,
|
||||
.channels = ad5744_channels,
|
||||
},
|
||||
[ID_AD5764] = {
|
||||
.int_vref = 0,
|
||||
.channels = ad5764_channels,
|
||||
},
|
||||
[ID_AD5764R] = {
|
||||
.int_vref = 5000000,
|
||||
.channels = ad5764_channels,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->data[0].d32 = cpu_to_be32((reg << 16) | val);
|
||||
|
||||
ret = spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->data[1].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
*val = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info)
|
||||
{
|
||||
switch (info) {
|
||||
case 0:
|
||||
return AD5764_REG_DATA(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return AD5764_REG_OFFSET(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
return AD5764_REG_FINE_GAIN(chan->address);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5764_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long info)
|
||||
{
|
||||
const int max_val = (1 << chan->scan_type.realbits);
|
||||
unsigned int reg;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
val <<= chan->scan_type.shift;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val >= 128 || val < -128)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val >= 32 || val < -32)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = ad5764_chan_info_to_reg(chan, info);
|
||||
return ad5764_write(indio_dev, reg, (u16)val);
|
||||
}
|
||||
|
||||
static int ad5764_get_channel_vref(struct ad5764_state *st,
|
||||
unsigned int channel)
|
||||
{
|
||||
if (st->chip_info->int_vref)
|
||||
return st->chip_info->int_vref;
|
||||
else
|
||||
return regulator_get_voltage(st->vref_reg[channel / 2].consumer);
|
||||
}
|
||||
|
||||
static int ad5764_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
unsigned int reg;
|
||||
int vref;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
reg = AD5764_REG_DATA(chan->address);
|
||||
ret = ad5764_read(indio_dev, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val >>= chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
reg = AD5764_REG_OFFSET(chan->address);
|
||||
ret = ad5764_read(indio_dev, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(*val, 7);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
reg = AD5764_REG_FINE_GAIN(chan->address);
|
||||
ret = ad5764_read(indio_dev, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(*val, 5);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* vout = 4 * vref + ((dac_code / 65536) - 0.5) */
|
||||
vref = ad5764_get_channel_vref(st, chan->channel);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
*val = vref * 4 / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = -(1 << chan->scan_type.realbits) / 2;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5764_info = {
|
||||
.read_raw = ad5764_read_raw,
|
||||
.write_raw = ad5764_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad5764_probe(struct spi_device *spi)
|
||||
{
|
||||
enum ad5764_type type = spi_get_device_id(spi)->driver_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5764_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, 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->spi = spi;
|
||||
st->chip_info = &ad5764_chip_infos[type];
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5764_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->num_channels = AD5764_NUM_CHANNELS;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
|
||||
if (st->chip_info->int_vref == 0) {
|
||||
st->vref_reg[0].supply = "vrefAB";
|
||||
st->vref_reg[1].supply = "vrefCD";
|
||||
|
||||
ret = devm_regulator_bulk_get(&st->spi->dev,
|
||||
ARRAY_SIZE(st->vref_reg), st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to request vref regulators: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg),
|
||||
st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable vref regulators: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (st->chip_info->int_vref == 0)
|
||||
regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5764_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (st->chip_info->int_vref == 0)
|
||||
regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5764_ids[] = {
|
||||
{ "ad5744", ID_AD5744 },
|
||||
{ "ad5744r", ID_AD5744R },
|
||||
{ "ad5764", ID_AD5764 },
|
||||
{ "ad5764r", ID_AD5764R },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5764_ids);
|
||||
|
||||
static struct spi_driver ad5764_driver = {
|
||||
.driver = {
|
||||
.name = "ad5764",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5764_probe,
|
||||
.remove = ad5764_remove,
|
||||
.id_table = ad5764_ids,
|
||||
};
|
||||
module_spi_driver(ad5764_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
474
drivers/iio/dac/ad5791.c
Normal file
474
drivers/iio/dac/ad5791.c
Normal file
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
* AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog
|
||||
* Converter
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/dac/ad5791.h>
|
||||
|
||||
#define AD5791_DAC_MASK GENMASK(19, 0)
|
||||
|
||||
#define AD5791_CMD_READ BIT(23)
|
||||
#define AD5791_CMD_WRITE 0
|
||||
#define AD5791_ADDR(addr) ((addr) << 20)
|
||||
|
||||
/* Registers */
|
||||
#define AD5791_ADDR_NOOP 0
|
||||
#define AD5791_ADDR_DAC0 1
|
||||
#define AD5791_ADDR_CTRL 2
|
||||
#define AD5791_ADDR_CLRCODE 3
|
||||
#define AD5791_ADDR_SW_CTRL 4
|
||||
|
||||
/* Control Register */
|
||||
#define AD5791_CTRL_RBUF BIT(1)
|
||||
#define AD5791_CTRL_OPGND BIT(2)
|
||||
#define AD5791_CTRL_DACTRI BIT(3)
|
||||
#define AD5791_CTRL_BIN2SC BIT(4)
|
||||
#define AD5791_CTRL_SDODIS BIT(5)
|
||||
#define AD5761_CTRL_LINCOMP(x) ((x) << 6)
|
||||
|
||||
#define AD5791_LINCOMP_0_10 0
|
||||
#define AD5791_LINCOMP_10_12 1
|
||||
#define AD5791_LINCOMP_12_16 2
|
||||
#define AD5791_LINCOMP_16_19 3
|
||||
#define AD5791_LINCOMP_19_20 12
|
||||
|
||||
#define AD5780_LINCOMP_0_10 0
|
||||
#define AD5780_LINCOMP_10_20 12
|
||||
|
||||
/* Software Control Register */
|
||||
#define AD5791_SWCTRL_LDAC BIT(0)
|
||||
#define AD5791_SWCTRL_CLR BIT(1)
|
||||
#define AD5791_SWCTRL_RESET BIT(2)
|
||||
|
||||
#define AD5791_DAC_PWRDN_6K 0
|
||||
#define AD5791_DAC_PWRDN_3STATE 1
|
||||
|
||||
/**
|
||||
* struct ad5791_chip_info - chip specific information
|
||||
* @get_lin_comp: function pointer to the device specific function
|
||||
*/
|
||||
|
||||
struct ad5791_chip_info {
|
||||
int (*get_lin_comp) (unsigned int span);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5791_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @reg_vdd: positive supply regulator
|
||||
* @reg_vss: negative supply regulator
|
||||
* @chip_info: chip model specific constants
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @vref_neg_mv: voltage of the negative supply
|
||||
* @pwr_down_mode current power down mode
|
||||
*/
|
||||
|
||||
struct ad5791_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg_vdd;
|
||||
struct regulator *reg_vss;
|
||||
const struct ad5791_chip_info *chip_info;
|
||||
unsigned short vref_mv;
|
||||
unsigned int vref_neg_mv;
|
||||
unsigned ctrl;
|
||||
unsigned pwr_down_mode;
|
||||
bool pwr_down;
|
||||
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/**
|
||||
* ad5791_supported_device_ids:
|
||||
*/
|
||||
|
||||
enum ad5791_supported_device_ids {
|
||||
ID_AD5760,
|
||||
ID_AD5780,
|
||||
ID_AD5781,
|
||||
ID_AD5791,
|
||||
};
|
||||
|
||||
static int ad5791_spi_write(struct ad5791_state *st, u8 addr, u32 val)
|
||||
{
|
||||
st->data[0].d32 = cpu_to_be32(AD5791_CMD_WRITE |
|
||||
AD5791_ADDR(addr) |
|
||||
(val & AD5791_DAC_MASK));
|
||||
|
||||
return spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
}
|
||||
|
||||
static int ad5791_spi_read(struct ad5791_state *st, u8 addr, u32 *val)
|
||||
{
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.bits_per_word = 8,
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.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(AD5791_CMD_READ |
|
||||
AD5791_ADDR(addr));
|
||||
st->data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
|
||||
|
||||
*val = be32_to_cpu(st->data[2].d32);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const ad5791_powerdown_modes[] = {
|
||||
"6kohm_to_gnd",
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode;
|
||||
}
|
||||
|
||||
static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5791_powerdown_mode_enum = {
|
||||
.items = ad5791_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5791_powerdown_modes),
|
||||
.get = ad5791_get_powerdown_mode,
|
||||
.set = ad5791_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down);
|
||||
}
|
||||
|
||||
static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!pwr_down) {
|
||||
st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI);
|
||||
} else {
|
||||
if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K)
|
||||
st->ctrl |= AD5791_CTRL_OPGND;
|
||||
else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE)
|
||||
st->ctrl |= AD5791_CTRL_DACTRI;
|
||||
}
|
||||
st->pwr_down = pwr_down;
|
||||
|
||||
ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static int ad5791_get_lin_comp(unsigned int span)
|
||||
{
|
||||
if (span <= 10000)
|
||||
return AD5791_LINCOMP_0_10;
|
||||
else if (span <= 12000)
|
||||
return AD5791_LINCOMP_10_12;
|
||||
else if (span <= 16000)
|
||||
return AD5791_LINCOMP_12_16;
|
||||
else if (span <= 19000)
|
||||
return AD5791_LINCOMP_16_19;
|
||||
else
|
||||
return AD5791_LINCOMP_19_20;
|
||||
}
|
||||
|
||||
static int ad5780_get_lin_comp(unsigned int span)
|
||||
{
|
||||
if (span <= 10000)
|
||||
return AD5780_LINCOMP_0_10;
|
||||
else
|
||||
return AD5780_LINCOMP_10_20;
|
||||
}
|
||||
static const struct ad5791_chip_info ad5791_chip_info_tbl[] = {
|
||||
[ID_AD5760] = {
|
||||
.get_lin_comp = ad5780_get_lin_comp,
|
||||
},
|
||||
[ID_AD5780] = {
|
||||
.get_lin_comp = ad5780_get_lin_comp,
|
||||
},
|
||||
[ID_AD5781] = {
|
||||
.get_lin_comp = ad5791_get_lin_comp,
|
||||
},
|
||||
[ID_AD5791] = {
|
||||
.get_lin_comp = ad5791_get_lin_comp,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5791_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
u64 val64;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5791_spi_read(st, chan->address, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val &= AD5791_DAC_MASK;
|
||||
*val >>= chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = (1 << chan->scan_type.realbits) - 1;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits);
|
||||
do_div(val64, st->vref_mv);
|
||||
*val = -val64;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5791_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.shared = IIO_SHARED_BY_TYPE,
|
||||
.read = ad5791_read_dac_powerdown,
|
||||
.write = ad5791_write_dac_powerdown,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5791_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5791_CHAN(bits, _shift) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.output = 1, \
|
||||
.indexed = 1, \
|
||||
.address = AD5791_ADDR_DAC0, \
|
||||
.channel = 0, \
|
||||
.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 = 24, \
|
||||
.shift = (_shift), \
|
||||
}, \
|
||||
.ext_info = ad5791_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad5791_channels[] = {
|
||||
[ID_AD5760] = AD5791_CHAN(16, 4),
|
||||
[ID_AD5780] = AD5791_CHAN(18, 2),
|
||||
[ID_AD5781] = AD5791_CHAN(18, 2),
|
||||
[ID_AD5791] = AD5791_CHAN(20, 0)
|
||||
};
|
||||
|
||||
static int ad5791_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
val &= GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
val <<= chan->scan_type.shift;
|
||||
|
||||
return ad5791_spi_write(st, chan->address, val);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ad5791_info = {
|
||||
.read_raw = &ad5791_read_raw,
|
||||
.write_raw = &ad5791_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad5791_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5791_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5791_state *st;
|
||||
int ret, pos_voltage_uv = 0, neg_voltage_uv = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
st = iio_priv(indio_dev);
|
||||
st->reg_vdd = devm_regulator_get(&spi->dev, "vdd");
|
||||
if (!IS_ERR(st->reg_vdd)) {
|
||||
ret = regulator_enable(st->reg_vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(st->reg_vdd);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg_pos;
|
||||
|
||||
pos_voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->reg_vss = devm_regulator_get(&spi->dev, "vss");
|
||||
if (!IS_ERR(st->reg_vss)) {
|
||||
ret = regulator_enable(st->reg_vss);
|
||||
if (ret)
|
||||
goto error_disable_reg_pos;
|
||||
|
||||
ret = regulator_get_voltage(st->reg_vss);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg_neg;
|
||||
|
||||
neg_voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->pwr_down = true;
|
||||
st->spi = spi;
|
||||
|
||||
if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) {
|
||||
st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000;
|
||||
st->vref_neg_mv = neg_voltage_uv / 1000;
|
||||
} else if (pdata) {
|
||||
st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv;
|
||||
st->vref_neg_mv = pdata->vref_neg_mv;
|
||||
} else {
|
||||
dev_warn(&spi->dev, "reference voltage unspecified\n");
|
||||
}
|
||||
|
||||
ret = ad5791_spi_write(st, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET);
|
||||
if (ret)
|
||||
goto error_disable_reg_neg;
|
||||
|
||||
st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi)
|
||||
->driver_data];
|
||||
|
||||
|
||||
st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv))
|
||||
| ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) |
|
||||
AD5791_CTRL_BIN2SC;
|
||||
|
||||
ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl |
|
||||
AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI);
|
||||
if (ret)
|
||||
goto error_disable_reg_neg;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &ad5791_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels
|
||||
= &ad5791_channels[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->num_channels = 1;
|
||||
indio_dev->name = spi_get_device_id(st->spi)->name;
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg_neg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg_neg:
|
||||
if (!IS_ERR(st->reg_vss))
|
||||
regulator_disable(st->reg_vss);
|
||||
error_disable_reg_pos:
|
||||
if (!IS_ERR(st->reg_vdd))
|
||||
regulator_disable(st->reg_vdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5791_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg_vdd))
|
||||
regulator_disable(st->reg_vdd);
|
||||
|
||||
if (!IS_ERR(st->reg_vss))
|
||||
regulator_disable(st->reg_vss);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5791_id[] = {
|
||||
{"ad5760", ID_AD5760},
|
||||
{"ad5780", ID_AD5780},
|
||||
{"ad5781", ID_AD5781},
|
||||
{"ad5790", ID_AD5791},
|
||||
{"ad5791", ID_AD5791},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5791_id);
|
||||
|
||||
static struct spi_driver ad5791_driver = {
|
||||
.driver = {
|
||||
.name = "ad5791",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5791_probe,
|
||||
.remove = ad5791_remove,
|
||||
.id_table = ad5791_id,
|
||||
};
|
||||
module_spi_driver(ad5791_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
303
drivers/iio/dac/ad7303.c
Normal file
303
drivers/iio/dac/ad7303.c
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* AD7303 Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#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/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include <linux/platform_data/ad7303.h>
|
||||
|
||||
#define AD7303_CFG_EXTERNAL_VREF BIT(15)
|
||||
#define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch))
|
||||
#define AD7303_CFG_ADDR_OFFSET 10
|
||||
|
||||
#define AD7303_CMD_UPDATE_DAC (0x3 << 8)
|
||||
|
||||
/**
|
||||
* struct ad7303_state - driver instance specific data
|
||||
* @spi: the device for this driver instance
|
||||
* @config: cached config register value
|
||||
* @dac_cache: current DAC raw value (chip does not support readback)
|
||||
* @data: spi transfer buffer
|
||||
*/
|
||||
|
||||
struct ad7303_state {
|
||||
struct spi_device *spi;
|
||||
uint16_t config;
|
||||
uint8_t dac_cache[2];
|
||||
|
||||
struct regulator *vdd_reg;
|
||||
struct regulator *vref_reg;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 data ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad7303_write(struct ad7303_state *st, unsigned int chan,
|
||||
uint8_t val)
|
||||
{
|
||||
st->data = cpu_to_be16(AD7303_CMD_UPDATE_DAC |
|
||||
(chan << AD7303_CFG_ADDR_OFFSET) |
|
||||
st->config | val);
|
||||
|
||||
return spi_write(st->spi, &st->data, sizeof(st->data));
|
||||
}
|
||||
|
||||
static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", (bool)(st->config &
|
||||
AD7303_CFG_POWER_DOWN(chan->channel)));
|
||||
}
|
||||
|
||||
static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (pwr_down)
|
||||
st->config |= AD7303_CFG_POWER_DOWN(chan->channel);
|
||||
else
|
||||
st->config &= ~AD7303_CFG_POWER_DOWN(chan->channel);
|
||||
|
||||
/* There is no noop cmd which allows us to only update the powerdown
|
||||
* mode, so just write one of the DAC channels again */
|
||||
ad7303_write(st, chan->channel, st->dac_cache[chan->channel]);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int ad7303_get_vref(struct ad7303_state *st,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (st->config & AD7303_CFG_EXTERNAL_VREF)
|
||||
return regulator_get_voltage(st->vref_reg);
|
||||
|
||||
ret = regulator_get_voltage(st->vdd_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return ret / 2;
|
||||
}
|
||||
|
||||
static int ad7303_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
int vref_uv;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = st->dac_cache[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
vref_uv = ad7303_get_vref(st, chan);
|
||||
if (vref_uv < 0)
|
||||
return vref_uv;
|
||||
|
||||
*val = 2 * vref_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad7303_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad7303_write(st, chan->address, val);
|
||||
if (ret == 0)
|
||||
st->dac_cache[chan->channel] = val;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7303_info = {
|
||||
.read_raw = ad7303_read_raw,
|
||||
.write_raw = ad7303_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad7303_read_dac_powerdown,
|
||||
.write = ad7303_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD7303_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = '8', \
|
||||
.storagebits = '8', \
|
||||
.shift = '0', \
|
||||
}, \
|
||||
.ext_info = ad7303_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad7303_channels[] = {
|
||||
AD7303_CHANNEL(0),
|
||||
AD7303_CHANNEL(1),
|
||||
};
|
||||
|
||||
static int ad7303_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7303_state *st;
|
||||
bool ext_ref;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd");
|
||||
if (IS_ERR(st->vdd_reg))
|
||||
return PTR_ERR(st->vdd_reg);
|
||||
|
||||
ret = regulator_enable(st->vdd_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (spi->dev.of_node) {
|
||||
ext_ref = of_property_read_bool(spi->dev.of_node,
|
||||
"REF-supply");
|
||||
} else {
|
||||
struct ad7303_platform_data *pdata = spi->dev.platform_data;
|
||||
if (pdata && pdata->use_external_ref)
|
||||
ext_ref = true;
|
||||
else
|
||||
ext_ref = false;
|
||||
}
|
||||
|
||||
if (ext_ref) {
|
||||
st->vref_reg = devm_regulator_get(&spi->dev, "REF");
|
||||
if (IS_ERR(st->vref_reg)) {
|
||||
ret = PTR_ERR(st->vref_reg);
|
||||
goto err_disable_vdd_reg;
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->vref_reg);
|
||||
if (ret)
|
||||
goto err_disable_vdd_reg;
|
||||
|
||||
st->config |= AD7303_CFG_EXTERNAL_VREF;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = &ad7303_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad7303_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7303_channels);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_disable_vref_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_vref_reg:
|
||||
if (st->vref_reg)
|
||||
regulator_disable(st->vref_reg);
|
||||
err_disable_vdd_reg:
|
||||
regulator_disable(st->vdd_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7303_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (st->vref_reg)
|
||||
regulator_disable(st->vref_reg);
|
||||
regulator_disable(st->vdd_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7303_spi_ids[] = {
|
||||
{ "ad7303", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
|
||||
|
||||
static struct spi_driver ad7303_driver = {
|
||||
.driver = {
|
||||
.name = "ad7303",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7303_probe,
|
||||
.remove = ad7303_remove,
|
||||
.id_table = ad7303_spi_ids,
|
||||
};
|
||||
module_spi_driver(ad7303_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
222
drivers/iio/dac/max517.c
Normal file
222
drivers/iio/dac/max517.c
Normal file
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* max517.c - Support for Maxim MAX517, MAX518 and MAX519
|
||||
*
|
||||
* Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/dac/max517.h>
|
||||
|
||||
#define MAX517_DRV_NAME "max517"
|
||||
|
||||
/* Commands */
|
||||
#define COMMAND_CHANNEL0 0x00
|
||||
#define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */
|
||||
#define COMMAND_PD 0x08 /* Power Down */
|
||||
|
||||
enum max517_device_ids {
|
||||
ID_MAX517,
|
||||
ID_MAX518,
|
||||
ID_MAX519,
|
||||
};
|
||||
|
||||
struct max517_data {
|
||||
struct i2c_client *client;
|
||||
unsigned short vref_mv[2];
|
||||
};
|
||||
|
||||
/*
|
||||
* channel: bit 0: channel 1
|
||||
* bit 1: channel 2
|
||||
* (this way, it's possible to set both channels at once)
|
||||
*/
|
||||
static int max517_set_value(struct iio_dev *indio_dev,
|
||||
long val, int channel)
|
||||
{
|
||||
struct max517_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 outbuf[2];
|
||||
int res;
|
||||
|
||||
if (val < 0 || val > 255)
|
||||
return -EINVAL;
|
||||
|
||||
outbuf[0] = channel;
|
||||
outbuf[1] = val;
|
||||
|
||||
res = i2c_master_send(client, outbuf, 2);
|
||||
if (res < 0)
|
||||
return res;
|
||||
else if (res != 2)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max517_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct max517_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Corresponds to Vref / 2^(bits) */
|
||||
*val = data->vref_mv[chan->channel];
|
||||
*val2 = 8;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int max517_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = max517_set_value(indio_dev, val, chan->channel);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max517_suspend(struct device *dev)
|
||||
{
|
||||
u8 outbuf = COMMAND_PD;
|
||||
|
||||
return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
|
||||
}
|
||||
|
||||
static int max517_resume(struct device *dev)
|
||||
{
|
||||
u8 outbuf = 0;
|
||||
|
||||
return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume);
|
||||
#define MAX517_PM_OPS (&max517_pm_ops)
|
||||
#else
|
||||
#define MAX517_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct iio_info max517_info = {
|
||||
.read_raw = max517_read_raw,
|
||||
.write_raw = max517_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define MAX517_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max517_channels[] = {
|
||||
MAX517_CHANNEL(0),
|
||||
MAX517_CHANNEL(1)
|
||||
};
|
||||
|
||||
static int max517_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max517_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max517_platform_data *platform_data = client->dev.platform_data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
/* establish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
/* reduced channel set for MAX517 */
|
||||
if (id->driver_data == ID_MAX517)
|
||||
indio_dev->num_channels = 1;
|
||||
else
|
||||
indio_dev->num_channels = 2;
|
||||
indio_dev->channels = max517_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &max517_info;
|
||||
|
||||
/*
|
||||
* Reference voltage on MAX518 and default is 5V, else take vref_mv
|
||||
* from platform_data
|
||||
*/
|
||||
if (id->driver_data == ID_MAX518 || !platform_data) {
|
||||
data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */
|
||||
} else {
|
||||
data->vref_mv[0] = platform_data->vref_mv[0];
|
||||
data->vref_mv[1] = platform_data->vref_mv[1];
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int max517_remove(struct i2c_client *client)
|
||||
{
|
||||
iio_device_unregister(i2c_get_clientdata(client));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max517_id[] = {
|
||||
{ "max517", ID_MAX517 },
|
||||
{ "max518", ID_MAX518 },
|
||||
{ "max519", ID_MAX519 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max517_id);
|
||||
|
||||
static struct i2c_driver max517_driver = {
|
||||
.driver = {
|
||||
.name = MAX517_DRV_NAME,
|
||||
.pm = MAX517_PM_OPS,
|
||||
},
|
||||
.probe = max517_probe,
|
||||
.remove = max517_remove,
|
||||
.id_table = max517_id,
|
||||
};
|
||||
module_i2c_driver(max517_driver);
|
||||
|
||||
MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
|
||||
MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC");
|
||||
MODULE_LICENSE("GPL");
|
405
drivers/iio/dac/max5821.c
Normal file
405
drivers/iio/dac/max5821.c
Normal file
|
@ -0,0 +1,405 @@
|
|||
/*
|
||||
* iio/dac/max5821.c
|
||||
* Copyright (C) 2014 Philippe Reynes
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MAX5821_MAX_DAC_CHANNELS 2
|
||||
|
||||
/* command bytes */
|
||||
#define MAX5821_LOAD_DAC_A_IN_REG_B 0x00
|
||||
#define MAX5821_LOAD_DAC_B_IN_REG_A 0x10
|
||||
#define MAX5821_EXTENDED_COMMAND_MODE 0xf0
|
||||
#define MAX5821_READ_DAC_A_COMMAND 0xf1
|
||||
#define MAX5821_READ_DAC_B_COMMAND 0xf2
|
||||
|
||||
#define MAX5821_EXTENDED_POWER_UP 0x00
|
||||
#define MAX5821_EXTENDED_POWER_DOWN_MODE0 0x01
|
||||
#define MAX5821_EXTENDED_POWER_DOWN_MODE1 0x02
|
||||
#define MAX5821_EXTENDED_POWER_DOWN_MODE2 0x03
|
||||
#define MAX5821_EXTENDED_DAC_A 0x04
|
||||
#define MAX5821_EXTENDED_DAC_B 0x08
|
||||
|
||||
enum max5821_device_ids {
|
||||
ID_MAX5821,
|
||||
};
|
||||
|
||||
struct max5821_data {
|
||||
struct i2c_client *client;
|
||||
struct regulator *vref_reg;
|
||||
unsigned short vref_mv;
|
||||
bool powerdown[MAX5821_MAX_DAC_CHANNELS];
|
||||
u8 powerdown_mode[MAX5821_MAX_DAC_CHANNELS];
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const char * const max5821_powerdown_modes[] = {
|
||||
"three_state",
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX5821_THREE_STATE,
|
||||
MAX5821_1KOHM_TO_GND,
|
||||
MAX5821_100KOHM_TO_GND
|
||||
};
|
||||
|
||||
static int max5821_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct max5821_data *st = iio_priv(indio_dev);
|
||||
|
||||
return st->powerdown_mode[chan->channel];
|
||||
}
|
||||
|
||||
static int max5821_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct max5821_data *st = iio_priv(indio_dev);
|
||||
|
||||
st->powerdown_mode[chan->channel] = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum max5821_powerdown_mode_enum = {
|
||||
.items = max5821_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(max5821_powerdown_modes),
|
||||
.get = max5821_get_powerdown_mode,
|
||||
.set = max5821_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t max5821_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct max5821_data *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->powerdown[chan->channel]);
|
||||
}
|
||||
|
||||
static int max5821_sync_powerdown_mode(struct max5821_data *data,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
u8 outbuf[2];
|
||||
|
||||
outbuf[0] = MAX5821_EXTENDED_COMMAND_MODE;
|
||||
|
||||
if (chan->channel == 0)
|
||||
outbuf[1] = MAX5821_EXTENDED_DAC_A;
|
||||
else
|
||||
outbuf[1] = MAX5821_EXTENDED_DAC_B;
|
||||
|
||||
if (data->powerdown[chan->channel])
|
||||
outbuf[1] |= data->powerdown_mode[chan->channel] + 1;
|
||||
else
|
||||
outbuf[1] |= MAX5821_EXTENDED_POWER_UP;
|
||||
|
||||
return i2c_master_send(data->client, outbuf, 2);
|
||||
}
|
||||
|
||||
static ssize_t max5821_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
bool powerdown;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &powerdown);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->powerdown[chan->channel] = powerdown;
|
||||
|
||||
ret = max5821_sync_powerdown_mode(data, chan);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info max5821_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = max5821_read_dac_powerdown,
|
||||
.write = max5821_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &max5821_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &max5821_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define MAX5821_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.ext_info = max5821_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max5821_channels[] = {
|
||||
MAX5821_CHANNEL(0),
|
||||
MAX5821_CHANNEL(1)
|
||||
};
|
||||
|
||||
static const u8 max5821_read_dac_command[] = {
|
||||
MAX5821_READ_DAC_A_COMMAND,
|
||||
MAX5821_READ_DAC_B_COMMAND
|
||||
};
|
||||
|
||||
static const u8 max5821_load_dac_command[] = {
|
||||
MAX5821_LOAD_DAC_A_IN_REG_B,
|
||||
MAX5821_LOAD_DAC_B_IN_REG_A
|
||||
};
|
||||
|
||||
static int max5821_get_value(struct iio_dev *indio_dev,
|
||||
int *val, int channel)
|
||||
{
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 outbuf[1];
|
||||
u8 inbuf[2];
|
||||
int ret;
|
||||
|
||||
if ((channel != 0) && (channel != 1))
|
||||
return -EINVAL;
|
||||
|
||||
outbuf[0] = max5821_read_dac_command[channel];
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
ret = i2c_master_send(client, outbuf, 1);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
} else if (ret != 1) {
|
||||
mutex_unlock(&data->lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(client, inbuf, 2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
} else if (ret != 2) {
|
||||
mutex_unlock(&data->lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
*val = ((inbuf[0] & 0x0f) << 6) | (inbuf[1] >> 2);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int max5821_set_value(struct iio_dev *indio_dev,
|
||||
int val, int channel)
|
||||
{
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 outbuf[2];
|
||||
int ret;
|
||||
|
||||
if ((val < 0) || (val > 1023))
|
||||
return -EINVAL;
|
||||
|
||||
if ((channel != 0) && (channel != 1))
|
||||
return -EINVAL;
|
||||
|
||||
outbuf[0] = max5821_load_dac_command[channel];
|
||||
outbuf[0] |= val >> 6;
|
||||
outbuf[1] = (val & 0x3f) << 2;
|
||||
|
||||
ret = i2c_master_send(client, outbuf, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 2)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max5821_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return max5821_get_value(indio_dev, val, chan->channel);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = data->vref_mv;
|
||||
*val2 = 10;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int max5821_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return max5821_set_value(indio_dev, val, chan->channel);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max5821_suspend(struct device *dev)
|
||||
{
|
||||
u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
|
||||
MAX5821_EXTENDED_DAC_A |
|
||||
MAX5821_EXTENDED_DAC_B |
|
||||
MAX5821_EXTENDED_POWER_DOWN_MODE2 };
|
||||
|
||||
return i2c_master_send(to_i2c_client(dev), outbuf, 2);
|
||||
}
|
||||
|
||||
static int max5821_resume(struct device *dev)
|
||||
{
|
||||
u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
|
||||
MAX5821_EXTENDED_DAC_A |
|
||||
MAX5821_EXTENDED_DAC_B |
|
||||
MAX5821_EXTENDED_POWER_UP };
|
||||
|
||||
return i2c_master_send(to_i2c_client(dev), outbuf, 2);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max5821_pm_ops, max5821_suspend, max5821_resume);
|
||||
#define MAX5821_PM_OPS (&max5821_pm_ops)
|
||||
#else
|
||||
#define MAX5821_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct iio_info max5821_info = {
|
||||
.read_raw = max5821_read_raw,
|
||||
.write_raw = max5821_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int max5821_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max5821_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
/* max5821 start in powerdown mode 100Kohm to ground */
|
||||
for (tmp = 0; tmp < MAX5821_MAX_DAC_CHANNELS; tmp++) {
|
||||
data->powerdown[tmp] = true;
|
||||
data->powerdown_mode[tmp] = MAX5821_100KOHM_TO_GND;
|
||||
}
|
||||
|
||||
data->vref_reg = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(data->vref_reg)) {
|
||||
ret = PTR_ERR(data->vref_reg);
|
||||
dev_err(&client->dev,
|
||||
"Failed to get vref regulator: %d\n", ret);
|
||||
goto error_free_reg;
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to enable vref regulator: %d\n", ret);
|
||||
goto error_free_reg;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(data->vref_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to get voltage on regulator: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
data->vref_mv = ret / 1000;
|
||||
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max5821_channels);
|
||||
indio_dev->channels = max5821_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &max5821_info;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
|
||||
error_disable_reg:
|
||||
regulator_disable(data->vref_reg);
|
||||
|
||||
error_free_reg:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max5821_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(data->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max5821_id[] = {
|
||||
{ "max5821", ID_MAX5821 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max5821_id);
|
||||
|
||||
static const struct of_device_id max5821_of_match[] = {
|
||||
{ .compatible = "maxim,max5821" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct i2c_driver max5821_driver = {
|
||||
.driver = {
|
||||
.name = "max5821",
|
||||
.pm = MAX5821_PM_OPS,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max5821_probe,
|
||||
.remove = max5821_remove,
|
||||
.id_table = max5821_id,
|
||||
};
|
||||
module_i2c_driver(max5821_driver);
|
||||
|
||||
MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>");
|
||||
MODULE_DESCRIPTION("MAX5821 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
349
drivers/iio/dac/mcp4725.c
Normal file
349
drivers/iio/dac/mcp4725.c
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* mcp4725.c - Support for Microchip MCP4725
|
||||
*
|
||||
* Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* Based on max517 by Roland Stigge <stigge@antcom.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* driver for the Microchip I2C 12-bit digital-to-analog converter (DAC)
|
||||
* (7-bit I2C slave address 0x60, the three LSBs can be configured in
|
||||
* hardware)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include <linux/iio/dac/mcp4725.h>
|
||||
|
||||
#define MCP4725_DRV_NAME "mcp4725"
|
||||
|
||||
struct mcp4725_data {
|
||||
struct i2c_client *client;
|
||||
u16 vref_mv;
|
||||
u16 dac_value;
|
||||
bool powerdown;
|
||||
unsigned powerdown_mode;
|
||||
};
|
||||
|
||||
static int mcp4725_suspend(struct device *dev)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
u8 outbuf[2];
|
||||
|
||||
outbuf[0] = (data->powerdown_mode + 1) << 4;
|
||||
outbuf[1] = 0;
|
||||
data->powerdown = true;
|
||||
|
||||
return i2c_master_send(data->client, outbuf, 2);
|
||||
}
|
||||
|
||||
static int mcp4725_resume(struct device *dev)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
u8 outbuf[2];
|
||||
|
||||
/* restore previous DAC value */
|
||||
outbuf[0] = (data->dac_value >> 8) & 0xf;
|
||||
outbuf[1] = data->dac_value & 0xff;
|
||||
data->powerdown = false;
|
||||
|
||||
return i2c_master_send(data->client, outbuf, 2);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume);
|
||||
#define MCP4725_PM_OPS (&mcp4725_pm_ops)
|
||||
#else
|
||||
#define MCP4725_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static ssize_t mcp4725_store_eeprom(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
int tries = 20;
|
||||
u8 inoutbuf[3];
|
||||
bool state;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!state)
|
||||
return 0;
|
||||
|
||||
inoutbuf[0] = 0x60; /* write EEPROM */
|
||||
inoutbuf[1] = data->dac_value >> 4;
|
||||
inoutbuf[2] = (data->dac_value & 0xf) << 4;
|
||||
|
||||
ret = i2c_master_send(data->client, inoutbuf, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 3)
|
||||
return -EIO;
|
||||
|
||||
/* wait for write complete, takes up to 50ms */
|
||||
while (tries--) {
|
||||
msleep(20);
|
||||
ret = i2c_master_recv(data->client, inoutbuf, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 3)
|
||||
return -EIO;
|
||||
|
||||
if (inoutbuf[0] & 0x80)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"mcp4725_store_eeprom() failed, incomplete\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR, NULL, mcp4725_store_eeprom, 0);
|
||||
|
||||
static struct attribute *mcp4725_attributes[] = {
|
||||
&iio_dev_attr_store_eeprom.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group mcp4725_attribute_group = {
|
||||
.attrs = mcp4725_attributes,
|
||||
};
|
||||
|
||||
static const char * const mcp4725_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"500kohm_to_gnd"
|
||||
};
|
||||
|
||||
static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
|
||||
return data->powerdown_mode;
|
||||
}
|
||||
|
||||
static int mcp4725_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned mode)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
|
||||
data->powerdown_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t mcp4725_read_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", data->powerdown);
|
||||
}
|
||||
|
||||
static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
bool state;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (state)
|
||||
ret = mcp4725_suspend(&data->client->dev);
|
||||
else
|
||||
ret = mcp4725_resume(&data->client->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = mcp4725_read_powerdown,
|
||||
.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),
|
||||
{ },
|
||||
};
|
||||
|
||||
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 int mcp4725_set_value(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
u8 outbuf[2];
|
||||
int ret;
|
||||
|
||||
if (val >= (1 << 12) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
outbuf[0] = (val >> 8) & 0xf;
|
||||
outbuf[1] = val & 0xff;
|
||||
|
||||
ret = i2c_master_send(data->client, outbuf, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 2)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp4725_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = data->dac_value;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = data->vref_mv;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp4725_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = mcp4725_set_value(indio_dev, val);
|
||||
data->dac_value = val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info mcp4725_info = {
|
||||
.read_raw = mcp4725_read_raw,
|
||||
.write_raw = mcp4725_write_raw,
|
||||
.attrs = &mcp4725_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mcp4725_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mcp4725_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp4725_platform_data *platform_data = client->dev.platform_data;
|
||||
u8 inbuf[3];
|
||||
u8 pd;
|
||||
int err;
|
||||
|
||||
if (!platform_data || !platform_data->vref_mv) {
|
||||
dev_err(&client->dev, "invalid platform data");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &mcp4725_info;
|
||||
indio_dev->channels = &mcp4725_channel;
|
||||
indio_dev->num_channels = 1;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data->vref_mv = platform_data->vref_mv;
|
||||
|
||||
/* read current DAC value */
|
||||
err = i2c_master_recv(client, inbuf, 3);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "failed to read DAC value");
|
||||
return err;
|
||||
}
|
||||
pd = (inbuf[0] >> 1) & 0x3;
|
||||
data->powerdown = pd > 0 ? true : false;
|
||||
data->powerdown_mode = pd ? pd-1 : 2; /* 500kohm_to_gnd */
|
||||
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int mcp4725_remove(struct i2c_client *client)
|
||||
{
|
||||
iio_device_unregister(i2c_get_clientdata(client));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4725_id[] = {
|
||||
{ "mcp4725", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4725_id);
|
||||
|
||||
static struct i2c_driver mcp4725_driver = {
|
||||
.driver = {
|
||||
.name = MCP4725_DRV_NAME,
|
||||
.pm = MCP4725_PM_OPS,
|
||||
},
|
||||
.probe = mcp4725_probe,
|
||||
.remove = mcp4725_remove,
|
||||
.id_table = mcp4725_id,
|
||||
};
|
||||
module_i2c_driver(mcp4725_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("MCP4725 12-bit DAC");
|
||||
MODULE_LICENSE("GPL");
|
216
drivers/iio/dac/mcp4922.c
Normal file
216
drivers/iio/dac/mcp4922.c
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* mcp4922.c
|
||||
*
|
||||
* Driver for Microchip Digital to Analog Converters.
|
||||
* Supports MCP4902, MCP4912, and MCP4922.
|
||||
*
|
||||
* Copyright (c) 2014 EMAC Inc.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define MCP4922_NUM_CHANNELS 2
|
||||
|
||||
enum mcp4922_supported_device_ids {
|
||||
ID_MCP4902,
|
||||
ID_MCP4912,
|
||||
ID_MCP4922,
|
||||
};
|
||||
|
||||
struct mcp4922_state {
|
||||
struct spi_device *spi;
|
||||
unsigned int value[MCP4922_NUM_CHANNELS];
|
||||
unsigned int vref_mv;
|
||||
struct regulator *vref_reg;
|
||||
u8 mosi[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#define MCP4922_CHAN(chan, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.output = 1, \
|
||||
.indexed = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
static int mcp4922_spi_write(struct mcp4922_state *state, u8 addr, u32 val)
|
||||
{
|
||||
state->mosi[1] = val & 0xff;
|
||||
state->mosi[0] = (addr == 0) ? 0x00 : 0x80;
|
||||
state->mosi[0] |= 0x30 | ((val >> 8) & 0x0f);
|
||||
|
||||
return spi_write(state->spi, state->mosi, 2);
|
||||
}
|
||||
|
||||
static int mcp4922_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct mcp4922_state *state = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = state->value[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = state->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mcp4922_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct mcp4922_state *state = iio_priv(indio_dev);
|
||||
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val > GENMASK(chan->scan_type.realbits-1, 0))
|
||||
return -EINVAL;
|
||||
val <<= chan->scan_type.shift;
|
||||
state->value[chan->channel] = val;
|
||||
return mcp4922_spi_write(state, chan->channel, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mcp4922_channels[3][MCP4922_NUM_CHANNELS] = {
|
||||
[ID_MCP4902] = { MCP4922_CHAN(0, 8), MCP4922_CHAN(1, 8) },
|
||||
[ID_MCP4912] = { MCP4922_CHAN(0, 10), MCP4922_CHAN(1, 10) },
|
||||
[ID_MCP4922] = { MCP4922_CHAN(0, 12), MCP4922_CHAN(1, 12) },
|
||||
};
|
||||
|
||||
static const struct iio_info mcp4922_info = {
|
||||
.read_raw = &mcp4922_read_raw,
|
||||
.write_raw = &mcp4922_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mcp4922_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp4922_state *state;
|
||||
const struct spi_device_id *id;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
state->spi = spi;
|
||||
state->vref_reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(state->vref_reg)) {
|
||||
dev_err(&spi->dev, "Vref regulator not specified\n");
|
||||
return PTR_ERR(state->vref_reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(state->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable vref regulator: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(state->vref_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Failed to read vref regulator: %d\n",
|
||||
ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
state->vref_mv = ret / 1000;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
id = spi_get_device_id(spi);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &mcp4922_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mcp4922_channels[id->driver_data];
|
||||
indio_dev->num_channels = MCP4922_NUM_CHANNELS;
|
||||
indio_dev->name = id->name;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device: %d\n",
|
||||
ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
regulator_disable(state->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp4922_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct mcp4922_state *state;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
state = iio_priv(indio_dev);
|
||||
regulator_disable(state->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id mcp4922_id[] = {
|
||||
{"mcp4902", ID_MCP4902},
|
||||
{"mcp4912", ID_MCP4912},
|
||||
{"mcp4922", ID_MCP4922},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mcp4922_id);
|
||||
|
||||
static struct spi_driver mcp4922_driver = {
|
||||
.driver = {
|
||||
.name = "mcp4922",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mcp4922_probe,
|
||||
.remove = mcp4922_remove,
|
||||
.id_table = mcp4922_id,
|
||||
};
|
||||
module_spi_driver(mcp4922_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Welling <mwelling@ieee.org>");
|
||||
MODULE_DESCRIPTION("Microchip MCP4902, MCP4912, MCP4922 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
Loading…
Add table
Add a link
Reference in a new issue