linux/drivers/iio/dac/ad7303.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * AD7303 Digital to analog converters driver
   4 *
   5 * Copyright 2013 Analog Devices Inc.
   6 */
   7
   8#include <linux/err.h>
   9#include <linux/module.h>
  10#include <linux/mod_devicetable.h>
  11#include <linux/kernel.h>
  12#include <linux/spi/spi.h>
  13#include <linux/slab.h>
  14#include <linux/sysfs.h>
  15#include <linux/regulator/consumer.h>
  16
  17#include <linux/iio/iio.h>
  18#include <linux/iio/sysfs.h>
  19
  20#define AD7303_CFG_EXTERNAL_VREF BIT(15)
  21#define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch))
  22#define AD7303_CFG_ADDR_OFFSET  10
  23
  24#define AD7303_CMD_UPDATE_DAC   (0x3 << 8)
  25
  26/**
  27 * struct ad7303_state - driver instance specific data
  28 * @spi:                the device for this driver instance
  29 * @config:             cached config register value
  30 * @dac_cache:          current DAC raw value (chip does not support readback)
  31 * @vdd_reg:            reference to VDD regulator
  32 * @vref_reg:           reference to VREF regulator
  33 * @lock:               protect writes and cache updates
  34 * @data:               spi transfer buffer
  35 */
  36
  37struct ad7303_state {
  38        struct spi_device *spi;
  39        uint16_t config;
  40        uint8_t dac_cache[2];
  41
  42        struct regulator *vdd_reg;
  43        struct regulator *vref_reg;
  44
  45        struct mutex lock;
  46        /*
  47         * DMA (thus cache coherency maintenance) requires the
  48         * transfer buffers to live in their own cache lines.
  49         */
  50        __be16 data ____cacheline_aligned;
  51};
  52
  53static int ad7303_write(struct ad7303_state *st, unsigned int chan,
  54        uint8_t val)
  55{
  56        st->data = cpu_to_be16(AD7303_CMD_UPDATE_DAC |
  57                (chan << AD7303_CFG_ADDR_OFFSET) |
  58                st->config | val);
  59
  60        return spi_write(st->spi, &st->data, sizeof(st->data));
  61}
  62
  63static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev,
  64        uintptr_t private, const struct iio_chan_spec *chan, char *buf)
  65{
  66        struct ad7303_state *st = iio_priv(indio_dev);
  67
  68        return sysfs_emit(buf, "%d\n", (bool)(st->config &
  69                AD7303_CFG_POWER_DOWN(chan->channel)));
  70}
  71
  72static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,
  73         uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
  74         size_t len)
  75{
  76        struct ad7303_state *st = iio_priv(indio_dev);
  77        bool pwr_down;
  78        int ret;
  79
  80        ret = strtobool(buf, &pwr_down);
  81        if (ret)
  82                return ret;
  83
  84        mutex_lock(&st->lock);
  85
  86        if (pwr_down)
  87                st->config |= AD7303_CFG_POWER_DOWN(chan->channel);
  88        else
  89                st->config &= ~AD7303_CFG_POWER_DOWN(chan->channel);
  90
  91        /* There is no noop cmd which allows us to only update the powerdown
  92         * mode, so just write one of the DAC channels again */
  93        ad7303_write(st, chan->channel, st->dac_cache[chan->channel]);
  94
  95        mutex_unlock(&st->lock);
  96        return len;
  97}
  98
  99static int ad7303_get_vref(struct ad7303_state *st,
 100        struct iio_chan_spec const *chan)
 101{
 102        int ret;
 103
 104        if (st->config & AD7303_CFG_EXTERNAL_VREF)
 105                return regulator_get_voltage(st->vref_reg);
 106
 107        ret = regulator_get_voltage(st->vdd_reg);
 108        if (ret < 0)
 109                return ret;
 110        return ret / 2;
 111}
 112
 113static int ad7303_read_raw(struct iio_dev *indio_dev,
 114        struct iio_chan_spec const *chan, int *val, int *val2, long info)
 115{
 116        struct ad7303_state *st = iio_priv(indio_dev);
 117        int vref_uv;
 118
 119        switch (info) {
 120        case IIO_CHAN_INFO_RAW:
 121                mutex_lock(&st->lock);
 122                *val = st->dac_cache[chan->channel];
 123                mutex_unlock(&st->lock);
 124                return IIO_VAL_INT;
 125        case IIO_CHAN_INFO_SCALE:
 126                vref_uv = ad7303_get_vref(st, chan);
 127                if (vref_uv < 0)
 128                        return vref_uv;
 129
 130                *val = 2 * vref_uv / 1000;
 131                *val2 = chan->scan_type.realbits;
 132
 133                return IIO_VAL_FRACTIONAL_LOG2;
 134        default:
 135                break;
 136        }
 137        return -EINVAL;
 138}
 139
 140static int ad7303_write_raw(struct iio_dev *indio_dev,
 141        struct iio_chan_spec const *chan, int val, int val2, long mask)
 142{
 143        struct ad7303_state *st = iio_priv(indio_dev);
 144        int ret;
 145
 146        switch (mask) {
 147        case IIO_CHAN_INFO_RAW:
 148                if (val >= (1 << chan->scan_type.realbits) || val < 0)
 149                        return -EINVAL;
 150
 151                mutex_lock(&st->lock);
 152                ret = ad7303_write(st, chan->address, val);
 153                if (ret == 0)
 154                        st->dac_cache[chan->channel] = val;
 155                mutex_unlock(&st->lock);
 156                break;
 157        default:
 158                ret = -EINVAL;
 159        }
 160
 161        return ret;
 162}
 163
 164static const struct iio_info ad7303_info = {
 165        .read_raw = ad7303_read_raw,
 166        .write_raw = ad7303_write_raw,
 167};
 168
 169static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
 170        {
 171                .name = "powerdown",
 172                .read = ad7303_read_dac_powerdown,
 173                .write = ad7303_write_dac_powerdown,
 174                .shared = IIO_SEPARATE,
 175        },
 176        { },
 177};
 178
 179#define AD7303_CHANNEL(chan) {                                  \
 180        .type = IIO_VOLTAGE,                                    \
 181        .indexed = 1,                                           \
 182        .output = 1,                                            \
 183        .channel = (chan),                                      \
 184        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
 185        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
 186        .address = (chan),                                      \
 187        .scan_type = {                                          \
 188                .sign = 'u',                                    \
 189                .realbits = 8,                                  \
 190                .storagebits = 8,                               \
 191                .shift = 0,                                     \
 192        },                                                      \
 193        .ext_info = ad7303_ext_info,                            \
 194}
 195
 196static const struct iio_chan_spec ad7303_channels[] = {
 197        AD7303_CHANNEL(0),
 198        AD7303_CHANNEL(1),
 199};
 200
 201static int ad7303_probe(struct spi_device *spi)
 202{
 203        const struct spi_device_id *id = spi_get_device_id(spi);
 204        struct iio_dev *indio_dev;
 205        struct ad7303_state *st;
 206        int ret;
 207
 208        indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
 209        if (indio_dev == NULL)
 210                return -ENOMEM;
 211
 212        st = iio_priv(indio_dev);
 213        spi_set_drvdata(spi, indio_dev);
 214
 215        st->spi = spi;
 216
 217        mutex_init(&st->lock);
 218
 219        st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd");
 220        if (IS_ERR(st->vdd_reg))
 221                return PTR_ERR(st->vdd_reg);
 222
 223        ret = regulator_enable(st->vdd_reg);
 224        if (ret)
 225                return ret;
 226
 227        st->vref_reg = devm_regulator_get_optional(&spi->dev, "REF");
 228        if (IS_ERR(st->vref_reg)) {
 229                ret = PTR_ERR(st->vref_reg);
 230                if (ret != -ENODEV)
 231                        goto err_disable_vdd_reg;
 232                st->vref_reg = NULL;
 233        }
 234
 235        if (st->vref_reg) {
 236                ret = regulator_enable(st->vref_reg);
 237                if (ret)
 238                        goto err_disable_vdd_reg;
 239
 240                st->config |= AD7303_CFG_EXTERNAL_VREF;
 241        }
 242
 243        indio_dev->name = id->name;
 244        indio_dev->info = &ad7303_info;
 245        indio_dev->modes = INDIO_DIRECT_MODE;
 246        indio_dev->channels = ad7303_channels;
 247        indio_dev->num_channels = ARRAY_SIZE(ad7303_channels);
 248
 249        ret = iio_device_register(indio_dev);
 250        if (ret)
 251                goto err_disable_vref_reg;
 252
 253        return 0;
 254
 255err_disable_vref_reg:
 256        if (st->vref_reg)
 257                regulator_disable(st->vref_reg);
 258err_disable_vdd_reg:
 259        regulator_disable(st->vdd_reg);
 260        return ret;
 261}
 262
 263static int ad7303_remove(struct spi_device *spi)
 264{
 265        struct iio_dev *indio_dev = spi_get_drvdata(spi);
 266        struct ad7303_state *st = iio_priv(indio_dev);
 267
 268        iio_device_unregister(indio_dev);
 269
 270        if (st->vref_reg)
 271                regulator_disable(st->vref_reg);
 272        regulator_disable(st->vdd_reg);
 273
 274        return 0;
 275}
 276
 277static const struct of_device_id ad7303_spi_of_match[] = {
 278        { .compatible = "adi,ad7303", },
 279        { /* sentinel */ },
 280};
 281MODULE_DEVICE_TABLE(of, ad7303_spi_of_match);
 282
 283static const struct spi_device_id ad7303_spi_ids[] = {
 284        { "ad7303", 0 },
 285        {}
 286};
 287MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
 288
 289static struct spi_driver ad7303_driver = {
 290        .driver = {
 291                .name = "ad7303",
 292                .of_match_table = ad7303_spi_of_match,
 293        },
 294        .probe = ad7303_probe,
 295        .remove = ad7303_remove,
 296        .id_table = ad7303_spi_ids,
 297};
 298module_spi_driver(ad7303_driver);
 299
 300MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 301MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver");
 302MODULE_LICENSE("GPL v2");
 303