Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * DAC7612 Dual, 12-Bit Serial input Digital-to-Analog Converter
0004  *
0005  * Copyright 2019 Qtechnology A/S
0006  * 2019 Ricardo Ribalda <ribalda@kernel.org>
0007  *
0008  * Licensed under the GPL-2.
0009  */
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/spi/spi.h>
0013 #include <linux/gpio/consumer.h>
0014 #include <linux/iio/iio.h>
0015 
0016 #define DAC7612_RESOLUTION 12
0017 #define DAC7612_ADDRESS 4
0018 #define DAC7612_START 5
0019 
0020 struct dac7612 {
0021     struct spi_device *spi;
0022     struct gpio_desc *loaddacs;
0023     uint16_t cache[2];
0024 
0025     /*
0026      * Lock to protect the state of the device from potential concurrent
0027      * write accesses from userspace. The write operation requires an
0028      * SPI write, then toggling of a GPIO, so the lock aims to protect
0029      * the sanity of the entire sequence of operation.
0030      */
0031     struct mutex lock;
0032 
0033     /*
0034      * DMA (thus cache coherency maintenance) may require the
0035      * transfer buffers to live in their own cache lines.
0036      */
0037     uint8_t data[2] __aligned(IIO_DMA_MINALIGN);
0038 };
0039 
0040 static int dac7612_cmd_single(struct dac7612 *priv, int channel, u16 val)
0041 {
0042     int ret;
0043 
0044     priv->data[0] = BIT(DAC7612_START) | (channel << DAC7612_ADDRESS);
0045     priv->data[0] |= val >> 8;
0046     priv->data[1] = val & 0xff;
0047 
0048     priv->cache[channel] = val;
0049 
0050     ret = spi_write(priv->spi, priv->data, sizeof(priv->data));
0051     if (ret)
0052         return ret;
0053 
0054     gpiod_set_value(priv->loaddacs, 1);
0055     gpiod_set_value(priv->loaddacs, 0);
0056 
0057     return 0;
0058 }
0059 
0060 #define dac7612_CHANNEL(chan, name) {               \
0061     .type = IIO_VOLTAGE,                    \
0062     .channel = (chan),                  \
0063     .indexed = 1,                       \
0064     .output = 1,                        \
0065     .datasheet_name = name,                 \
0066     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),       \
0067     .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
0068 }
0069 
0070 static const struct iio_chan_spec dac7612_channels[] = {
0071     dac7612_CHANNEL(0, "OUTA"),
0072     dac7612_CHANNEL(1, "OUTB"),
0073 };
0074 
0075 static int dac7612_read_raw(struct iio_dev *iio_dev,
0076                 const struct iio_chan_spec *chan,
0077                 int *val, int *val2, long mask)
0078 {
0079     struct dac7612 *priv;
0080 
0081     switch (mask) {
0082     case IIO_CHAN_INFO_RAW:
0083         priv = iio_priv(iio_dev);
0084         *val = priv->cache[chan->channel];
0085         return IIO_VAL_INT;
0086 
0087     case IIO_CHAN_INFO_SCALE:
0088         *val = 1;
0089         return IIO_VAL_INT;
0090 
0091     default:
0092         return -EINVAL;
0093     }
0094 }
0095 
0096 static int dac7612_write_raw(struct iio_dev *iio_dev,
0097                  const struct iio_chan_spec *chan,
0098                  int val, int val2, long mask)
0099 {
0100     struct dac7612 *priv = iio_priv(iio_dev);
0101     int ret;
0102 
0103     if (mask != IIO_CHAN_INFO_RAW)
0104         return -EINVAL;
0105 
0106     if ((val >= BIT(DAC7612_RESOLUTION)) || val < 0 || val2)
0107         return -EINVAL;
0108 
0109     if (val == priv->cache[chan->channel])
0110         return 0;
0111 
0112     mutex_lock(&priv->lock);
0113     ret = dac7612_cmd_single(priv, chan->channel, val);
0114     mutex_unlock(&priv->lock);
0115 
0116     return ret;
0117 }
0118 
0119 static const struct iio_info dac7612_info = {
0120     .read_raw = dac7612_read_raw,
0121     .write_raw = dac7612_write_raw,
0122 };
0123 
0124 static int dac7612_probe(struct spi_device *spi)
0125 {
0126     struct iio_dev *iio_dev;
0127     struct dac7612 *priv;
0128     int i;
0129     int ret;
0130 
0131     iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
0132     if (!iio_dev)
0133         return -ENOMEM;
0134 
0135     priv = iio_priv(iio_dev);
0136     /*
0137      * LOADDACS pin can be controlled by the driver or externally.
0138      * When controlled by the driver, the DAC value is updated after
0139      * every write.
0140      * When the driver does not control the PIN, the user or an external
0141      * event can change the value of all DACs by pulsing down the LOADDACs
0142      * pin.
0143      */
0144     priv->loaddacs = devm_gpiod_get_optional(&spi->dev, "ti,loaddacs",
0145                          GPIOD_OUT_LOW);
0146     if (IS_ERR(priv->loaddacs))
0147         return PTR_ERR(priv->loaddacs);
0148     priv->spi = spi;
0149     spi_set_drvdata(spi, iio_dev);
0150     iio_dev->info = &dac7612_info;
0151     iio_dev->modes = INDIO_DIRECT_MODE;
0152     iio_dev->channels = dac7612_channels;
0153     iio_dev->num_channels = ARRAY_SIZE(priv->cache);
0154     iio_dev->name = spi_get_device_id(spi)->name;
0155 
0156     mutex_init(&priv->lock);
0157 
0158     for (i = 0; i < ARRAY_SIZE(priv->cache); i++) {
0159         ret = dac7612_cmd_single(priv, i, 0);
0160         if (ret)
0161             return ret;
0162     }
0163 
0164     return devm_iio_device_register(&spi->dev, iio_dev);
0165 }
0166 
0167 static const struct spi_device_id dac7612_id[] = {
0168     {"ti-dac7612"},
0169     {}
0170 };
0171 MODULE_DEVICE_TABLE(spi, dac7612_id);
0172 
0173 static const struct of_device_id dac7612_of_match[] = {
0174     { .compatible = "ti,dac7612" },
0175     { .compatible = "ti,dac7612u" },
0176     { .compatible = "ti,dac7612ub" },
0177     { },
0178 };
0179 MODULE_DEVICE_TABLE(of, dac7612_of_match);
0180 
0181 static struct spi_driver dac7612_driver = {
0182     .driver = {
0183            .name = "ti-dac7612",
0184            .of_match_table = dac7612_of_match,
0185            },
0186     .probe = dac7612_probe,
0187     .id_table = dac7612_id,
0188 };
0189 module_spi_driver(dac7612_driver);
0190 
0191 MODULE_AUTHOR("Ricardo Ribalda <ribalda@kernel.org>");
0192 MODULE_DESCRIPTION("Texas Instruments DAC7612 DAC driver");
0193 MODULE_LICENSE("GPL v2");