Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: dac: AD8801: add Analog Devices AD8801/AD8803 support

Add support for Analog Devices AD8801/AD8803, 8 channels 8bits, Digital to
Analog converters.

Signed-off-by: Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>

authored by

Gwenhael Goavec-Merou and committed by
Jonathan Cameron
7f270bc9 9d1894cd

+250
+10
drivers/iio/dac/Kconfig
··· 190 190 base port addresses for the devices may be configured via the base 191 191 array module parameter. 192 192 193 + config AD8801 194 + tristate "Analog Devices AD8801/AD8803 DAC driver" 195 + depends on SPI_MASTER 196 + help 197 + Say yes here to build support for Analog Devices AD8801, AD8803 Digital to 198 + Analog Converters (DAC). 199 + 200 + To compile this driver as a module choose M here: the module will be called 201 + ad8801. 202 + 193 203 config LPC18XX_DAC 194 204 tristate "NXP LPC18xx DAC driver" 195 205 depends on ARCH_LPC18XX || COMPILE_TEST
+1
drivers/iio/dac/Makefile
··· 20 20 obj-$(CONFIG_AD5791) += ad5791.o 21 21 obj-$(CONFIG_AD5686) += ad5686.o 22 22 obj-$(CONFIG_AD7303) += ad7303.o 23 + obj-$(CONFIG_AD8801) += ad8801.o 23 24 obj-$(CONFIG_CIO_DAC) += cio-dac.o 24 25 obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o 25 26 obj-$(CONFIG_M62332) += m62332.o
+239
drivers/iio/dac/ad8801.c
··· 1 + /* 2 + * IIO DAC driver for Analog Devices AD8801 DAC 3 + * 4 + * Copyright (C) 2016 Gwenhael Goavec-Merou 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License, version 2, as 7 + * published by the Free Software Foundation. 8 + * 9 + * This program is distributed in the hope that it will be useful, but 10 + * WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 + * General Public License for more details. 13 + * 14 + */ 15 + 16 + #include <linux/iio/iio.h> 17 + #include <linux/module.h> 18 + #include <linux/regulator/consumer.h> 19 + #include <linux/spi/spi.h> 20 + #include <linux/sysfs.h> 21 + 22 + #define AD8801_CFG_ADDR_OFFSET 8 23 + 24 + enum ad8801_device_ids { 25 + ID_AD8801, 26 + ID_AD8803, 27 + }; 28 + 29 + struct ad8801_state { 30 + struct spi_device *spi; 31 + unsigned char dac_cache[8]; /* Value write on each channel */ 32 + unsigned int vrefh_mv; 33 + unsigned int vrefl_mv; 34 + struct regulator *vrefh_reg; 35 + struct regulator *vrefl_reg; 36 + 37 + __be16 data ____cacheline_aligned; 38 + }; 39 + 40 + static int ad8801_spi_write(struct ad8801_state *state, 41 + u8 channel, unsigned char value) 42 + { 43 + state->data = cpu_to_be16((channel << AD8801_CFG_ADDR_OFFSET) | value); 44 + return spi_write(state->spi, &state->data, sizeof(state->data)); 45 + } 46 + 47 + static int ad8801_write_raw(struct iio_dev *indio_dev, 48 + struct iio_chan_spec const *chan, int val, int val2, long mask) 49 + { 50 + struct ad8801_state *state = iio_priv(indio_dev); 51 + int ret; 52 + 53 + switch (mask) { 54 + case IIO_CHAN_INFO_RAW: 55 + if (val >= 256 || val < 0) 56 + return -EINVAL; 57 + 58 + ret = ad8801_spi_write(state, chan->channel, val); 59 + if (ret == 0) 60 + state->dac_cache[chan->channel] = val; 61 + break; 62 + default: 63 + ret = -EINVAL; 64 + } 65 + 66 + return ret; 67 + } 68 + 69 + static int ad8801_read_raw(struct iio_dev *indio_dev, 70 + struct iio_chan_spec const *chan, int *val, int *val2, long info) 71 + { 72 + struct ad8801_state *state = iio_priv(indio_dev); 73 + 74 + switch (info) { 75 + case IIO_CHAN_INFO_RAW: 76 + *val = state->dac_cache[chan->channel]; 77 + return IIO_VAL_INT; 78 + case IIO_CHAN_INFO_SCALE: 79 + *val = state->vrefh_mv - state->vrefl_mv; 80 + *val2 = 8; 81 + return IIO_VAL_FRACTIONAL_LOG2; 82 + case IIO_CHAN_INFO_OFFSET: 83 + *val = state->vrefl_mv; 84 + return IIO_VAL_INT; 85 + default: 86 + return -EINVAL; 87 + } 88 + 89 + return -EINVAL; 90 + } 91 + 92 + static const struct iio_info ad8801_info = { 93 + .read_raw = ad8801_read_raw, 94 + .write_raw = ad8801_write_raw, 95 + .driver_module = THIS_MODULE, 96 + }; 97 + 98 + #define AD8801_CHANNEL(chan) { \ 99 + .type = IIO_VOLTAGE, \ 100 + .indexed = 1, \ 101 + .output = 1, \ 102 + .channel = chan, \ 103 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 104 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 105 + BIT(IIO_CHAN_INFO_OFFSET), \ 106 + } 107 + 108 + static const struct iio_chan_spec ad8801_channels[] = { 109 + AD8801_CHANNEL(0), 110 + AD8801_CHANNEL(1), 111 + AD8801_CHANNEL(2), 112 + AD8801_CHANNEL(3), 113 + AD8801_CHANNEL(4), 114 + AD8801_CHANNEL(5), 115 + AD8801_CHANNEL(6), 116 + AD8801_CHANNEL(7), 117 + }; 118 + 119 + static int ad8801_probe(struct spi_device *spi) 120 + { 121 + struct iio_dev *indio_dev; 122 + struct ad8801_state *state; 123 + const struct spi_device_id *id; 124 + int ret; 125 + 126 + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); 127 + if (indio_dev == NULL) 128 + return -ENOMEM; 129 + 130 + state = iio_priv(indio_dev); 131 + state->spi = spi; 132 + id = spi_get_device_id(spi); 133 + 134 + state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh"); 135 + if (IS_ERR(state->vrefh_reg)) { 136 + dev_err(&spi->dev, "Vrefh regulator not specified\n"); 137 + return PTR_ERR(state->vrefh_reg); 138 + } 139 + 140 + ret = regulator_enable(state->vrefh_reg); 141 + if (ret) { 142 + dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n", 143 + ret); 144 + return ret; 145 + } 146 + 147 + ret = regulator_get_voltage(state->vrefh_reg); 148 + if (ret < 0) { 149 + dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n", 150 + ret); 151 + goto error_disable_vrefh_reg; 152 + } 153 + state->vrefh_mv = ret / 1000; 154 + 155 + if (id->driver_data == ID_AD8803) { 156 + state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl"); 157 + if (IS_ERR(state->vrefl_reg)) { 158 + dev_err(&spi->dev, "Vrefl regulator not specified\n"); 159 + ret = PTR_ERR(state->vrefl_reg); 160 + goto error_disable_vrefh_reg; 161 + } 162 + 163 + ret = regulator_enable(state->vrefl_reg); 164 + if (ret) { 165 + dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n", 166 + ret); 167 + goto error_disable_vrefh_reg; 168 + } 169 + 170 + ret = regulator_get_voltage(state->vrefl_reg); 171 + if (ret < 0) { 172 + dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n", 173 + ret); 174 + goto error_disable_vrefl_reg; 175 + } 176 + state->vrefl_mv = ret / 1000; 177 + } else { 178 + state->vrefl_mv = 0; 179 + state->vrefl_reg = NULL; 180 + } 181 + 182 + spi_set_drvdata(spi, indio_dev); 183 + indio_dev->dev.parent = &spi->dev; 184 + indio_dev->info = &ad8801_info; 185 + indio_dev->modes = INDIO_DIRECT_MODE; 186 + indio_dev->channels = ad8801_channels; 187 + indio_dev->num_channels = ARRAY_SIZE(ad8801_channels); 188 + indio_dev->name = id->name; 189 + 190 + ret = iio_device_register(indio_dev); 191 + if (ret) { 192 + dev_err(&spi->dev, "Failed to register iio device: %d\n", 193 + ret); 194 + goto error_disable_vrefl_reg; 195 + } 196 + 197 + return 0; 198 + 199 + error_disable_vrefl_reg: 200 + if (state->vrefl_reg) 201 + regulator_disable(state->vrefl_reg); 202 + error_disable_vrefh_reg: 203 + regulator_disable(state->vrefh_reg); 204 + return ret; 205 + } 206 + 207 + static int ad8801_remove(struct spi_device *spi) 208 + { 209 + struct iio_dev *indio_dev = spi_get_drvdata(spi); 210 + struct ad8801_state *state = iio_priv(indio_dev); 211 + 212 + iio_device_unregister(indio_dev); 213 + if (state->vrefl_reg) 214 + regulator_disable(state->vrefl_reg); 215 + regulator_disable(state->vrefh_reg); 216 + 217 + return 0; 218 + } 219 + 220 + static const struct spi_device_id ad8801_ids[] = { 221 + {"ad8801", ID_AD8801}, 222 + {"ad8803", ID_AD8803}, 223 + {} 224 + }; 225 + MODULE_DEVICE_TABLE(spi, ad8801_ids); 226 + 227 + static struct spi_driver ad8801_driver = { 228 + .driver = { 229 + .name = "ad8801", 230 + }, 231 + .probe = ad8801_probe, 232 + .remove = ad8801_remove, 233 + .id_table = ad8801_ids, 234 + }; 235 + module_spi_driver(ad8801_driver); 236 + 237 + MODULE_AUTHOR("Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>"); 238 + MODULE_DESCRIPTION("Analog Devices AD8801/AD8803 DAC"); 239 + MODULE_LICENSE("GPL v2");