Commit 8f2b5482 authored by Mihail Chindris's avatar Mihail Chindris Committed by Jonathan Cameron

drivers:iio:dac: Add AD3552R driver support

The AD3552R-16 is a low drift ultrafast, 16-bit accuracy,
current output digital-to-analog converter (DAC) designed
to generate multiple output voltage span ranges.
The AD3552R-16 operates with a fixed 2.5V reference.

Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad3552r.pdfSigned-off-by: default avatarMihail Chindris <mihail.chindris@analog.com>
Link: https://lore.kernel.org/r/20211213110825.244347-3-mihail.chindris@analog.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent b0a96c5f
...@@ -6,6 +6,16 @@ ...@@ -6,6 +6,16 @@
menu "Digital to analog converters" menu "Digital to analog converters"
config AD3552R
tristate "Analog Devices AD3552R DAC driver"
depends on SPI_MASTER
help
Say yes here to build support for Analog Devices AD3552R
Digital to Analog Converter.
To compile this driver as a module, choose M here: the
module will be called ad3552r.
config AD5064 config AD5064
tristate "Analog Devices AD5064 and similar multi-channel DAC driver" tristate "Analog Devices AD5064 and similar multi-channel DAC driver"
depends on (SPI_MASTER && I2C!=m) || I2C depends on (SPI_MASTER && I2C!=m) || I2C
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
# #
# When adding new entries keep the list in alphabetical order # When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD3552R) += ad3552r.o
obj-$(CONFIG_AD5360) += ad5360.o obj-$(CONFIG_AD5360) += ad5360.o
obj-$(CONFIG_AD5380) += ad5380.o obj-$(CONFIG_AD5380) += ad5380.o
obj-$(CONFIG_AD5421) += ad5421.o obj-$(CONFIG_AD5421) += ad5421.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices AD3552R
* Digital to Analog converter driver
*
* Copyright 2021 Analog Devices Inc.
*/
#include <asm/unaligned.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/regulator/consumer.h>
#include <linux/spi/spi.h>
/* Register addresses */
/* Primary address space */
#define AD3552R_REG_ADDR_INTERFACE_CONFIG_A 0x00
#define AD3552R_MASK_SOFTWARE_RESET (BIT(7) | BIT(0))
#define AD3552R_MASK_ADDR_ASCENSION BIT(5)
#define AD3552R_MASK_SDO_ACTIVE BIT(4)
#define AD3552R_REG_ADDR_INTERFACE_CONFIG_B 0x01
#define AD3552R_MASK_SINGLE_INST BIT(7)
#define AD3552R_MASK_SHORT_INSTRUCTION BIT(3)
#define AD3552R_REG_ADDR_DEVICE_CONFIG 0x02
#define AD3552R_MASK_DEVICE_STATUS(n) BIT(4 + (n))
#define AD3552R_MASK_CUSTOM_MODES GENMASK(3, 2)
#define AD3552R_MASK_OPERATING_MODES GENMASK(1, 0)
#define AD3552R_REG_ADDR_CHIP_TYPE 0x03
#define AD3552R_MASK_CLASS GENMASK(7, 0)
#define AD3552R_REG_ADDR_PRODUCT_ID_L 0x04
#define AD3552R_REG_ADDR_PRODUCT_ID_H 0x05
#define AD3552R_REG_ADDR_CHIP_GRADE 0x06
#define AD3552R_MASK_GRADE GENMASK(7, 4)
#define AD3552R_MASK_DEVICE_REVISION GENMASK(3, 0)
#define AD3552R_REG_ADDR_SCRATCH_PAD 0x0A
#define AD3552R_REG_ADDR_SPI_REVISION 0x0B
#define AD3552R_REG_ADDR_VENDOR_L 0x0C
#define AD3552R_REG_ADDR_VENDOR_H 0x0D
#define AD3552R_REG_ADDR_STREAM_MODE 0x0E
#define AD3552R_MASK_LENGTH GENMASK(7, 0)
#define AD3552R_REG_ADDR_TRANSFER_REGISTER 0x0F
#define AD3552R_MASK_MULTI_IO_MODE GENMASK(7, 6)
#define AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE BIT(2)
#define AD3552R_REG_ADDR_INTERFACE_CONFIG_C 0x10
#define AD3552R_MASK_CRC_ENABLE (GENMASK(7, 6) |\
GENMASK(1, 0))
#define AD3552R_MASK_STRICT_REGISTER_ACCESS BIT(5)
#define AD3552R_REG_ADDR_INTERFACE_STATUS_A 0x11
#define AD3552R_MASK_INTERFACE_NOT_READY BIT(7)
#define AD3552R_MASK_CLOCK_COUNTING_ERROR BIT(5)
#define AD3552R_MASK_INVALID_OR_NO_CRC BIT(3)
#define AD3552R_MASK_WRITE_TO_READ_ONLY_REGISTER BIT(2)
#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS BIT(1)
#define AD3552R_MASK_REGISTER_ADDRESS_INVALID BIT(0)
#define AD3552R_REG_ADDR_INTERFACE_CONFIG_D 0x14
#define AD3552R_MASK_ALERT_ENABLE_PULLUP BIT(6)
#define AD3552R_MASK_MEM_CRC_EN BIT(4)
#define AD3552R_MASK_SDO_DRIVE_STRENGTH GENMASK(3, 2)
#define AD3552R_MASK_DUAL_SPI_SYNCHROUNOUS_EN BIT(1)
#define AD3552R_MASK_SPI_CONFIG_DDR BIT(0)
#define AD3552R_REG_ADDR_SH_REFERENCE_CONFIG 0x15
#define AD3552R_MASK_IDUMP_FAST_MODE BIT(6)
#define AD3552R_MASK_SAMPLE_HOLD_DIFFERENTIAL_USER_EN BIT(5)
#define AD3552R_MASK_SAMPLE_HOLD_USER_TRIM GENMASK(4, 3)
#define AD3552R_MASK_SAMPLE_HOLD_USER_ENABLE BIT(2)
#define AD3552R_MASK_REFERENCE_VOLTAGE_SEL GENMASK(1, 0)
#define AD3552R_REG_ADDR_ERR_ALARM_MASK 0x16
#define AD3552R_MASK_REF_RANGE_ALARM BIT(6)
#define AD3552R_MASK_CLOCK_COUNT_ERR_ALARM BIT(5)
#define AD3552R_MASK_MEM_CRC_ERR_ALARM BIT(4)
#define AD3552R_MASK_SPI_CRC_ERR_ALARM BIT(3)
#define AD3552R_MASK_WRITE_TO_READ_ONLY_ALARM BIT(2)
#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS_ALARM BIT(1)
#define AD3552R_MASK_REGISTER_ADDRESS_INVALID_ALARM BIT(0)
#define AD3552R_REG_ADDR_ERR_STATUS 0x17
#define AD3552R_MASK_REF_RANGE_ERR_STATUS BIT(6)
#define AD3552R_MASK_DUAL_SPI_STREAM_EXCEEDS_DAC_ERR_STATUS BIT(5)
#define AD3552R_MASK_MEM_CRC_ERR_STATUS BIT(4)
#define AD3552R_MASK_RESET_STATUS BIT(0)
#define AD3552R_REG_ADDR_POWERDOWN_CONFIG 0x18
#define AD3552R_MASK_CH_DAC_POWERDOWN(ch) BIT(4 + (ch))
#define AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch) BIT(ch)
#define AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE 0x19
#define AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch) ((ch) ? GENMASK(7, 4) :\
GENMASK(3, 0))
#define AD3552R_REG_ADDR_CH_OFFSET(ch) (0x1B + (ch) * 2)
#define AD3552R_MASK_CH_OFFSET_BITS_0_7 GENMASK(7, 0)
#define AD3552R_REG_ADDR_CH_GAIN(ch) (0x1C + (ch) * 2)
#define AD3552R_MASK_CH_RANGE_OVERRIDE BIT(7)
#define AD3552R_MASK_CH_GAIN_SCALING_N GENMASK(6, 5)
#define AD3552R_MASK_CH_GAIN_SCALING_P GENMASK(4, 3)
#define AD3552R_MASK_CH_OFFSET_POLARITY BIT(2)
#define AD3552R_MASK_CH_OFFSET_BIT_8 BIT(0)
/*
* Secondary region
* For multibyte registers specify the highest address because the access is
* done in descending order
*/
#define AD3552R_SECONDARY_REGION_START 0x28
#define AD3552R_REG_ADDR_HW_LDAC_16B 0x28
#define AD3552R_REG_ADDR_CH_DAC_16B(ch) (0x2C - (1 - ch) * 2)
#define AD3552R_REG_ADDR_DAC_PAGE_MASK_16B 0x2E
#define AD3552R_REG_ADDR_CH_SELECT_16B 0x2F
#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_16B 0x31
#define AD3552R_REG_ADDR_SW_LDAC_16B 0x32
#define AD3552R_REG_ADDR_CH_INPUT_16B(ch) (0x36 - (1 - ch) * 2)
/* 3 bytes registers */
#define AD3552R_REG_START_24B 0x37
#define AD3552R_REG_ADDR_HW_LDAC_24B 0x37
#define AD3552R_REG_ADDR_CH_DAC_24B(ch) (0x3D - (1 - ch) * 3)
#define AD3552R_REG_ADDR_DAC_PAGE_MASK_24B 0x40
#define AD3552R_REG_ADDR_CH_SELECT_24B 0x41
#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B 0x44
#define AD3552R_REG_ADDR_SW_LDAC_24B 0x45
#define AD3552R_REG_ADDR_CH_INPUT_24B(ch) (0x4B - (1 - ch) * 3)
/* Useful defines */
#define AD3552R_NUM_CH 2
#define AD3552R_MASK_CH(ch) BIT(ch)
#define AD3552R_MASK_ALL_CH GENMASK(1, 0)
#define AD3552R_MAX_REG_SIZE 3
#define AD3552R_READ_BIT BIT(7)
#define AD3552R_ADDR_MASK GENMASK(6, 0)
#define AD3552R_MASK_DAC_12B 0xFFF0
#define AD3552R_DEFAULT_CONFIG_B_VALUE 0x8
#define AD3552R_SCRATCH_PAD_TEST_VAL1 0x34
#define AD3552R_SCRATCH_PAD_TEST_VAL2 0xB2
#define AD3552R_GAIN_SCALE 1000
#define AD3552R_LDAC_PULSE_US 100
enum ad3552r_ch_vref_select {
/* Internal source with Vref I/O floating */
AD3552R_INTERNAL_VREF_PIN_FLOATING,
/* Internal source with Vref I/O at 2.5V */
AD3552R_INTERNAL_VREF_PIN_2P5V,
/* External source with Vref I/O as input */
AD3552R_EXTERNAL_VREF_PIN_INPUT
};
enum ad3542r_id {
AD3542R_ID = 0x4008,
AD3552R_ID = 0x4009,
};
enum ad3552r_ch_output_range {
/* Range from 0 V to 2.5 V. Requires Rfb1x connection */
AD3552R_CH_OUTPUT_RANGE_0__2P5V,
/* Range from 0 V to 5 V. Requires Rfb1x connection */
AD3552R_CH_OUTPUT_RANGE_0__5V,
/* Range from 0 V to 10 V. Requires Rfb2x connection */
AD3552R_CH_OUTPUT_RANGE_0__10V,
/* Range from -5 V to 5 V. Requires Rfb2x connection */
AD3552R_CH_OUTPUT_RANGE_NEG_5__5V,
/* Range from -10 V to 10 V. Requires Rfb4x connection */
AD3552R_CH_OUTPUT_RANGE_NEG_10__10V,
};
static const s32 ad3552r_ch_ranges[][2] = {
[AD3552R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500},
[AD3552R_CH_OUTPUT_RANGE_0__5V] = {0, 5000},
[AD3552R_CH_OUTPUT_RANGE_0__10V] = {0, 10000},
[AD3552R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000},
[AD3552R_CH_OUTPUT_RANGE_NEG_10__10V] = {-10000, 10000}
};
enum ad3542r_ch_output_range {
/* Range from 0 V to 2.5 V. Requires Rfb1x connection */
AD3542R_CH_OUTPUT_RANGE_0__2P5V,
/* Range from 0 V to 3 V. Requires Rfb1x connection */
AD3542R_CH_OUTPUT_RANGE_0__3V,
/* Range from 0 V to 5 V. Requires Rfb1x connection */
AD3542R_CH_OUTPUT_RANGE_0__5V,
/* Range from 0 V to 10 V. Requires Rfb2x connection */
AD3542R_CH_OUTPUT_RANGE_0__10V,
/* Range from -2.5 V to 7.5 V. Requires Rfb2x connection */
AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V,
/* Range from -5 V to 5 V. Requires Rfb2x connection */
AD3542R_CH_OUTPUT_RANGE_NEG_5__5V,
};
static const s32 ad3542r_ch_ranges[][2] = {
[AD3542R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500},
[AD3542R_CH_OUTPUT_RANGE_0__3V] = {0, 3000},
[AD3542R_CH_OUTPUT_RANGE_0__5V] = {0, 5000},
[AD3542R_CH_OUTPUT_RANGE_0__10V] = {0, 10000},
[AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = {-2500, 7500},
[AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000}
};
enum ad3552r_ch_gain_scaling {
/* Gain scaling of 1 */
AD3552R_CH_GAIN_SCALING_1,
/* Gain scaling of 0.5 */
AD3552R_CH_GAIN_SCALING_0_5,
/* Gain scaling of 0.25 */
AD3552R_CH_GAIN_SCALING_0_25,
/* Gain scaling of 0.125 */
AD3552R_CH_GAIN_SCALING_0_125,
};
/* Gain * AD3552R_GAIN_SCALE */
static const s32 gains_scaling_table[] = {
[AD3552R_CH_GAIN_SCALING_1] = 1000,
[AD3552R_CH_GAIN_SCALING_0_5] = 500,
[AD3552R_CH_GAIN_SCALING_0_25] = 250,
[AD3552R_CH_GAIN_SCALING_0_125] = 125
};
enum ad3552r_dev_attributes {
/* - Direct register values */
/* From 0-3 */
AD3552R_SDO_DRIVE_STRENGTH,
/*
* 0 -> Internal Vref, vref_io pin floating (default)
* 1 -> Internal Vref, vref_io driven by internal vref
* 2 or 3 -> External Vref
*/
AD3552R_VREF_SELECT,
/* Read registers in ascending order if set. Else descending */
AD3552R_ADDR_ASCENSION,
};
enum ad3552r_ch_attributes {
/* DAC powerdown */
AD3552R_CH_DAC_POWERDOWN,
/* DAC amplifier powerdown */
AD3552R_CH_AMPLIFIER_POWERDOWN,
/* Select the output range. Select from enum ad3552r_ch_output_range */
AD3552R_CH_OUTPUT_RANGE_SEL,
/*
* Over-rider the range selector in order to manually set the output
* voltage range
*/
AD3552R_CH_RANGE_OVERRIDE,
/* Manually set the offset voltage */
AD3552R_CH_GAIN_OFFSET,
/* Sets the polarity of the offset. */
AD3552R_CH_GAIN_OFFSET_POLARITY,
/* PDAC gain scaling */
AD3552R_CH_GAIN_SCALING_P,
/* NDAC gain scaling */
AD3552R_CH_GAIN_SCALING_N,
/* Rfb value */
AD3552R_CH_RFB,
/* Channel select. When set allow Input -> DAC and Mask -> DAC */
AD3552R_CH_SELECT,
};
struct ad3552r_ch_data {
s32 scale_int;
s32 scale_dec;
s32 offset_int;
s32 offset_dec;
s16 gain_offset;
u16 rfb;
u8 n;
u8 p;
u8 range;
bool range_override;
};
struct ad3552r_desc {
/* 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_NUM_CH];
struct iio_chan_spec channels[AD3552R_NUM_CH + 1];
unsigned long enabled_ch;
unsigned int num_ch;
enum ad3542r_id chip_id;
};
static const u16 addr_mask_map[][2] = {
[AD3552R_ADDR_ASCENSION] = {
AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
AD3552R_MASK_ADDR_ASCENSION
},
[AD3552R_SDO_DRIVE_STRENGTH] = {
AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
AD3552R_MASK_SDO_DRIVE_STRENGTH
},
[AD3552R_VREF_SELECT] = {
AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
AD3552R_MASK_REFERENCE_VOLTAGE_SEL
},
};
/* 0 -> reg addr, 1->ch0 mask, 2->ch1 mask */
static const u16 addr_mask_map_ch[][3] = {
[AD3552R_CH_DAC_POWERDOWN] = {
AD3552R_REG_ADDR_POWERDOWN_CONFIG,
AD3552R_MASK_CH_DAC_POWERDOWN(0),
AD3552R_MASK_CH_DAC_POWERDOWN(1)
},
[AD3552R_CH_AMPLIFIER_POWERDOWN] = {
AD3552R_REG_ADDR_POWERDOWN_CONFIG,
AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0),
AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1)
},
[AD3552R_CH_OUTPUT_RANGE_SEL] = {
AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE,
AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0),
AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1)
},
[AD3552R_CH_SELECT] = {
AD3552R_REG_ADDR_CH_SELECT_16B,
AD3552R_MASK_CH(0),
AD3552R_MASK_CH(1)
}
};
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;
}
static u16 ad3552r_field_prep(u16 val, u16 mask)
{
return (val << __ffs(mask)) & mask;
}
/* 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, &reg);
if (ret < 0)
return ret;
reg &= ~mask;
reg |= ad3552r_field_prep(val, mask);
return ad3552r_write_reg(dac, addr, reg);
}
static int ad3552r_set_ch_value(struct ad3552r_desc *dac,
enum ad3552r_ch_attributes attr,
u8 ch,
u16 val)
{
/* Update register related to attributes in chip */
return ad3552r_update_reg_field(dac, addr_mask_map_ch[attr][0],
addr_mask_map_ch[attr][ch + 1], val);
}
#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:
err = ad3552r_set_ch_value(dac, AD3552R_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_NUM_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_NUM_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;
u16 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,
addr_mask_map[AD3552R_ADDR_ASCENSION][0],
addr_mask_map[AD3552R_ADDR_ASCENSION][1],
val);
}
static void ad3552r_get_custom_range(struct ad3552r_desc *dac, s32 i, s32 *v_min,
s32 *v_max)
{
s64 vref, tmp, common, offset, gn, gp;
/*
* From datasheet formula (In Volts):
* Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03]
* Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03]
* Calculus are converted to milivolts
*/
vref = 2500;
/* 2.5 * 1.03 * 1000 (To mV) */
common = 2575 * dac->ch_data[i].rfb;
offset = dac->ch_data[i].gain_offset;
gn = gains_scaling_table[dac->ch_data[i].n];
tmp = (1024 * gn + AD3552R_GAIN_SCALE * offset) * common;
tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE);
*v_max = vref + tmp;
gp = gains_scaling_table[dac->ch_data[i].p];
tmp = (1024 * gp - AD3552R_GAIN_SCALE * offset) * common;
tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE);
*v_min = vref - tmp;
}
static void ad3552r_calc_gain_and_offset(struct ad3552r_desc *dac, s32 ch)
{
s32 idx, v_max, v_min, span, rem;
s64 tmp;
if (dac->ch_data[ch].range_override) {
ad3552r_get_custom_range(dac, ch, &v_min, &v_max);
} else {
/* Normal range */
idx = dac->ch_data[ch].range;
if (dac->chip_id == AD3542R_ID) {
v_min = ad3542r_ch_ranges[idx][0];
v_max = ad3542r_ch_ranges[idx][1];
} else {
v_min = ad3552r_ch_ranges[idx][0];
v_max = ad3552r_ch_ranges[idx][1];
}
}
/*
* From datasheet formula:
* Vout = Span * (D / 65536) + Vmin
* Converted to scale and offset:
* Scale = Span / 65536
* Offset = 65536 * Vmin / Span
*
* Reminders are in micros in order to be printed as
* IIO_VAL_INT_PLUS_MICRO
*/
span = v_max - v_min;
dac->ch_data[ch].scale_int = div_s64_rem(span, 65536, &rem);
/* Do operations in microvolts */
dac->ch_data[ch].scale_dec = DIV_ROUND_CLOSEST((s64)rem * 1000000,
65536);
dac->ch_data[ch].offset_int = div_s64_rem(v_min * 65536, span, &rem);
tmp = (s64)rem * 1000000;
dac->ch_data[ch].offset_dec = div_s64(tmp, span);
}
static int ad3552r_find_range(u16 id, s32 *vals)
{
int i, len;
const s32 (*ranges)[2];
if (id == AD3542R_ID) {
len = ARRAY_SIZE(ad3542r_ch_ranges);
ranges = ad3542r_ch_ranges;
} else {
len = ARRAY_SIZE(ad3552r_ch_ranges);
ranges = ad3552r_ch_ranges;
}
for (i = 0; i < len; i++)
if (vals[0] == ranges[i][0] * 1000 &&
vals[1] == ranges[i][1] * 1000)
return i;
return -EINVAL;
}
static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
struct fwnode_handle *child,
u32 ch)
{
struct device *dev = &dac->spi->dev;
struct fwnode_handle *gain_child;
u32 val;
int err;
u8 addr;
u16 reg = 0, offset;
gain_child = fwnode_get_named_child_node(child,
"custom-output-range-config");
if (IS_ERR(gain_child)) {
dev_err(dev,
"mandatory custom-output-range-config property missing\n");
return PTR_ERR(gain_child);
}
dac->ch_data[ch].range_override = 1;
reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE);
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
if (err) {
dev_err(dev, "mandatory adi,gain-scaling-p property missing\n");
goto put_child;
}
reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P);
dac->ch_data[ch].p = val;
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
if (err) {
dev_err(dev, "mandatory adi,gain-scaling-n property missing\n");
goto put_child;
}
reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N);
dac->ch_data[ch].n = val;
err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
if (err) {
dev_err(dev, "mandatory adi,rfb-ohms property missing\n");
goto put_child;
}
dac->ch_data[ch].rfb = val;
err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val);
if (err) {
dev_err(dev, "mandatory adi,gain-offset property missing\n");
goto put_child;
}
dac->ch_data[ch].gain_offset = val;
offset = abs((s32)val);
reg |= ad3552r_field_prep((offset >> 8), AD3552R_MASK_CH_OFFSET_BIT_8);
reg |= ad3552r_field_prep((s32)val < 0, AD3552R_MASK_CH_OFFSET_POLARITY);
addr = AD3552R_REG_ADDR_CH_GAIN(ch);
err = ad3552r_write_reg(dac, addr,
offset & AD3552R_MASK_CH_OFFSET_BITS_0_7);
if (err) {
dev_err(dev, "Error writing register\n");
goto put_child;
}
err = ad3552r_write_reg(dac, addr, reg);
if (err) {
dev_err(dev, "Error writing register\n");
goto put_child;
}
put_child:
fwnode_handle_put(gain_child);
return err;
}
static void ad3552r_reg_disable(void *reg)
{
regulator_disable(reg);
}
static int ad3552r_configure_device(struct ad3552r_desc *dac)
{
struct device *dev = &dac->spi->dev;
struct fwnode_handle *child;
struct regulator *vref;
int err, cnt = 0, voltage, delta = 100000;
u32 vals[2], 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");
vref = devm_regulator_get_optional(dev, "vref");
if (IS_ERR(vref)) {
if (PTR_ERR(vref) != -ENODEV)
return dev_err_probe(dev, PTR_ERR(vref),
"Error getting vref");
if (device_property_read_bool(dev, "adi,vref-out-en"))
val = AD3552R_INTERNAL_VREF_PIN_2P5V;
else
val = AD3552R_INTERNAL_VREF_PIN_FLOATING;
} else {
err = regulator_enable(vref);
if (err) {
dev_err(dev, "Failed to enable external vref supply\n");
return err;
}
err = devm_add_action_or_reset(dev, ad3552r_reg_disable, vref);
if (err) {
regulator_disable(vref);
return err;
}
voltage = regulator_get_voltage(vref);
if (voltage > 2500000 + delta || voltage < 2500000 - delta) {
dev_warn(dev, "vref-supply must be 2.5V");
return -EINVAL;
}
val = AD3552R_EXTERNAL_VREF_PIN_INPUT;
}
err = ad3552r_update_reg_field(dac,
addr_mask_map[AD3552R_VREF_SELECT][0],
addr_mask_map[AD3552R_VREF_SELECT][1],
val);
if (err)
return err;
err = device_property_read_u32(dev, "adi,sdo-drive-strength", &val);
if (!err) {
if (val > 3) {
dev_err(dev, "adi,sdo-drive-strength must be less than 4\n");
return -EINVAL;
}
err = ad3552r_update_reg_field(dac,
addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][0],
addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][1],
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(dev, child) {
err = fwnode_property_read_u32(child, "reg", &ch);
if (err) {
dev_err(dev, "mandatory reg property missing\n");
goto put_child;
}
if (ch >= AD3552R_NUM_CH) {
dev_err(dev, "reg must be less than %d\n",
AD3552R_NUM_CH);
err = -EINVAL;
goto put_child;
}
if (fwnode_property_present(child, "adi,output-range-microvolt")) {
err = fwnode_property_read_u32_array(child,
"adi,output-range-microvolt",
vals,
2);
if (err) {
dev_err(dev,
"adi,output-range-microvolt property could not be parsed\n");
goto put_child;
}
err = ad3552r_find_range(dac->chip_id, vals);
if (err < 0) {
dev_err(dev,
"Invalid adi,output-range-microvolt value\n");
goto put_child;
}
val = err;
err = ad3552r_set_ch_value(dac,
AD3552R_CH_OUTPUT_RANGE_SEL,
ch, val);
if (err)
goto put_child;
dac->ch_data[ch].range = val;
} else if (dac->chip_id == AD3542R_ID) {
dev_err(dev,
"adi,output-range-microvolt is required for ad3542r\n");
err = -EINVAL;
goto put_child;
} else {
err = ad3552r_configure_custom_gain(dac, child, ch);
if (err)
goto put_child;
}
ad3552r_calc_gain_and_offset(dac, ch);
dac->enabled_ch |= BIT(ch);
err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1);
if (err < 0)
goto put_child;
dac->channels[cnt] = AD3552R_CH_DAC(ch);
++cnt;
}
/* Disable unused channels */
for_each_clear_bit(ch, &dac->enabled_ch, AD3552R_NUM_CH) {
err = ad3552r_set_ch_value(dac, AD3552R_CH_AMPLIFIER_POWERDOWN,
ch, 1);
if (err)
return err;
}
dac->num_ch = cnt;
return 0;
put_child:
fwnode_handle_put(child);
return err;
}
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->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)
{
const struct spi_device_id *id = spi_get_device_id(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->chip_id = id->driver_data;
mutex_init(&dac->lock);
err = ad3552r_init(dac);
if (err)
return err;
/* Config triggered buffer device */
if (dac->chip_id == AD3552R_ID)
indio_dev->name = "ad3552r";
else
indio_dev->name = "ad3542r";
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 spi_device_id ad3552r_id[] = {
{ "ad3542r", AD3542R_ID },
{ "ad3552r", AD3552R_ID },
{ }
};
MODULE_DEVICE_TABLE(spi, ad3552r_id);
static const struct of_device_id ad3552r_of_match[] = {
{ .compatible = "adi,ad3542r"},
{ .compatible = "adi,ad3552r"},
{ }
};
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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment