1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * AD5706R 16-bit Current Output Digital to Analog Converter 4 * 5 * Copyright 2026 Analog Devices Inc. 6 */ 7 8 #include <linux/array_size.h> 9 #include <linux/bits.h> 10 #include <linux/dev_printk.h> 11 #include <linux/err.h> 12 #include <linux/iio/iio.h> 13 #include <linux/module.h> 14 #include <linux/regmap.h> 15 #include <linux/spi/spi.h> 16 #include <linux/types.h> 17 #include <linux/unaligned.h> 18 19 /* SPI frame layout */ 20 #define AD5706R_RD_MASK BIT(15) 21 #define AD5706R_ADDR_MASK GENMASK(11, 0) 22 23 /* Registers */ 24 #define AD5706R_REG_DAC_INPUT_A_CH(x) (0x60 + ((x) * 2)) 25 #define AD5706R_REG_DAC_DATA_READBACK_CH(x) (0x68 + ((x) * 2)) 26 27 #define AD5706R_DAC_RESOLUTION 16 28 #define AD5706R_DAC_MAX_CODE GENMASK(15, 0) 29 #define AD5706R_MULTIBYTE_REG_START 0x14 30 #define AD5706R_MULTIBYTE_REG_END 0x71 31 #define AD5706R_MAX_REG 0x77 32 33 struct ad5706r_state { 34 struct spi_device *spi; 35 struct regmap *regmap; 36 37 u8 tx_buf[4] __aligned(IIO_DMA_MINALIGN); 38 u8 rx_buf[4]; 39 }; 40 41 static int ad5706r_reg_len(unsigned int reg) 42 { 43 if (reg >= AD5706R_MULTIBYTE_REG_START && reg <= AD5706R_MULTIBYTE_REG_END) 44 return 2; 45 46 return 1; 47 } 48 49 static int ad5706r_regmap_write(void *context, const void *data, size_t count) 50 { 51 struct ad5706r_state *st = context; 52 unsigned int num_bytes; 53 u16 reg, val; 54 55 if (count != 4) 56 return -EINVAL; 57 58 reg = get_unaligned_be16(data); 59 val = get_unaligned_be16(data + 2); 60 num_bytes = ad5706r_reg_len(reg); 61 62 struct spi_transfer xfer = { 63 .tx_buf = st->tx_buf, 64 .len = num_bytes + 2, 65 }; 66 67 put_unaligned_be16(reg, &st->tx_buf[0]); 68 69 if (num_bytes == 1) 70 st->tx_buf[2] = (u8)val; 71 else if (num_bytes == 2) 72 put_unaligned_be16(val, &st->tx_buf[2]); 73 else 74 return -EINVAL; 75 76 return spi_sync_transfer(st->spi, &xfer, 1); 77 } 78 79 static int ad5706r_regmap_read(void *context, const void *reg_buf, 80 size_t reg_size, void *val_buf, size_t val_size) 81 { 82 struct ad5706r_state *st = context; 83 unsigned int num_bytes; 84 u16 reg, cmd, val; 85 int ret; 86 87 if (reg_size != 2 || val_size != 2) 88 return -EINVAL; 89 90 reg = get_unaligned_be16(reg_buf); 91 num_bytes = ad5706r_reg_len(reg); 92 93 /* Full duplex, device responds immediately after command */ 94 struct spi_transfer xfer = { 95 .tx_buf = st->tx_buf, 96 .rx_buf = st->rx_buf, 97 .len = 2 + num_bytes, 98 }; 99 100 cmd = AD5706R_RD_MASK | (reg & AD5706R_ADDR_MASK); 101 put_unaligned_be16(cmd, &st->tx_buf[0]); 102 put_unaligned_be16(0, &st->tx_buf[2]); 103 104 ret = spi_sync_transfer(st->spi, &xfer, 1); 105 if (ret) 106 return ret; 107 108 /* Extract value from response (skip 2-byte command echo) */ 109 if (num_bytes == 1) 110 val = st->rx_buf[2]; 111 else if (num_bytes == 2) 112 val = get_unaligned_be16(&st->rx_buf[2]); 113 else 114 return -EINVAL; 115 116 put_unaligned_be16(val, val_buf); 117 118 return 0; 119 } 120 121 static int ad5706r_read_raw(struct iio_dev *indio_dev, 122 struct iio_chan_spec const *chan, 123 int *val, int *val2, long mask) 124 { 125 struct ad5706r_state *st = iio_priv(indio_dev); 126 unsigned int reg, reg_val; 127 int ret; 128 129 switch (mask) { 130 case IIO_CHAN_INFO_RAW: 131 reg = AD5706R_REG_DAC_DATA_READBACK_CH(chan->channel); 132 ret = regmap_read(st->regmap, reg, ®_val); 133 if (ret) 134 return ret; 135 136 *val = reg_val; 137 return IIO_VAL_INT; 138 case IIO_CHAN_INFO_SCALE: 139 *val = 50; 140 *val2 = AD5706R_DAC_RESOLUTION; 141 return IIO_VAL_FRACTIONAL_LOG2; 142 default: 143 return -EINVAL; 144 } 145 } 146 147 static int ad5706r_write_raw(struct iio_dev *indio_dev, 148 struct iio_chan_spec const *chan, 149 int val, int val2, long mask) 150 { 151 struct ad5706r_state *st = iio_priv(indio_dev); 152 unsigned int reg; 153 154 switch (mask) { 155 case IIO_CHAN_INFO_RAW: 156 if (val < 0 || val > AD5706R_DAC_MAX_CODE) 157 return -EINVAL; 158 159 reg = AD5706R_REG_DAC_INPUT_A_CH(chan->channel); 160 return regmap_write(st->regmap, reg, val); 161 default: 162 return -EINVAL; 163 } 164 } 165 166 static const struct regmap_bus ad5706r_regmap_bus = { 167 .write = ad5706r_regmap_write, 168 .read = ad5706r_regmap_read, 169 .reg_format_endian_default = REGMAP_ENDIAN_BIG, 170 .val_format_endian_default = REGMAP_ENDIAN_BIG, 171 }; 172 173 static const struct regmap_config ad5706r_regmap_config = { 174 .reg_bits = 16, 175 .val_bits = 16, 176 .max_register = AD5706R_MAX_REG, 177 }; 178 179 static const struct iio_info ad5706r_info = { 180 .read_raw = ad5706r_read_raw, 181 .write_raw = ad5706r_write_raw, 182 }; 183 184 #define AD5706R_CHAN(_channel) { \ 185 .type = IIO_CURRENT, \ 186 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 187 BIT(IIO_CHAN_INFO_SCALE), \ 188 .output = 1, \ 189 .indexed = 1, \ 190 .channel = _channel, \ 191 } 192 193 static const struct iio_chan_spec ad5706r_channels[] = { 194 AD5706R_CHAN(0), 195 AD5706R_CHAN(1), 196 AD5706R_CHAN(2), 197 AD5706R_CHAN(3), 198 }; 199 200 static int ad5706r_probe(struct spi_device *spi) 201 { 202 struct device *dev = &spi->dev; 203 struct iio_dev *indio_dev; 204 struct ad5706r_state *st; 205 206 indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); 207 if (!indio_dev) 208 return -ENOMEM; 209 210 st = iio_priv(indio_dev); 211 st->spi = spi; 212 213 st->regmap = devm_regmap_init(dev, &ad5706r_regmap_bus, 214 st, &ad5706r_regmap_config); 215 if (IS_ERR(st->regmap)) 216 return dev_err_probe(dev, PTR_ERR(st->regmap), 217 "Failed to init regmap\n"); 218 219 indio_dev->name = "ad5706r"; 220 indio_dev->info = &ad5706r_info; 221 indio_dev->modes = INDIO_DIRECT_MODE; 222 indio_dev->channels = ad5706r_channels; 223 indio_dev->num_channels = ARRAY_SIZE(ad5706r_channels); 224 225 return devm_iio_device_register(dev, indio_dev); 226 } 227 228 static const struct of_device_id ad5706r_of_match[] = { 229 { .compatible = "adi,ad5706r" }, 230 { } 231 }; 232 MODULE_DEVICE_TABLE(of, ad5706r_of_match); 233 234 static const struct spi_device_id ad5706r_id[] = { 235 { .name = "ad5706r" }, 236 { } 237 }; 238 MODULE_DEVICE_TABLE(spi, ad5706r_id); 239 240 static struct spi_driver ad5706r_driver = { 241 .driver = { 242 .name = "ad5706r", 243 .of_match_table = ad5706r_of_match, 244 }, 245 .probe = ad5706r_probe, 246 .id_table = ad5706r_id, 247 }; 248 module_spi_driver(ad5706r_driver); 249 250 MODULE_AUTHOR("Alexis Czezar Torreno <alexisczezar.torreno@analog.com>"); 251 MODULE_DESCRIPTION("AD5706R 16-bit Current Output DAC driver"); 252 MODULE_LICENSE("GPL"); 253