// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5755 , AD5755 - 1 , AD5757 , AD5735 , AD5737 Digital to analog converters driver
*
* Copyright 2012 Analog Devices Inc .
*/
#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/property.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.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) (0 x08 | (chan))
#define AD5755_WRITE_REG_OFFSET(chan) (0 x10 | (chan))
#define AD5755_WRITE_REG_CTRL(chan) (0 x1c | (chan))
#define AD5755_READ_REG_DATA(chan) (chan)
#define AD5755_READ_REG_CTRL(chan) (0 x4 | (chan))
#define AD5755_READ_REG_GAIN(chan) (0 x8 | (chan))
#define AD5755_READ_REG_OFFSET(chan) (0 xc | (chan))
#define AD5755_READ_REG_CLEAR(chan) (0 x10 | (chan))
#define AD5755_READ_REG_SLEW(chan) (0 x14 | (chan))
#define AD5755_READ_REG_STATUS 0 x18
#define AD5755_READ_REG_MAIN 0 x19
#define AD5755_READ_REG_DC_DC 0 x1a
#define AD5755_CTRL_REG_SLEW 0 x0
#define AD5755_CTRL_REG_MAIN 0 x1
#define AD5755_CTRL_REG_DAC 0 x2
#define AD5755_CTRL_REG_DC_DC 0 x3
#define AD5755_CTRL_REG_SW 0 x4
#define AD5755_READ_FLAG 0 x800000
#define AD5755_NOOP 0 x1CE000
#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 )
enum ad5755_mode {
AD5755_MODE_VOLTAGE_0V_5V = 0 ,
AD5755_MODE_VOLTAGE_0V_10V = 1 ,
AD5755_MODE_VOLTAGE_PLUSMINUS_5V = 2 ,
AD5755_MODE_VOLTAGE_PLUSMINUS_10V = 3 ,
AD5755_MODE_CURRENT_4mA_20mA = 4 ,
AD5755_MODE_CURRENT_0mA_20mA = 5 ,
AD5755_MODE_CURRENT_0mA_24mA = 6 ,
};
enum ad5755_dc_dc_phase {
AD5755_DC_DC_PHASE_ALL_SAME_EDGE = 0 ,
AD5755_DC_DC_PHASE_A_B_SAME_EDGE_C_D_OPP_EDGE = 1 ,
AD5755_DC_DC_PHASE_A_C_SAME_EDGE_B_D_OPP_EDGE = 2 ,
AD5755_DC_DC_PHASE_90_DEGREE = 3 ,
};
enum ad5755_dc_dc_freq {
AD5755_DC_DC_FREQ_250kHZ = 0 ,
AD5755_DC_DC_FREQ_410kHZ = 1 ,
AD5755_DC_DC_FREQ_650kHZ = 2 ,
};
enum ad5755_dc_dc_maxv {
AD5755_DC_DC_MAXV_23V = 0 ,
AD5755_DC_DC_MAXV_24V5 = 1 ,
AD5755_DC_DC_MAXV_27V = 2 ,
AD5755_DC_DC_MAXV_29V5 = 3 ,
};
enum ad5755_slew_rate {
AD5755_SLEW_RATE_64k = 0 ,
AD5755_SLEW_RATE_32k = 1 ,
AD5755_SLEW_RATE_16k = 2 ,
AD5755_SLEW_RATE_8k = 3 ,
AD5755_SLEW_RATE_4k = 4 ,
AD5755_SLEW_RATE_2k = 5 ,
AD5755_SLEW_RATE_1k = 6 ,
AD5755_SLEW_RATE_500 = 7 ,
AD5755_SLEW_RATE_250 = 8 ,
AD5755_SLEW_RATE_125 = 9 ,
AD5755_SLEW_RATE_64 = 10 ,
AD5755_SLEW_RATE_32 = 11 ,
AD5755_SLEW_RATE_16 = 12 ,
AD5755_SLEW_RATE_8 = 13 ,
AD5755_SLEW_RATE_4 = 14 ,
AD5755_SLEW_RATE_0_5 = 15 ,
};
enum ad5755_slew_step_size {
AD5755_SLEW_STEP_SIZE_1 = 0 ,
AD5755_SLEW_STEP_SIZE_2 = 1 ,
AD5755_SLEW_STEP_SIZE_4 = 2 ,
AD5755_SLEW_STEP_SIZE_8 = 3 ,
AD5755_SLEW_STEP_SIZE_16 = 4 ,
AD5755_SLEW_STEP_SIZE_32 = 5 ,
AD5755_SLEW_STEP_SIZE_64 = 6 ,
AD5755_SLEW_STEP_SIZE_128 = 7 ,
AD5755_SLEW_STEP_SIZE_256 = 8 ,
};
/**
* struct ad5755_platform_data - AD5755 DAC driver platform data
* @ ext_dc_dc_compenstation_resistor : Whether an external DC - DC converter
* compensation register is used .
* @ dc_dc_phase : DC - DC converter phase .
* @ dc_dc_freq : DC - DC converter frequency .
* @ dc_dc_maxv : DC - DC maximum allowed boost voltage .
* @ dac : Per DAC instance parameters .
* @ dac . mode : The mode to be used for the DAC output .
* @ dac . ext_current_sense_resistor : Whether an external current sense resistor
* is used .
* @ dac . enable_voltage_overrange : Whether to enable 20 % voltage output overrange .
* @ dac . slew . enable : Whether to enable digital slew .
* @ dac . slew . rate : Slew rate of the digital slew .
* @ dac . slew . step_size : Slew step size of the digital slew .
**/
struct ad5755_platform_data {
bool ext_dc_dc_compenstation_resistor;
enum ad5755_dc_dc_phase dc_dc_phase;
enum ad5755_dc_dc_freq dc_dc_freq;
enum ad5755_dc_dc_maxv dc_dc_maxv;
struct {
enum ad5755_mode mode;
bool ext_current_sense_resistor;
bool enable_voltage_overrange;
struct {
bool enable;
enum ad5755_slew_rate rate;
enum ad5755_slew_step_size step_size;
} slew;
} dac[4 ];
};
/**
* 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
* @ lock : lock to protect the data buffer during SPI ops
* @ 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];
struct mutex lock;
/*
* DMA ( thus cache coherency maintenance ) may require the
* transfer buffers to live in their own cache lines .
*/
union {
__be32 d32;
u8 d8[4 ];
} data[2 ] __aligned(IIO_DMA_MINALIGN);
};
enum ad5755_type {
ID_AD5755,
ID_AD5757,
ID_AD5735,
ID_AD5737,
};
static const int ad5755_dcdc_freq_table[][2 ] = {
{ 250000 , AD5755_DC_DC_FREQ_250kHZ },
{ 410000 , AD5755_DC_DC_FREQ_410kHZ },
{ 650000 , AD5755_DC_DC_FREQ_650kHZ }
};
static const int ad5755_dcdc_maxv_table[][2 ] = {
{ 23000000 , AD5755_DC_DC_MAXV_23V },
{ 24500000 , AD5755_DC_DC_MAXV_24V5 },
{ 27000000 , AD5755_DC_DC_MAXV_27V },
{ 29500000 , AD5755_DC_DC_MAXV_29V5 },
};
static const int ad5755_slew_rate_table[][2 ] = {
{ 64000 , AD5755_SLEW_RATE_64k },
{ 32000 , AD5755_SLEW_RATE_32k },
{ 16000 , AD5755_SLEW_RATE_16k },
{ 8000 , AD5755_SLEW_RATE_8k },
{ 4000 , AD5755_SLEW_RATE_4k },
{ 2000 , AD5755_SLEW_RATE_2k },
{ 1000 , AD5755_SLEW_RATE_1k },
{ 500 , AD5755_SLEW_RATE_500 },
{ 250 , AD5755_SLEW_RATE_250 },
{ 125 , AD5755_SLEW_RATE_125 },
{ 64 , AD5755_SLEW_RATE_64 },
{ 32 , AD5755_SLEW_RATE_32 },
{ 16 , AD5755_SLEW_RATE_16 },
{ 8 , AD5755_SLEW_RATE_8 },
{ 4 , AD5755_SLEW_RATE_4 },
{ 0 , AD5755_SLEW_RATE_0_5 },
};
static const int ad5755_slew_step_table[][2 ] = {
{ 256 , AD5755_SLEW_STEP_SIZE_256 },
{ 128 , AD5755_SLEW_STEP_SIZE_128 },
{ 64 , AD5755_SLEW_STEP_SIZE_64 },
{ 32 , AD5755_SLEW_STEP_SIZE_32 },
{ 16 , AD5755_SLEW_STEP_SIZE_16 },
{ 4 , AD5755_SLEW_STEP_SIZE_4 },
{ 2 , AD5755_SLEW_STEP_SIZE_2 },
{ 1 , AD5755_SLEW_STEP_SIZE_1 },
};
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)
{
struct ad5755_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
ret = ad5755_write_unlocked(indio_dev, reg, val);
mutex_unlock(&st->lock);
return ret;
}
static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel,
unsigned int reg, unsigned int val)
{
struct ad5755_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val);
mutex_unlock(&st->lock);
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(&st->lock);
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) & 0 xffff;
mutex_unlock(&st->lock);
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(&st->lock);
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(&st->lock);
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 > 0 xffff)
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 sysfs_emit(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 = kstrtobool(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,
};
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 struct ad5755_platform_data *ad5755_parse_fw(struct device *dev)
{
struct ad5755_platform_data *pdata;
unsigned int tmp;
unsigned int tmparray[3 ];
int devnr, i;
if (!dev_fwnode(dev))
return NULL;
pdata = devm_kzalloc(dev, sizeof (*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
pdata->ext_dc_dc_compenstation_resistor =
device_property_read_bool(dev, "adi,ext-dc-dc-compenstation-resistor" );
pdata->dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE;
device_property_read_u32(dev, "adi,dc-dc-phase" , &pdata->dc_dc_phase);
pdata->dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ;
if (!device_property_read_u32(dev, "adi,dc-dc-freq-hz" , &tmp)) {
for (i = 0 ; i < ARRAY_SIZE(ad5755_dcdc_freq_table); i++) {
if (tmp == ad5755_dcdc_freq_table[i][0 ]) {
pdata->dc_dc_freq = ad5755_dcdc_freq_table[i][1 ];
break ;
}
}
if (i == ARRAY_SIZE(ad5755_dcdc_freq_table))
dev_err(dev,
"adi,dc-dc-freq out of range selecting 410kHz\n" );
}
pdata->dc_dc_maxv = AD5755_DC_DC_MAXV_23V;
if (!device_property_read_u32(dev, "adi,dc-dc-max-microvolt" , &tmp)) {
for (i = 0 ; i < ARRAY_SIZE(ad5755_dcdc_maxv_table); i++) {
if (tmp == ad5755_dcdc_maxv_table[i][0 ]) {
pdata->dc_dc_maxv = ad5755_dcdc_maxv_table[i][1 ];
break ;
}
}
if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table))
dev_err(dev,
"adi,dc-dc-maxv out of range selecting 23V\n" );
}
devnr = 0 ;
device_for_each_child_node_scoped(dev, pp) {
if (devnr >= AD5755_NUM_CHANNELS) {
dev_err(dev,
"There are too many channels defined in DT\n" );
devm_kfree(dev, pdata);
return NULL;
}
pdata->dac[devnr].mode = AD5755_MODE_CURRENT_4mA_20mA;
fwnode_property_read_u32(pp, "adi,mode" , &pdata->dac[devnr].mode);
pdata->dac[devnr].ext_current_sense_resistor =
fwnode_property_read_bool(pp, "adi,ext-current-sense-resistor" );
pdata->dac[devnr].enable_voltage_overrange =
fwnode_property_read_bool(pp, "adi,enable-voltage-overrange" );
if (!fwnode_property_read_u32_array(pp, "adi,slew" , tmparray, 3 )) {
pdata->dac[devnr].slew.enable = tmparray[0 ];
pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k;
for (i = 0 ; i < ARRAY_SIZE(ad5755_slew_rate_table); i++) {
if (tmparray[1 ] == ad5755_slew_rate_table[i][0 ]) {
pdata->dac[devnr].slew.rate =
ad5755_slew_rate_table[i][1 ];
break ;
}
}
if (i == ARRAY_SIZE(ad5755_slew_rate_table))
dev_err(dev,
"channel %d slew rate out of range selecting 64kHz\n" ,
devnr);
pdata->dac[devnr].slew.step_size = AD5755_SLEW_STEP_SIZE_1;
for (i = 0 ; i < ARRAY_SIZE(ad5755_slew_step_table); i++) {
if (tmparray[2 ] == ad5755_slew_step_table[i][0 ]) {
pdata->dac[devnr].slew.step_size =
ad5755_slew_step_table[i][1 ];
break ;
}
}
if (i == ARRAY_SIZE(ad5755_slew_step_table))
dev_err(dev,
"channel %d slew step size out of range selecting 1 LSB\n" ,
devnr);
} else {
pdata->dac[devnr].slew.enable = false ;
pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k;
pdata->dac[devnr].slew.step_size =
AD5755_SLEW_STEP_SIZE_1;
}
devnr++;
}
return pdata;
}
static int ad5755_probe(struct spi_device *spi)
{
const struct ad5755_platform_data *pdata;
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 = spi_get_device_match_data(spi);
st->spi = spi;
st->pwr_down = 0 xf;
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;
mutex_init(&st->lock);
pdata = ad5755_parse_fw(&spi->dev);
if (!pdata) {
dev_warn(&spi->dev, "no firmware provided parameters? using default\n" );
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" , (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
{ "ad5755-1" , (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
{ "ad5757" , (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] },
{ "ad5735" , (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] },
{ "ad5737" , (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] },
{ }
};
MODULE_DEVICE_TABLE(spi, ad5755_id);
static const struct of_device_id ad5755_of_match[] = {
{ .compatible = "adi,ad5755" , &ad5755_chip_info_tbl[ID_AD5755] },
{ .compatible = "adi,ad5755-1" , &ad5755_chip_info_tbl[ID_AD5755] },
{ .compatible = "adi,ad5757" , &ad5755_chip_info_tbl[ID_AD5757] },
{ .compatible = "adi,ad5735" , &ad5755_chip_info_tbl[ID_AD5735] },
{ .compatible = "adi,ad5737" , &ad5755_chip_info_tbl[ID_AD5737] },
{ }
};
MODULE_DEVICE_TABLE(of, ad5755_of_match);
static struct spi_driver ad5755_driver = {
.driver = {
.name = "ad5755" ,
.of_match_table = ad5755_of_match,
},
.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" );
Messung V0.5 in Prozent C=95 H=93 G=93
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-10-12)
¤
*© Formatika GbR, Deutschland