162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AD5770R Digital to analog converters driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2018 Analog Devices Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bits.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1262306a36Sopenharmony_ci#include <linux/iio/iio.h> 1362306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/property.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1962306a36Sopenharmony_ci#include <linux/spi/spi.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define ADI_SPI_IF_CONFIG_A 0x00 2262306a36Sopenharmony_ci#define ADI_SPI_IF_CONFIG_B 0x01 2362306a36Sopenharmony_ci#define ADI_SPI_IF_DEVICE_CONFIG 0x02 2462306a36Sopenharmony_ci#define ADI_SPI_IF_CHIP_TYPE 0x03 2562306a36Sopenharmony_ci#define ADI_SPI_IF_PRODUCT_ID_L 0x04 2662306a36Sopenharmony_ci#define ADI_SPI_IF_PRODUCT_ID_H 0x05 2762306a36Sopenharmony_ci#define ADI_SPI_IF_CHIP_GRADE 0x06 2862306a36Sopenharmony_ci#define ADI_SPI_IF_SCRACTH_PAD 0x0A 2962306a36Sopenharmony_ci#define ADI_SPI_IF_SPI_REVISION 0x0B 3062306a36Sopenharmony_ci#define ADI_SPI_IF_SPI_VENDOR_L 0x0C 3162306a36Sopenharmony_ci#define ADI_SPI_IF_SPI_VENDOR_H 0x0D 3262306a36Sopenharmony_ci#define ADI_SPI_IF_SPI_STREAM_MODE 0x0E 3362306a36Sopenharmony_ci#define ADI_SPI_IF_CONFIG_C 0x10 3462306a36Sopenharmony_ci#define ADI_SPI_IF_STATUS_A 0x11 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* ADI_SPI_IF_CONFIG_A */ 3762306a36Sopenharmony_ci#define ADI_SPI_IF_SW_RESET_MSK (BIT(0) | BIT(7)) 3862306a36Sopenharmony_ci#define ADI_SPI_IF_SW_RESET_SEL(x) ((x) & ADI_SPI_IF_SW_RESET_MSK) 3962306a36Sopenharmony_ci#define ADI_SPI_IF_ADDR_ASC_MSK (BIT(2) | BIT(5)) 4062306a36Sopenharmony_ci#define ADI_SPI_IF_ADDR_ASC_SEL(x) (((x) << 2) & ADI_SPI_IF_ADDR_ASC_MSK) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* ADI_SPI_IF_CONFIG_B */ 4362306a36Sopenharmony_ci#define ADI_SPI_IF_SINGLE_INS_MSK BIT(7) 4462306a36Sopenharmony_ci#define ADI_SPI_IF_SINGLE_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x) 4562306a36Sopenharmony_ci#define ADI_SPI_IF_SHORT_INS_MSK BIT(7) 4662306a36Sopenharmony_ci#define ADI_SPI_IF_SHORT_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* ADI_SPI_IF_CONFIG_C */ 4962306a36Sopenharmony_ci#define ADI_SPI_IF_STRICT_REG_MSK BIT(5) 5062306a36Sopenharmony_ci#define ADI_SPI_IF_STRICT_REG_GET(x) FIELD_GET(ADI_SPI_IF_STRICT_REG_MSK, x) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* AD5770R configuration registers */ 5362306a36Sopenharmony_ci#define AD5770R_CHANNEL_CONFIG 0x14 5462306a36Sopenharmony_ci#define AD5770R_OUTPUT_RANGE(ch) (0x15 + (ch)) 5562306a36Sopenharmony_ci#define AD5770R_FILTER_RESISTOR(ch) (0x1D + (ch)) 5662306a36Sopenharmony_ci#define AD5770R_REFERENCE 0x1B 5762306a36Sopenharmony_ci#define AD5770R_DAC_LSB(ch) (0x26 + 2 * (ch)) 5862306a36Sopenharmony_ci#define AD5770R_DAC_MSB(ch) (0x27 + 2 * (ch)) 5962306a36Sopenharmony_ci#define AD5770R_CH_SELECT 0x34 6062306a36Sopenharmony_ci#define AD5770R_CH_ENABLE 0x44 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* AD5770R_CHANNEL_CONFIG */ 6362306a36Sopenharmony_ci#define AD5770R_CFG_CH0_SINK_EN(x) (((x) & 0x1) << 7) 6462306a36Sopenharmony_ci#define AD5770R_CFG_SHUTDOWN_B(x, ch) (((x) & 0x1) << (ch)) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* AD5770R_OUTPUT_RANGE */ 6762306a36Sopenharmony_ci#define AD5770R_RANGE_OUTPUT_SCALING(x) (((x) & GENMASK(5, 0)) << 2) 6862306a36Sopenharmony_ci#define AD5770R_RANGE_MODE(x) ((x) & GENMASK(1, 0)) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* AD5770R_REFERENCE */ 7162306a36Sopenharmony_ci#define AD5770R_REF_RESISTOR_SEL(x) (((x) & 0x1) << 2) 7262306a36Sopenharmony_ci#define AD5770R_REF_SEL(x) ((x) & GENMASK(1, 0)) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* AD5770R_CH_ENABLE */ 7562306a36Sopenharmony_ci#define AD5770R_CH_SET(x, ch) (((x) & 0x1) << (ch)) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define AD5770R_MAX_CHANNELS 6 7862306a36Sopenharmony_ci#define AD5770R_MAX_CH_MODES 14 7962306a36Sopenharmony_ci#define AD5770R_LOW_VREF_mV 1250 8062306a36Sopenharmony_ci#define AD5770R_HIGH_VREF_mV 2500 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cienum ad5770r_ch0_modes { 8362306a36Sopenharmony_ci AD5770R_CH0_0_300 = 0, 8462306a36Sopenharmony_ci AD5770R_CH0_NEG_60_0, 8562306a36Sopenharmony_ci AD5770R_CH0_NEG_60_300 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cienum ad5770r_ch1_modes { 8962306a36Sopenharmony_ci AD5770R_CH1_0_140_LOW_HEAD = 1, 9062306a36Sopenharmony_ci AD5770R_CH1_0_140_LOW_NOISE, 9162306a36Sopenharmony_ci AD5770R_CH1_0_250 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cienum ad5770r_ch2_5_modes { 9562306a36Sopenharmony_ci AD5770R_CH_LOW_RANGE = 0, 9662306a36Sopenharmony_ci AD5770R_CH_HIGH_RANGE 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cienum ad5770r_ref_v { 10062306a36Sopenharmony_ci AD5770R_EXT_2_5_V = 0, 10162306a36Sopenharmony_ci AD5770R_INT_1_25_V_OUT_ON, 10262306a36Sopenharmony_ci AD5770R_EXT_1_25_V, 10362306a36Sopenharmony_ci AD5770R_INT_1_25_V_OUT_OFF 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cienum ad5770r_output_filter_resistor { 10762306a36Sopenharmony_ci AD5770R_FILTER_60_OHM = 0x0, 10862306a36Sopenharmony_ci AD5770R_FILTER_5_6_KOHM = 0x5, 10962306a36Sopenharmony_ci AD5770R_FILTER_11_2_KOHM, 11062306a36Sopenharmony_ci AD5770R_FILTER_22_2_KOHM, 11162306a36Sopenharmony_ci AD5770R_FILTER_44_4_KOHM, 11262306a36Sopenharmony_ci AD5770R_FILTER_104_KOHM, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistruct ad5770r_out_range { 11662306a36Sopenharmony_ci u8 out_scale; 11762306a36Sopenharmony_ci u8 out_range_mode; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * struct ad5770r_state - driver instance specific data 12262306a36Sopenharmony_ci * @spi: spi_device 12362306a36Sopenharmony_ci * @regmap: regmap 12462306a36Sopenharmony_ci * @vref_reg: fixed regulator for reference configuration 12562306a36Sopenharmony_ci * @gpio_reset: gpio descriptor 12662306a36Sopenharmony_ci * @output_mode: array contains channels output ranges 12762306a36Sopenharmony_ci * @vref: reference value 12862306a36Sopenharmony_ci * @ch_pwr_down: powerdown flags 12962306a36Sopenharmony_ci * @internal_ref: internal reference flag 13062306a36Sopenharmony_ci * @external_res: external 2.5k resistor flag 13162306a36Sopenharmony_ci * @transf_buf: cache aligned buffer for spi read/write 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistruct ad5770r_state { 13462306a36Sopenharmony_ci struct spi_device *spi; 13562306a36Sopenharmony_ci struct regmap *regmap; 13662306a36Sopenharmony_ci struct regulator *vref_reg; 13762306a36Sopenharmony_ci struct gpio_desc *gpio_reset; 13862306a36Sopenharmony_ci struct ad5770r_out_range output_mode[AD5770R_MAX_CHANNELS]; 13962306a36Sopenharmony_ci int vref; 14062306a36Sopenharmony_ci bool ch_pwr_down[AD5770R_MAX_CHANNELS]; 14162306a36Sopenharmony_ci bool internal_ref; 14262306a36Sopenharmony_ci bool external_res; 14362306a36Sopenharmony_ci u8 transf_buf[2] __aligned(IIO_DMA_MINALIGN); 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic const struct regmap_config ad5770r_spi_regmap_config = { 14762306a36Sopenharmony_ci .reg_bits = 8, 14862306a36Sopenharmony_ci .val_bits = 8, 14962306a36Sopenharmony_ci .read_flag_mask = BIT(7), 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistruct ad5770r_output_modes { 15362306a36Sopenharmony_ci unsigned int ch; 15462306a36Sopenharmony_ci u8 mode; 15562306a36Sopenharmony_ci int min; 15662306a36Sopenharmony_ci int max; 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic struct ad5770r_output_modes ad5770r_rng_tbl[] = { 16062306a36Sopenharmony_ci { 0, AD5770R_CH0_0_300, 0, 300 }, 16162306a36Sopenharmony_ci { 0, AD5770R_CH0_NEG_60_0, -60, 0 }, 16262306a36Sopenharmony_ci { 0, AD5770R_CH0_NEG_60_300, -60, 300 }, 16362306a36Sopenharmony_ci { 1, AD5770R_CH1_0_140_LOW_HEAD, 0, 140 }, 16462306a36Sopenharmony_ci { 1, AD5770R_CH1_0_140_LOW_NOISE, 0, 140 }, 16562306a36Sopenharmony_ci { 1, AD5770R_CH1_0_250, 0, 250 }, 16662306a36Sopenharmony_ci { 2, AD5770R_CH_LOW_RANGE, 0, 55 }, 16762306a36Sopenharmony_ci { 2, AD5770R_CH_HIGH_RANGE, 0, 150 }, 16862306a36Sopenharmony_ci { 3, AD5770R_CH_LOW_RANGE, 0, 45 }, 16962306a36Sopenharmony_ci { 3, AD5770R_CH_HIGH_RANGE, 0, 100 }, 17062306a36Sopenharmony_ci { 4, AD5770R_CH_LOW_RANGE, 0, 45 }, 17162306a36Sopenharmony_ci { 4, AD5770R_CH_HIGH_RANGE, 0, 100 }, 17262306a36Sopenharmony_ci { 5, AD5770R_CH_LOW_RANGE, 0, 45 }, 17362306a36Sopenharmony_ci { 5, AD5770R_CH_HIGH_RANGE, 0, 100 }, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const unsigned int ad5770r_filter_freqs[] = { 17762306a36Sopenharmony_ci 153, 357, 715, 1400, 2800, 262000, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic const unsigned int ad5770r_filter_reg_vals[] = { 18162306a36Sopenharmony_ci AD5770R_FILTER_104_KOHM, 18262306a36Sopenharmony_ci AD5770R_FILTER_44_4_KOHM, 18362306a36Sopenharmony_ci AD5770R_FILTER_22_2_KOHM, 18462306a36Sopenharmony_ci AD5770R_FILTER_11_2_KOHM, 18562306a36Sopenharmony_ci AD5770R_FILTER_5_6_KOHM, 18662306a36Sopenharmony_ci AD5770R_FILTER_60_OHM 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int ad5770r_set_output_mode(struct ad5770r_state *st, 19062306a36Sopenharmony_ci const struct ad5770r_out_range *out_mode, 19162306a36Sopenharmony_ci int channel) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci unsigned int regval; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci regval = AD5770R_RANGE_OUTPUT_SCALING(out_mode->out_scale) | 19662306a36Sopenharmony_ci AD5770R_RANGE_MODE(out_mode->out_range_mode); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return regmap_write(st->regmap, 19962306a36Sopenharmony_ci AD5770R_OUTPUT_RANGE(channel), regval); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int ad5770r_set_reference(struct ad5770r_state *st) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci unsigned int regval; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci regval = AD5770R_REF_RESISTOR_SEL(st->external_res); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (st->internal_ref) { 20962306a36Sopenharmony_ci regval |= AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF); 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci switch (st->vref) { 21262306a36Sopenharmony_ci case AD5770R_LOW_VREF_mV: 21362306a36Sopenharmony_ci regval |= AD5770R_REF_SEL(AD5770R_EXT_1_25_V); 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci case AD5770R_HIGH_VREF_mV: 21662306a36Sopenharmony_ci regval |= AD5770R_REF_SEL(AD5770R_EXT_2_5_V); 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci default: 21962306a36Sopenharmony_ci regval = AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF); 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return regmap_write(st->regmap, AD5770R_REFERENCE, regval); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int ad5770r_soft_reset(struct ad5770r_state *st) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci return regmap_write(st->regmap, ADI_SPI_IF_CONFIG_A, 23062306a36Sopenharmony_ci ADI_SPI_IF_SW_RESET_SEL(1)); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int ad5770r_reset(struct ad5770r_state *st) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci /* Perform software reset if no GPIO provided */ 23662306a36Sopenharmony_ci if (!st->gpio_reset) 23762306a36Sopenharmony_ci return ad5770r_soft_reset(st); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci gpiod_set_value_cansleep(st->gpio_reset, 0); 24062306a36Sopenharmony_ci usleep_range(10, 20); 24162306a36Sopenharmony_ci gpiod_set_value_cansleep(st->gpio_reset, 1); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* data must not be written during reset timeframe */ 24462306a36Sopenharmony_ci usleep_range(100, 200); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int ad5770r_get_range(struct ad5770r_state *st, 25062306a36Sopenharmony_ci int ch, int *min, int *max) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci int i; 25362306a36Sopenharmony_ci u8 tbl_ch, tbl_mode, out_range; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci out_range = st->output_mode[ch].out_range_mode; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci for (i = 0; i < AD5770R_MAX_CH_MODES; i++) { 25862306a36Sopenharmony_ci tbl_ch = ad5770r_rng_tbl[i].ch; 25962306a36Sopenharmony_ci tbl_mode = ad5770r_rng_tbl[i].mode; 26062306a36Sopenharmony_ci if (tbl_ch == ch && tbl_mode == out_range) { 26162306a36Sopenharmony_ci *min = ad5770r_rng_tbl[i].min; 26262306a36Sopenharmony_ci *max = ad5770r_rng_tbl[i].max; 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int ad5770r_get_filter_freq(struct iio_dev *indio_dev, 27162306a36Sopenharmony_ci const struct iio_chan_spec *chan, int *freq) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct ad5770r_state *st = iio_priv(indio_dev); 27462306a36Sopenharmony_ci int ret; 27562306a36Sopenharmony_ci unsigned int regval, i; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = regmap_read(st->regmap, 27862306a36Sopenharmony_ci AD5770R_FILTER_RESISTOR(chan->channel), ®val); 27962306a36Sopenharmony_ci if (ret < 0) 28062306a36Sopenharmony_ci return ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ad5770r_filter_reg_vals); i++) 28362306a36Sopenharmony_ci if (regval == ad5770r_filter_reg_vals[i]) 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci if (i == ARRAY_SIZE(ad5770r_filter_reg_vals)) 28662306a36Sopenharmony_ci return -EINVAL; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci *freq = ad5770r_filter_freqs[i]; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return IIO_VAL_INT; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int ad5770r_set_filter_freq(struct iio_dev *indio_dev, 29462306a36Sopenharmony_ci const struct iio_chan_spec *chan, 29562306a36Sopenharmony_ci unsigned int freq) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct ad5770r_state *st = iio_priv(indio_dev); 29862306a36Sopenharmony_ci unsigned int regval, i; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ad5770r_filter_freqs); i++) 30162306a36Sopenharmony_ci if (ad5770r_filter_freqs[i] >= freq) 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci if (i == ARRAY_SIZE(ad5770r_filter_freqs)) 30462306a36Sopenharmony_ci return -EINVAL; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci regval = ad5770r_filter_reg_vals[i]; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return regmap_write(st->regmap, AD5770R_FILTER_RESISTOR(chan->channel), 30962306a36Sopenharmony_ci regval); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int ad5770r_read_raw(struct iio_dev *indio_dev, 31362306a36Sopenharmony_ci struct iio_chan_spec const *chan, 31462306a36Sopenharmony_ci int *val, int *val2, long info) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct ad5770r_state *st = iio_priv(indio_dev); 31762306a36Sopenharmony_ci int max, min, ret; 31862306a36Sopenharmony_ci u16 buf16; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci switch (info) { 32162306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 32262306a36Sopenharmony_ci ret = regmap_bulk_read(st->regmap, 32362306a36Sopenharmony_ci chan->address, 32462306a36Sopenharmony_ci st->transf_buf, 2); 32562306a36Sopenharmony_ci if (ret) 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci buf16 = st->transf_buf[0] + (st->transf_buf[1] << 8); 32962306a36Sopenharmony_ci *val = buf16 >> 2; 33062306a36Sopenharmony_ci return IIO_VAL_INT; 33162306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 33262306a36Sopenharmony_ci ret = ad5770r_get_range(st, chan->channel, &min, &max); 33362306a36Sopenharmony_ci if (ret < 0) 33462306a36Sopenharmony_ci return ret; 33562306a36Sopenharmony_ci *val = max - min; 33662306a36Sopenharmony_ci /* There is no sign bit. (negative current is mapped from 0) 33762306a36Sopenharmony_ci * (sourced/sinked) current = raw * scale + offset 33862306a36Sopenharmony_ci * where offset in case of CH0 can be negative. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci *val2 = 14; 34162306a36Sopenharmony_ci return IIO_VAL_FRACTIONAL_LOG2; 34262306a36Sopenharmony_ci case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 34362306a36Sopenharmony_ci return ad5770r_get_filter_freq(indio_dev, chan, val); 34462306a36Sopenharmony_ci case IIO_CHAN_INFO_OFFSET: 34562306a36Sopenharmony_ci ret = ad5770r_get_range(st, chan->channel, &min, &max); 34662306a36Sopenharmony_ci if (ret < 0) 34762306a36Sopenharmony_ci return ret; 34862306a36Sopenharmony_ci *val = min; 34962306a36Sopenharmony_ci return IIO_VAL_INT; 35062306a36Sopenharmony_ci default: 35162306a36Sopenharmony_ci return -EINVAL; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int ad5770r_write_raw(struct iio_dev *indio_dev, 35662306a36Sopenharmony_ci struct iio_chan_spec const *chan, 35762306a36Sopenharmony_ci int val, int val2, long info) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct ad5770r_state *st = iio_priv(indio_dev); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci switch (info) { 36262306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 36362306a36Sopenharmony_ci st->transf_buf[0] = ((u16)val >> 6); 36462306a36Sopenharmony_ci st->transf_buf[1] = (val & GENMASK(5, 0)) << 2; 36562306a36Sopenharmony_ci return regmap_bulk_write(st->regmap, chan->address, 36662306a36Sopenharmony_ci st->transf_buf, 2); 36762306a36Sopenharmony_ci case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 36862306a36Sopenharmony_ci return ad5770r_set_filter_freq(indio_dev, chan, val); 36962306a36Sopenharmony_ci default: 37062306a36Sopenharmony_ci return -EINVAL; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int ad5770r_read_freq_avail(struct iio_dev *indio_dev, 37562306a36Sopenharmony_ci struct iio_chan_spec const *chan, 37662306a36Sopenharmony_ci const int **vals, int *type, int *length, 37762306a36Sopenharmony_ci long mask) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci switch (mask) { 38062306a36Sopenharmony_ci case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 38162306a36Sopenharmony_ci *type = IIO_VAL_INT; 38262306a36Sopenharmony_ci *vals = ad5770r_filter_freqs; 38362306a36Sopenharmony_ci *length = ARRAY_SIZE(ad5770r_filter_freqs); 38462306a36Sopenharmony_ci return IIO_AVAIL_LIST; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return -EINVAL; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int ad5770r_reg_access(struct iio_dev *indio_dev, 39162306a36Sopenharmony_ci unsigned int reg, 39262306a36Sopenharmony_ci unsigned int writeval, 39362306a36Sopenharmony_ci unsigned int *readval) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct ad5770r_state *st = iio_priv(indio_dev); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (readval) 39862306a36Sopenharmony_ci return regmap_read(st->regmap, reg, readval); 39962306a36Sopenharmony_ci else 40062306a36Sopenharmony_ci return regmap_write(st->regmap, reg, writeval); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic const struct iio_info ad5770r_info = { 40462306a36Sopenharmony_ci .read_raw = ad5770r_read_raw, 40562306a36Sopenharmony_ci .write_raw = ad5770r_write_raw, 40662306a36Sopenharmony_ci .read_avail = ad5770r_read_freq_avail, 40762306a36Sopenharmony_ci .debugfs_reg_access = &ad5770r_reg_access, 40862306a36Sopenharmony_ci}; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int ad5770r_store_output_range(struct ad5770r_state *st, 41162306a36Sopenharmony_ci int min, int max, int index) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci int i; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci for (i = 0; i < AD5770R_MAX_CH_MODES; i++) { 41662306a36Sopenharmony_ci if (ad5770r_rng_tbl[i].ch != index) 41762306a36Sopenharmony_ci continue; 41862306a36Sopenharmony_ci if (ad5770r_rng_tbl[i].min != min || 41962306a36Sopenharmony_ci ad5770r_rng_tbl[i].max != max) 42062306a36Sopenharmony_ci continue; 42162306a36Sopenharmony_ci st->output_mode[index].out_range_mode = ad5770r_rng_tbl[i].mode; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return -EINVAL; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic ssize_t ad5770r_read_dac_powerdown(struct iio_dev *indio_dev, 43062306a36Sopenharmony_ci uintptr_t private, 43162306a36Sopenharmony_ci const struct iio_chan_spec *chan, 43262306a36Sopenharmony_ci char *buf) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct ad5770r_state *st = iio_priv(indio_dev); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", st->ch_pwr_down[chan->channel]); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic ssize_t ad5770r_write_dac_powerdown(struct iio_dev *indio_dev, 44062306a36Sopenharmony_ci uintptr_t private, 44162306a36Sopenharmony_ci const struct iio_chan_spec *chan, 44262306a36Sopenharmony_ci const char *buf, size_t len) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct ad5770r_state *st = iio_priv(indio_dev); 44562306a36Sopenharmony_ci unsigned int regval; 44662306a36Sopenharmony_ci unsigned int mask; 44762306a36Sopenharmony_ci bool readin; 44862306a36Sopenharmony_ci int ret; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci ret = kstrtobool(buf, &readin); 45162306a36Sopenharmony_ci if (ret) 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci readin = !readin; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci regval = AD5770R_CFG_SHUTDOWN_B(readin, chan->channel); 45762306a36Sopenharmony_ci if (chan->channel == 0 && 45862306a36Sopenharmony_ci st->output_mode[0].out_range_mode > AD5770R_CH0_0_300) { 45962306a36Sopenharmony_ci regval |= AD5770R_CFG_CH0_SINK_EN(readin); 46062306a36Sopenharmony_ci mask = BIT(chan->channel) + BIT(7); 46162306a36Sopenharmony_ci } else { 46262306a36Sopenharmony_ci mask = BIT(chan->channel); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci ret = regmap_update_bits(st->regmap, AD5770R_CHANNEL_CONFIG, mask, 46562306a36Sopenharmony_ci regval); 46662306a36Sopenharmony_ci if (ret) 46762306a36Sopenharmony_ci return ret; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci regval = AD5770R_CH_SET(readin, chan->channel); 47062306a36Sopenharmony_ci ret = regmap_update_bits(st->regmap, AD5770R_CH_ENABLE, 47162306a36Sopenharmony_ci BIT(chan->channel), regval); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci st->ch_pwr_down[chan->channel] = !readin; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return len; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic const struct iio_chan_spec_ext_info ad5770r_ext_info[] = { 48162306a36Sopenharmony_ci { 48262306a36Sopenharmony_ci .name = "powerdown", 48362306a36Sopenharmony_ci .read = ad5770r_read_dac_powerdown, 48462306a36Sopenharmony_ci .write = ad5770r_write_dac_powerdown, 48562306a36Sopenharmony_ci .shared = IIO_SEPARATE, 48662306a36Sopenharmony_ci }, 48762306a36Sopenharmony_ci { } 48862306a36Sopenharmony_ci}; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci#define AD5770R_IDAC_CHANNEL(index, reg) { \ 49162306a36Sopenharmony_ci .type = IIO_CURRENT, \ 49262306a36Sopenharmony_ci .address = reg, \ 49362306a36Sopenharmony_ci .indexed = 1, \ 49462306a36Sopenharmony_ci .channel = index, \ 49562306a36Sopenharmony_ci .output = 1, \ 49662306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 49762306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE) | \ 49862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_OFFSET) | \ 49962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ 50062306a36Sopenharmony_ci .info_mask_shared_by_type_available = \ 50162306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ 50262306a36Sopenharmony_ci .ext_info = ad5770r_ext_info, \ 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic const struct iio_chan_spec ad5770r_channels[] = { 50662306a36Sopenharmony_ci AD5770R_IDAC_CHANNEL(0, AD5770R_DAC_MSB(0)), 50762306a36Sopenharmony_ci AD5770R_IDAC_CHANNEL(1, AD5770R_DAC_MSB(1)), 50862306a36Sopenharmony_ci AD5770R_IDAC_CHANNEL(2, AD5770R_DAC_MSB(2)), 50962306a36Sopenharmony_ci AD5770R_IDAC_CHANNEL(3, AD5770R_DAC_MSB(3)), 51062306a36Sopenharmony_ci AD5770R_IDAC_CHANNEL(4, AD5770R_DAC_MSB(4)), 51162306a36Sopenharmony_ci AD5770R_IDAC_CHANNEL(5, AD5770R_DAC_MSB(5)), 51262306a36Sopenharmony_ci}; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int ad5770r_channel_config(struct ad5770r_state *st) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci int ret, tmp[2], min, max; 51762306a36Sopenharmony_ci unsigned int num; 51862306a36Sopenharmony_ci struct fwnode_handle *child; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci num = device_get_child_node_count(&st->spi->dev); 52162306a36Sopenharmony_ci if (num != AD5770R_MAX_CHANNELS) 52262306a36Sopenharmony_ci return -EINVAL; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci device_for_each_child_node(&st->spi->dev, child) { 52562306a36Sopenharmony_ci ret = fwnode_property_read_u32(child, "reg", &num); 52662306a36Sopenharmony_ci if (ret) 52762306a36Sopenharmony_ci goto err_child_out; 52862306a36Sopenharmony_ci if (num >= AD5770R_MAX_CHANNELS) { 52962306a36Sopenharmony_ci ret = -EINVAL; 53062306a36Sopenharmony_ci goto err_child_out; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci ret = fwnode_property_read_u32_array(child, 53462306a36Sopenharmony_ci "adi,range-microamp", 53562306a36Sopenharmony_ci tmp, 2); 53662306a36Sopenharmony_ci if (ret) 53762306a36Sopenharmony_ci goto err_child_out; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci min = tmp[0] / 1000; 54062306a36Sopenharmony_ci max = tmp[1] / 1000; 54162306a36Sopenharmony_ci ret = ad5770r_store_output_range(st, min, max, num); 54262306a36Sopenharmony_ci if (ret) 54362306a36Sopenharmony_ci goto err_child_out; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cierr_child_out: 54962306a36Sopenharmony_ci fwnode_handle_put(child); 55062306a36Sopenharmony_ci return ret; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int ad5770r_init(struct ad5770r_state *st) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci int ret, i; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", 55862306a36Sopenharmony_ci GPIOD_OUT_HIGH); 55962306a36Sopenharmony_ci if (IS_ERR(st->gpio_reset)) 56062306a36Sopenharmony_ci return PTR_ERR(st->gpio_reset); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* Perform a reset */ 56362306a36Sopenharmony_ci ret = ad5770r_reset(st); 56462306a36Sopenharmony_ci if (ret) 56562306a36Sopenharmony_ci return ret; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Set output range */ 56862306a36Sopenharmony_ci ret = ad5770r_channel_config(st); 56962306a36Sopenharmony_ci if (ret) 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci for (i = 0; i < AD5770R_MAX_CHANNELS; i++) { 57362306a36Sopenharmony_ci ret = ad5770r_set_output_mode(st, &st->output_mode[i], i); 57462306a36Sopenharmony_ci if (ret) 57562306a36Sopenharmony_ci return ret; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci st->external_res = fwnode_property_read_bool(st->spi->dev.fwnode, 57962306a36Sopenharmony_ci "adi,external-resistor"); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci ret = ad5770r_set_reference(st); 58262306a36Sopenharmony_ci if (ret) 58362306a36Sopenharmony_ci return ret; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Set outputs off */ 58662306a36Sopenharmony_ci ret = regmap_write(st->regmap, AD5770R_CHANNEL_CONFIG, 0x00); 58762306a36Sopenharmony_ci if (ret) 58862306a36Sopenharmony_ci return ret; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci ret = regmap_write(st->regmap, AD5770R_CH_ENABLE, 0x00); 59162306a36Sopenharmony_ci if (ret) 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci for (i = 0; i < AD5770R_MAX_CHANNELS; i++) 59562306a36Sopenharmony_ci st->ch_pwr_down[i] = true; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic void ad5770r_disable_regulator(void *data) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct ad5770r_state *st = data; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci regulator_disable(st->vref_reg); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int ad5770r_probe(struct spi_device *spi) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct ad5770r_state *st; 61062306a36Sopenharmony_ci struct iio_dev *indio_dev; 61162306a36Sopenharmony_ci struct regmap *regmap; 61262306a36Sopenharmony_ci int ret; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); 61562306a36Sopenharmony_ci if (!indio_dev) 61662306a36Sopenharmony_ci return -ENOMEM; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci st = iio_priv(indio_dev); 61962306a36Sopenharmony_ci spi_set_drvdata(spi, indio_dev); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci st->spi = spi; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci regmap = devm_regmap_init_spi(spi, &ad5770r_spi_regmap_config); 62462306a36Sopenharmony_ci if (IS_ERR(regmap)) { 62562306a36Sopenharmony_ci dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", 62662306a36Sopenharmony_ci PTR_ERR(regmap)); 62762306a36Sopenharmony_ci return PTR_ERR(regmap); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci st->regmap = regmap; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); 63262306a36Sopenharmony_ci if (!IS_ERR(st->vref_reg)) { 63362306a36Sopenharmony_ci ret = regulator_enable(st->vref_reg); 63462306a36Sopenharmony_ci if (ret) { 63562306a36Sopenharmony_ci dev_err(&spi->dev, 63662306a36Sopenharmony_ci "Failed to enable vref regulators: %d\n", ret); 63762306a36Sopenharmony_ci return ret; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci ret = devm_add_action_or_reset(&spi->dev, 64162306a36Sopenharmony_ci ad5770r_disable_regulator, 64262306a36Sopenharmony_ci st); 64362306a36Sopenharmony_ci if (ret < 0) 64462306a36Sopenharmony_ci return ret; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ret = regulator_get_voltage(st->vref_reg); 64762306a36Sopenharmony_ci if (ret < 0) 64862306a36Sopenharmony_ci return ret; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci st->vref = ret / 1000; 65162306a36Sopenharmony_ci } else { 65262306a36Sopenharmony_ci if (PTR_ERR(st->vref_reg) == -ENODEV) { 65362306a36Sopenharmony_ci st->vref = AD5770R_LOW_VREF_mV; 65462306a36Sopenharmony_ci st->internal_ref = true; 65562306a36Sopenharmony_ci } else { 65662306a36Sopenharmony_ci return PTR_ERR(st->vref_reg); 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci indio_dev->name = spi_get_device_id(spi)->name; 66162306a36Sopenharmony_ci indio_dev->info = &ad5770r_info; 66262306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 66362306a36Sopenharmony_ci indio_dev->channels = ad5770r_channels; 66462306a36Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(ad5770r_channels); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci ret = ad5770r_init(st); 66762306a36Sopenharmony_ci if (ret < 0) { 66862306a36Sopenharmony_ci dev_err(&spi->dev, "AD5770R init failed\n"); 66962306a36Sopenharmony_ci return ret; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return devm_iio_device_register(&st->spi->dev, indio_dev); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic const struct of_device_id ad5770r_of_id[] = { 67662306a36Sopenharmony_ci { .compatible = "adi,ad5770r", }, 67762306a36Sopenharmony_ci {}, 67862306a36Sopenharmony_ci}; 67962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ad5770r_of_id); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic const struct spi_device_id ad5770r_id[] = { 68262306a36Sopenharmony_ci { "ad5770r", 0 }, 68362306a36Sopenharmony_ci {}, 68462306a36Sopenharmony_ci}; 68562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ad5770r_id); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic struct spi_driver ad5770r_driver = { 68862306a36Sopenharmony_ci .driver = { 68962306a36Sopenharmony_ci .name = KBUILD_MODNAME, 69062306a36Sopenharmony_ci .of_match_table = ad5770r_of_id, 69162306a36Sopenharmony_ci }, 69262306a36Sopenharmony_ci .probe = ad5770r_probe, 69362306a36Sopenharmony_ci .id_table = ad5770r_id, 69462306a36Sopenharmony_ci}; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cimodule_spi_driver(ad5770r_driver); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ciMODULE_AUTHOR("Mircea Caprioru <mircea.caprioru@analog.com>"); 69962306a36Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices AD5770R IDAC"); 70062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 701