732 lines
17 KiB
C
732 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Analog Devices AD3552R
|
|
* Digital to Analog converter driver
|
|
*
|
|
* Copyright 2021 Analog Devices Inc.
|
|
*/
|
|
#include <linux/unaligned.h>
|
|
#include <linux/bitfield.h>
|
|
#include <linux/device.h>
|
|
#include <linux/iio/triggered_buffer.h>
|
|
#include <linux/iio/trigger_consumer.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/spi/spi.h>
|
|
|
|
#include "ad3552r.h"
|
|
|
|
struct ad3552r_desc {
|
|
const struct ad3552r_model_data *model_data;
|
|
/* Used to look the spi bus for atomic operations where needed */
|
|
struct mutex lock;
|
|
struct gpio_desc *gpio_reset;
|
|
struct gpio_desc *gpio_ldac;
|
|
struct spi_device *spi;
|
|
struct ad3552r_ch_data ch_data[AD3552R_MAX_CH];
|
|
struct iio_chan_spec channels[AD3552R_MAX_CH + 1];
|
|
unsigned long enabled_ch;
|
|
unsigned int num_ch;
|
|
};
|
|
|
|
static u8 _ad3552r_reg_len(u8 addr)
|
|
{
|
|
switch (addr) {
|
|
case AD3552R_REG_ADDR_HW_LDAC_16B:
|
|
case AD3552R_REG_ADDR_CH_SELECT_16B:
|
|
case AD3552R_REG_ADDR_SW_LDAC_16B:
|
|
case AD3552R_REG_ADDR_HW_LDAC_24B:
|
|
case AD3552R_REG_ADDR_CH_SELECT_24B:
|
|
case AD3552R_REG_ADDR_SW_LDAC_24B:
|
|
return 1;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (addr > AD3552R_REG_ADDR_HW_LDAC_24B)
|
|
return 3;
|
|
if (addr > AD3552R_REG_ADDR_HW_LDAC_16B)
|
|
return 2;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* SPI transfer to device */
|
|
static int ad3552r_transfer(struct ad3552r_desc *dac, u8 addr, u32 len,
|
|
u8 *data, bool is_read)
|
|
{
|
|
/* Maximum transfer: Addr (1B) + 2 * (Data Reg (3B)) + SW LDAC(1B) */
|
|
u8 buf[8];
|
|
|
|
buf[0] = addr & AD3552R_ADDR_MASK;
|
|
buf[0] |= is_read ? AD3552R_READ_BIT : 0;
|
|
if (is_read)
|
|
return spi_write_then_read(dac->spi, buf, 1, data, len);
|
|
|
|
memcpy(buf + 1, data, len);
|
|
return spi_write_then_read(dac->spi, buf, len + 1, NULL, 0);
|
|
}
|
|
|
|
static int ad3552r_write_reg(struct ad3552r_desc *dac, u8 addr, u16 val)
|
|
{
|
|
u8 reg_len;
|
|
u8 buf[AD3552R_MAX_REG_SIZE] = { 0 };
|
|
|
|
reg_len = _ad3552r_reg_len(addr);
|
|
if (reg_len == 2)
|
|
/* Only DAC register are 2 bytes wide */
|
|
val &= AD3552R_MASK_DAC_12B;
|
|
if (reg_len == 1)
|
|
buf[0] = val & 0xFF;
|
|
else
|
|
/* reg_len can be 2 or 3, but 3rd bytes needs to be set to 0 */
|
|
put_unaligned_be16(val, buf);
|
|
|
|
return ad3552r_transfer(dac, addr, reg_len, buf, false);
|
|
}
|
|
|
|
static int ad3552r_read_reg(struct ad3552r_desc *dac, u8 addr, u16 *val)
|
|
{
|
|
int err;
|
|
u8 reg_len, buf[AD3552R_MAX_REG_SIZE] = { 0 };
|
|
|
|
reg_len = _ad3552r_reg_len(addr);
|
|
err = ad3552r_transfer(dac, addr, reg_len, buf, true);
|
|
if (err)
|
|
return err;
|
|
|
|
if (reg_len == 1)
|
|
*val = buf[0];
|
|
else
|
|
/* reg_len can be 2 or 3, but only first 2 bytes are relevant */
|
|
*val = get_unaligned_be16(buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Update field of a register, shift val if needed */
|
|
static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask,
|
|
u16 val)
|
|
{
|
|
int ret;
|
|
u16 reg;
|
|
|
|
ret = ad3552r_read_reg(dac, addr, ®);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
reg &= ~mask;
|
|
reg |= val;
|
|
|
|
return ad3552r_write_reg(dac, addr, reg);
|
|
}
|
|
|
|
#define AD3552R_CH_DAC(_idx) ((struct iio_chan_spec) { \
|
|
.type = IIO_VOLTAGE, \
|
|
.output = true, \
|
|
.indexed = true, \
|
|
.channel = _idx, \
|
|
.scan_index = _idx, \
|
|
.scan_type = { \
|
|
.sign = 'u', \
|
|
.realbits = 16, \
|
|
.storagebits = 16, \
|
|
.endianness = IIO_BE, \
|
|
}, \
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
|
BIT(IIO_CHAN_INFO_SCALE) | \
|
|
BIT(IIO_CHAN_INFO_ENABLE) | \
|
|
BIT(IIO_CHAN_INFO_OFFSET), \
|
|
})
|
|
|
|
static int ad3552r_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val,
|
|
int *val2,
|
|
long mask)
|
|
{
|
|
struct ad3552r_desc *dac = iio_priv(indio_dev);
|
|
u16 tmp_val;
|
|
int err;
|
|
u8 ch = chan->channel;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
mutex_lock(&dac->lock);
|
|
err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_CH_DAC_24B(ch),
|
|
&tmp_val);
|
|
mutex_unlock(&dac->lock);
|
|
if (err < 0)
|
|
return err;
|
|
*val = tmp_val;
|
|
return IIO_VAL_INT;
|
|
case IIO_CHAN_INFO_ENABLE:
|
|
mutex_lock(&dac->lock);
|
|
err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_POWERDOWN_CONFIG,
|
|
&tmp_val);
|
|
mutex_unlock(&dac->lock);
|
|
if (err < 0)
|
|
return err;
|
|
*val = !((tmp_val & AD3552R_MASK_CH_DAC_POWERDOWN(ch)) >>
|
|
__ffs(AD3552R_MASK_CH_DAC_POWERDOWN(ch)));
|
|
return IIO_VAL_INT;
|
|
case IIO_CHAN_INFO_SCALE:
|
|
*val = dac->ch_data[ch].scale_int;
|
|
*val2 = dac->ch_data[ch].scale_dec;
|
|
return IIO_VAL_INT_PLUS_MICRO;
|
|
case IIO_CHAN_INFO_OFFSET:
|
|
*val = dac->ch_data[ch].offset_int;
|
|
*val2 = dac->ch_data[ch].offset_dec;
|
|
return IIO_VAL_INT_PLUS_MICRO;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int ad3552r_write_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int val,
|
|
int val2,
|
|
long mask)
|
|
{
|
|
struct ad3552r_desc *dac = iio_priv(indio_dev);
|
|
int err;
|
|
|
|
mutex_lock(&dac->lock);
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
err = ad3552r_write_reg(dac,
|
|
AD3552R_REG_ADDR_CH_DAC_24B(chan->channel),
|
|
val);
|
|
break;
|
|
case IIO_CHAN_INFO_ENABLE:
|
|
if (chan->channel == 0)
|
|
val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(0), !val);
|
|
else
|
|
val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(1), !val);
|
|
|
|
err = ad3552r_update_reg_field(dac, AD3552R_REG_ADDR_POWERDOWN_CONFIG,
|
|
AD3552R_MASK_CH_DAC_POWERDOWN(chan->channel),
|
|
val);
|
|
break;
|
|
default:
|
|
err = -EINVAL;
|
|
break;
|
|
}
|
|
mutex_unlock(&dac->lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
static const struct iio_info ad3552r_iio_info = {
|
|
.read_raw = ad3552r_read_raw,
|
|
.write_raw = ad3552r_write_raw
|
|
};
|
|
|
|
static int32_t ad3552r_trigger_hw_ldac(struct gpio_desc *ldac)
|
|
{
|
|
gpiod_set_value_cansleep(ldac, 0);
|
|
usleep_range(AD3552R_LDAC_PULSE_US, AD3552R_LDAC_PULSE_US + 10);
|
|
gpiod_set_value_cansleep(ldac, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad3552r_write_all_channels(struct ad3552r_desc *dac, u8 *data)
|
|
{
|
|
int err, len;
|
|
u8 addr, buff[AD3552R_MAX_CH * AD3552R_MAX_REG_SIZE + 1];
|
|
|
|
addr = AD3552R_REG_ADDR_CH_INPUT_24B(1);
|
|
/* CH1 */
|
|
memcpy(buff, data + 2, 2);
|
|
buff[2] = 0;
|
|
/* CH0 */
|
|
memcpy(buff + 3, data, 2);
|
|
buff[5] = 0;
|
|
len = 6;
|
|
if (!dac->gpio_ldac) {
|
|
/* Software LDAC */
|
|
buff[6] = AD3552R_MASK_ALL_CH;
|
|
++len;
|
|
}
|
|
err = ad3552r_transfer(dac, addr, len, buff, false);
|
|
if (err)
|
|
return err;
|
|
|
|
if (dac->gpio_ldac)
|
|
return ad3552r_trigger_hw_ldac(dac->gpio_ldac);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad3552r_write_codes(struct ad3552r_desc *dac, u32 mask, u8 *data)
|
|
{
|
|
int err;
|
|
u8 addr, buff[AD3552R_MAX_REG_SIZE];
|
|
|
|
if (mask == AD3552R_MASK_ALL_CH) {
|
|
if (memcmp(data, data + 2, 2) != 0)
|
|
return ad3552r_write_all_channels(dac, data);
|
|
|
|
addr = AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B;
|
|
} else {
|
|
addr = AD3552R_REG_ADDR_CH_INPUT_24B(__ffs(mask));
|
|
}
|
|
|
|
memcpy(buff, data, 2);
|
|
buff[2] = 0;
|
|
err = ad3552r_transfer(dac, addr, 3, data, false);
|
|
if (err)
|
|
return err;
|
|
|
|
if (dac->gpio_ldac)
|
|
return ad3552r_trigger_hw_ldac(dac->gpio_ldac);
|
|
|
|
return ad3552r_write_reg(dac, AD3552R_REG_ADDR_SW_LDAC_24B, mask);
|
|
}
|
|
|
|
static irqreturn_t ad3552r_trigger_handler(int irq, void *p)
|
|
{
|
|
struct iio_poll_func *pf = p;
|
|
struct iio_dev *indio_dev = pf->indio_dev;
|
|
struct iio_buffer *buf = indio_dev->buffer;
|
|
struct ad3552r_desc *dac = iio_priv(indio_dev);
|
|
/* Maximum size of a scan */
|
|
u8 buff[AD3552R_MAX_CH * AD3552R_MAX_REG_SIZE];
|
|
int err;
|
|
|
|
memset(buff, 0, sizeof(buff));
|
|
err = iio_pop_from_buffer(buf, buff);
|
|
if (err)
|
|
goto end;
|
|
|
|
mutex_lock(&dac->lock);
|
|
ad3552r_write_codes(dac, *indio_dev->active_scan_mask, buff);
|
|
mutex_unlock(&dac->lock);
|
|
end:
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int ad3552r_check_scratch_pad(struct ad3552r_desc *dac)
|
|
{
|
|
const u16 val1 = AD3552R_SCRATCH_PAD_TEST_VAL1;
|
|
const u16 val2 = AD3552R_SCRATCH_PAD_TEST_VAL2;
|
|
u16 val;
|
|
int err;
|
|
|
|
err = ad3552r_write_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, val1);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, &val);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (val1 != val)
|
|
return -ENODEV;
|
|
|
|
err = ad3552r_write_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, val2);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, &val);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (val2 != val)
|
|
return -ENODEV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct reg_addr_pool {
|
|
struct ad3552r_desc *dac;
|
|
u8 addr;
|
|
};
|
|
|
|
static int ad3552r_read_reg_wrapper(struct reg_addr_pool *addr)
|
|
{
|
|
int err;
|
|
u16 val;
|
|
|
|
err = ad3552r_read_reg(addr->dac, addr->addr, &val);
|
|
if (err)
|
|
return err;
|
|
|
|
return val;
|
|
}
|
|
|
|
static int ad3552r_reset(struct ad3552r_desc *dac)
|
|
{
|
|
struct reg_addr_pool addr;
|
|
int ret;
|
|
int val;
|
|
|
|
dac->gpio_reset = devm_gpiod_get_optional(&dac->spi->dev, "reset",
|
|
GPIOD_OUT_LOW);
|
|
if (IS_ERR(dac->gpio_reset))
|
|
return dev_err_probe(&dac->spi->dev, PTR_ERR(dac->gpio_reset),
|
|
"Error while getting gpio reset");
|
|
|
|
if (dac->gpio_reset) {
|
|
/* Perform hardware reset */
|
|
usleep_range(10, 20);
|
|
gpiod_set_value_cansleep(dac->gpio_reset, 1);
|
|
} else {
|
|
/* Perform software reset if no GPIO provided */
|
|
ret = ad3552r_update_reg_field(dac,
|
|
AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
|
|
AD3552R_MASK_SOFTWARE_RESET,
|
|
AD3552R_MASK_SOFTWARE_RESET);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
}
|
|
|
|
addr.dac = dac;
|
|
addr.addr = AD3552R_REG_ADDR_INTERFACE_CONFIG_B;
|
|
ret = readx_poll_timeout(ad3552r_read_reg_wrapper, &addr, val,
|
|
val == AD3552R_DEFAULT_CONFIG_B_VALUE ||
|
|
val < 0,
|
|
5000, 50000);
|
|
if (val < 0)
|
|
ret = val;
|
|
if (ret) {
|
|
dev_err(&dac->spi->dev, "Error while resetting");
|
|
return ret;
|
|
}
|
|
|
|
ret = readx_poll_timeout(ad3552r_read_reg_wrapper, &addr, val,
|
|
!(val & AD3552R_MASK_INTERFACE_NOT_READY) ||
|
|
val < 0,
|
|
5000, 50000);
|
|
if (val < 0)
|
|
ret = val;
|
|
if (ret) {
|
|
dev_err(&dac->spi->dev, "Error while resetting");
|
|
return ret;
|
|
}
|
|
|
|
return ad3552r_update_reg_field(dac,
|
|
AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
|
|
AD3552R_MASK_ADDR_ASCENSION,
|
|
FIELD_PREP(AD3552R_MASK_ADDR_ASCENSION, val));
|
|
}
|
|
|
|
static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
|
|
struct fwnode_handle *child,
|
|
u32 ch)
|
|
{
|
|
struct device *dev = &dac->spi->dev;
|
|
int err;
|
|
u8 addr;
|
|
u16 reg;
|
|
|
|
err = ad3552r_get_custom_gain(dev, child,
|
|
&dac->ch_data[ch].p,
|
|
&dac->ch_data[ch].n,
|
|
&dac->ch_data[ch].rfb,
|
|
&dac->ch_data[ch].gain_offset);
|
|
if (err)
|
|
return err;
|
|
|
|
dac->ch_data[ch].range_override = 1;
|
|
|
|
addr = AD3552R_REG_ADDR_CH_GAIN(ch);
|
|
err = ad3552r_write_reg(dac, addr,
|
|
abs((s32)dac->ch_data[ch].gain_offset) &
|
|
AD3552R_MASK_CH_OFFSET_BITS_0_7);
|
|
if (err)
|
|
return dev_err_probe(dev, err, "Error writing register\n");
|
|
|
|
reg = ad3552r_calc_custom_gain(dac->ch_data[ch].p, dac->ch_data[ch].n,
|
|
dac->ch_data[ch].gain_offset);
|
|
|
|
err = ad3552r_write_reg(dac, addr, reg);
|
|
if (err)
|
|
return dev_err_probe(dev, err, "Error writing register\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad3552r_configure_device(struct ad3552r_desc *dac)
|
|
{
|
|
struct device *dev = &dac->spi->dev;
|
|
int err, cnt = 0;
|
|
u32 val, ch;
|
|
|
|
dac->gpio_ldac = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH);
|
|
if (IS_ERR(dac->gpio_ldac))
|
|
return dev_err_probe(dev, PTR_ERR(dac->gpio_ldac),
|
|
"Error getting gpio ldac");
|
|
|
|
err = ad3552r_get_ref_voltage(dev, &val);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = ad3552r_update_reg_field(dac,
|
|
AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
|
|
AD3552R_MASK_REFERENCE_VOLTAGE_SEL,
|
|
FIELD_PREP(AD3552R_MASK_REFERENCE_VOLTAGE_SEL, val));
|
|
if (err)
|
|
return err;
|
|
|
|
err = ad3552r_get_drive_strength(dev, &val);
|
|
if (!err) {
|
|
err = ad3552r_update_reg_field(dac,
|
|
AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
|
|
AD3552R_MASK_SDO_DRIVE_STRENGTH,
|
|
FIELD_PREP(AD3552R_MASK_SDO_DRIVE_STRENGTH, val));
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
dac->num_ch = device_get_child_node_count(dev);
|
|
if (!dac->num_ch) {
|
|
dev_err(dev, "No channels defined\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
device_for_each_child_node_scoped(dev, child) {
|
|
err = fwnode_property_read_u32(child, "reg", &ch);
|
|
if (err)
|
|
return dev_err_probe(dev, err,
|
|
"mandatory reg property missing\n");
|
|
if (ch >= dac->model_data->num_hw_channels)
|
|
return dev_err_probe(dev, -EINVAL,
|
|
"reg must be less than %d\n",
|
|
dac->model_data->num_hw_channels);
|
|
|
|
err = ad3552r_get_output_range(dev, dac->model_data,
|
|
child, &val);
|
|
if (err && err != -ENOENT)
|
|
return err;
|
|
|
|
if (!err) {
|
|
if (ch == 0)
|
|
val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), val);
|
|
else
|
|
val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1), val);
|
|
|
|
err = ad3552r_update_reg_field(dac,
|
|
AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE,
|
|
AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch),
|
|
val);
|
|
if (err)
|
|
return err;
|
|
|
|
dac->ch_data[ch].range = val;
|
|
} else if (dac->model_data->requires_output_range) {
|
|
return dev_err_probe(dev, -EINVAL,
|
|
"adi,output-range-microvolt is required for %s\n",
|
|
dac->model_data->model_name);
|
|
} else {
|
|
err = ad3552r_configure_custom_gain(dac, child, ch);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
ad3552r_calc_gain_and_offset(&dac->ch_data[ch], dac->model_data);
|
|
dac->enabled_ch |= BIT(ch);
|
|
|
|
if (ch == 0)
|
|
val = FIELD_PREP(AD3552R_MASK_CH(0), 1);
|
|
else
|
|
val = FIELD_PREP(AD3552R_MASK_CH(1), 1);
|
|
|
|
err = ad3552r_update_reg_field(dac,
|
|
AD3552R_REG_ADDR_CH_SELECT_16B,
|
|
AD3552R_MASK_CH(ch), val);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
dac->channels[cnt] = AD3552R_CH_DAC(ch);
|
|
++cnt;
|
|
|
|
}
|
|
|
|
/* Disable unused channels */
|
|
for_each_clear_bit(ch, &dac->enabled_ch,
|
|
dac->model_data->num_hw_channels) {
|
|
if (ch == 0)
|
|
val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0), 1);
|
|
else
|
|
val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1), 1);
|
|
|
|
err = ad3552r_update_reg_field(dac,
|
|
AD3552R_REG_ADDR_POWERDOWN_CONFIG,
|
|
AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch),
|
|
val);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
dac->num_ch = cnt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad3552r_init(struct ad3552r_desc *dac)
|
|
{
|
|
int err;
|
|
u16 val, id;
|
|
|
|
err = ad3552r_reset(dac);
|
|
if (err) {
|
|
dev_err(&dac->spi->dev, "Reset failed\n");
|
|
return err;
|
|
}
|
|
|
|
err = ad3552r_check_scratch_pad(dac);
|
|
if (err) {
|
|
dev_err(&dac->spi->dev, "Scratch pad test failed\n");
|
|
return err;
|
|
}
|
|
|
|
err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_PRODUCT_ID_L, &val);
|
|
if (err) {
|
|
dev_err(&dac->spi->dev, "Fail read PRODUCT_ID_L\n");
|
|
return err;
|
|
}
|
|
|
|
id = val;
|
|
err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_PRODUCT_ID_H, &val);
|
|
if (err) {
|
|
dev_err(&dac->spi->dev, "Fail read PRODUCT_ID_H\n");
|
|
return err;
|
|
}
|
|
|
|
id |= val << 8;
|
|
if (id != dac->model_data->chip_id) {
|
|
dev_err(&dac->spi->dev, "Product id not matching\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
return ad3552r_configure_device(dac);
|
|
}
|
|
|
|
static int ad3552r_probe(struct spi_device *spi)
|
|
{
|
|
struct ad3552r_desc *dac;
|
|
struct iio_dev *indio_dev;
|
|
int err;
|
|
|
|
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*dac));
|
|
if (!indio_dev)
|
|
return -ENOMEM;
|
|
|
|
dac = iio_priv(indio_dev);
|
|
dac->spi = spi;
|
|
dac->model_data = spi_get_device_match_data(spi);
|
|
if (!dac->model_data)
|
|
return -EINVAL;
|
|
|
|
mutex_init(&dac->lock);
|
|
|
|
err = ad3552r_init(dac);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Config triggered buffer device */
|
|
indio_dev->name = dac->model_data->model_name;
|
|
indio_dev->dev.parent = &spi->dev;
|
|
indio_dev->info = &ad3552r_iio_info;
|
|
indio_dev->num_channels = dac->num_ch;
|
|
indio_dev->channels = dac->channels;
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
|
|
err = devm_iio_triggered_buffer_setup_ext(&indio_dev->dev, indio_dev, NULL,
|
|
&ad3552r_trigger_handler,
|
|
IIO_BUFFER_DIRECTION_OUT,
|
|
NULL,
|
|
NULL);
|
|
if (err)
|
|
return err;
|
|
|
|
return devm_iio_device_register(&spi->dev, indio_dev);
|
|
}
|
|
|
|
static const struct ad3552r_model_data ad3541r_model_data = {
|
|
.model_name = "ad3541r",
|
|
.chip_id = AD3541R_ID,
|
|
.num_hw_channels = 1,
|
|
.ranges_table = ad3542r_ch_ranges,
|
|
.num_ranges = ARRAY_SIZE(ad3542r_ch_ranges),
|
|
.requires_output_range = true,
|
|
};
|
|
|
|
static const struct ad3552r_model_data ad3542r_model_data = {
|
|
.model_name = "ad3542r",
|
|
.chip_id = AD3542R_ID,
|
|
.num_hw_channels = 2,
|
|
.ranges_table = ad3542r_ch_ranges,
|
|
.num_ranges = ARRAY_SIZE(ad3542r_ch_ranges),
|
|
.requires_output_range = true,
|
|
};
|
|
|
|
static const struct ad3552r_model_data ad3551r_model_data = {
|
|
.model_name = "ad3551r",
|
|
.chip_id = AD3551R_ID,
|
|
.num_hw_channels = 1,
|
|
.ranges_table = ad3552r_ch_ranges,
|
|
.num_ranges = ARRAY_SIZE(ad3552r_ch_ranges),
|
|
.requires_output_range = false,
|
|
};
|
|
|
|
static const struct ad3552r_model_data ad3552r_model_data = {
|
|
.model_name = "ad3552r",
|
|
.chip_id = AD3552R_ID,
|
|
.num_hw_channels = 2,
|
|
.ranges_table = ad3552r_ch_ranges,
|
|
.num_ranges = ARRAY_SIZE(ad3552r_ch_ranges),
|
|
.requires_output_range = false,
|
|
};
|
|
|
|
static const struct spi_device_id ad3552r_id[] = {
|
|
{
|
|
.name = "ad3541r",
|
|
.driver_data = (kernel_ulong_t)&ad3541r_model_data
|
|
},
|
|
{
|
|
.name = "ad3542r",
|
|
.driver_data = (kernel_ulong_t)&ad3542r_model_data
|
|
},
|
|
{
|
|
.name = "ad3551r",
|
|
.driver_data = (kernel_ulong_t)&ad3551r_model_data
|
|
},
|
|
{
|
|
.name = "ad3552r",
|
|
.driver_data = (kernel_ulong_t)&ad3552r_model_data
|
|
},
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(spi, ad3552r_id);
|
|
|
|
static const struct of_device_id ad3552r_of_match[] = {
|
|
{ .compatible = "adi,ad3541r", .data = &ad3541r_model_data },
|
|
{ .compatible = "adi,ad3542r", .data = &ad3542r_model_data },
|
|
{ .compatible = "adi,ad3551r", .data = &ad3551r_model_data },
|
|
{ .compatible = "adi,ad3552r", .data = &ad3552r_model_data },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, ad3552r_of_match);
|
|
|
|
static struct spi_driver ad3552r_driver = {
|
|
.driver = {
|
|
.name = "ad3552r",
|
|
.of_match_table = ad3552r_of_match,
|
|
},
|
|
.probe = ad3552r_probe,
|
|
.id_table = ad3552r_id
|
|
};
|
|
module_spi_driver(ad3552r_driver);
|
|
|
|
MODULE_AUTHOR("Mihail Chindris <mihail.chindris@analog.com>");
|
|
MODULE_DESCRIPTION("Analog Device AD3552R DAC");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_IMPORT_NS("IIO_AD3552R");
|