Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * IIO DAC emulation driver using a digital potentiometer
0004  *
0005  * Copyright (C) 2016 Axentia Technologies AB
0006  *
0007  * Author: Peter Rosin <peda@axentia.se>
0008  */
0009 
0010 /*
0011  * It is assumed that the dpot is used as a voltage divider between the
0012  * current dpot wiper setting and the maximum resistance of the dpot. The
0013  * divided voltage is provided by a vref regulator.
0014  *
0015  *                   .------.
0016  *    .-----------.  |      |
0017  *    | vref      |--'    .---.
0018  *    | regulator |--.    |   |
0019  *    '-----------'  |    | d |
0020  *                   |    | p |
0021  *                   |    | o |  wiper
0022  *                   |    | t |<---------+
0023  *                   |    |   |
0024  *                   |    '---'       dac output voltage
0025  *                   |      |
0026  *                   '------+------------+
0027  */
0028 
0029 #include <linux/err.h>
0030 #include <linux/iio/consumer.h>
0031 #include <linux/iio/iio.h>
0032 #include <linux/module.h>
0033 #include <linux/mod_devicetable.h>
0034 #include <linux/platform_device.h>
0035 #include <linux/regulator/consumer.h>
0036 
0037 struct dpot_dac {
0038     struct regulator *vref;
0039     struct iio_channel *dpot;
0040     u32 max_ohms;
0041 };
0042 
0043 static const struct iio_chan_spec dpot_dac_iio_channel = {
0044     .type = IIO_VOLTAGE,
0045     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
0046                 | BIT(IIO_CHAN_INFO_SCALE),
0047     .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
0048     .output = 1,
0049     .indexed = 1,
0050 };
0051 
0052 static int dpot_dac_read_raw(struct iio_dev *indio_dev,
0053                  struct iio_chan_spec const *chan,
0054                  int *val, int *val2, long mask)
0055 {
0056     struct dpot_dac *dac = iio_priv(indio_dev);
0057     int ret;
0058     unsigned long long tmp;
0059 
0060     switch (mask) {
0061     case IIO_CHAN_INFO_RAW:
0062         return iio_read_channel_raw(dac->dpot, val);
0063 
0064     case IIO_CHAN_INFO_SCALE:
0065         ret = iio_read_channel_scale(dac->dpot, val, val2);
0066         switch (ret) {
0067         case IIO_VAL_FRACTIONAL_LOG2:
0068             tmp = *val * 1000000000LL;
0069             do_div(tmp, dac->max_ohms);
0070             tmp *= regulator_get_voltage(dac->vref) / 1000;
0071             do_div(tmp, 1000000000LL);
0072             *val = tmp;
0073             return ret;
0074         case IIO_VAL_INT:
0075             /*
0076              * Convert integer scale to fractional scale by
0077              * setting the denominator (val2) to one...
0078              */
0079             *val2 = 1;
0080             ret = IIO_VAL_FRACTIONAL;
0081             /* ...and fall through. Say it again for GCC. */
0082             fallthrough;
0083         case IIO_VAL_FRACTIONAL:
0084             *val *= regulator_get_voltage(dac->vref) / 1000;
0085             *val2 *= dac->max_ohms;
0086             break;
0087         }
0088 
0089         return ret;
0090     }
0091 
0092     return -EINVAL;
0093 }
0094 
0095 static int dpot_dac_read_avail(struct iio_dev *indio_dev,
0096                    struct iio_chan_spec const *chan,
0097                    const int **vals, int *type, int *length,
0098                    long mask)
0099 {
0100     struct dpot_dac *dac = iio_priv(indio_dev);
0101 
0102     switch (mask) {
0103     case IIO_CHAN_INFO_RAW:
0104         *type = IIO_VAL_INT;
0105         return iio_read_avail_channel_raw(dac->dpot, vals, length);
0106     }
0107 
0108     return -EINVAL;
0109 }
0110 
0111 static int dpot_dac_write_raw(struct iio_dev *indio_dev,
0112                   struct iio_chan_spec const *chan,
0113                   int val, int val2, long mask)
0114 {
0115     struct dpot_dac *dac = iio_priv(indio_dev);
0116 
0117     switch (mask) {
0118     case IIO_CHAN_INFO_RAW:
0119         return iio_write_channel_raw(dac->dpot, val);
0120     }
0121 
0122     return -EINVAL;
0123 }
0124 
0125 static const struct iio_info dpot_dac_info = {
0126     .read_raw = dpot_dac_read_raw,
0127     .read_avail = dpot_dac_read_avail,
0128     .write_raw = dpot_dac_write_raw,
0129 };
0130 
0131 static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev)
0132 {
0133     struct device *dev = &indio_dev->dev;
0134     struct dpot_dac *dac = iio_priv(indio_dev);
0135     unsigned long long tmp;
0136     int ret;
0137     int val;
0138     int val2;
0139     int max;
0140 
0141     ret = iio_read_max_channel_raw(dac->dpot, &max);
0142     if (ret < 0) {
0143         dev_err(dev, "dpot does not indicate its raw maximum value\n");
0144         return ret;
0145     }
0146 
0147     switch (iio_read_channel_scale(dac->dpot, &val, &val2)) {
0148     case IIO_VAL_INT:
0149         return max * val;
0150     case IIO_VAL_FRACTIONAL:
0151         tmp = (unsigned long long)max * val;
0152         do_div(tmp, val2);
0153         return tmp;
0154     case IIO_VAL_FRACTIONAL_LOG2:
0155         tmp = val * 1000000000LL * max >> val2;
0156         do_div(tmp, 1000000000LL);
0157         return tmp;
0158     default:
0159         dev_err(dev, "dpot has a scale that is too weird\n");
0160     }
0161 
0162     return -EINVAL;
0163 }
0164 
0165 static int dpot_dac_probe(struct platform_device *pdev)
0166 {
0167     struct device *dev = &pdev->dev;
0168     struct iio_dev *indio_dev;
0169     struct dpot_dac *dac;
0170     enum iio_chan_type type;
0171     int ret;
0172 
0173     indio_dev = devm_iio_device_alloc(dev, sizeof(*dac));
0174     if (!indio_dev)
0175         return -ENOMEM;
0176 
0177     platform_set_drvdata(pdev, indio_dev);
0178     dac = iio_priv(indio_dev);
0179 
0180     indio_dev->name = dev_name(dev);
0181     indio_dev->info = &dpot_dac_info;
0182     indio_dev->modes = INDIO_DIRECT_MODE;
0183     indio_dev->channels = &dpot_dac_iio_channel;
0184     indio_dev->num_channels = 1;
0185 
0186     dac->vref = devm_regulator_get(dev, "vref");
0187     if (IS_ERR(dac->vref))
0188         return dev_err_probe(&pdev->dev, PTR_ERR(dac->vref),
0189                      "failed to get vref regulator\n");
0190 
0191     dac->dpot = devm_iio_channel_get(dev, "dpot");
0192     if (IS_ERR(dac->dpot))
0193         return dev_err_probe(&pdev->dev, PTR_ERR(dac->dpot),
0194                      "failed to get dpot input channel\n");
0195 
0196     ret = iio_get_channel_type(dac->dpot, &type);
0197     if (ret < 0)
0198         return ret;
0199 
0200     if (type != IIO_RESISTANCE) {
0201         dev_err(dev, "dpot is of the wrong type\n");
0202         return -EINVAL;
0203     }
0204 
0205     ret = dpot_dac_channel_max_ohms(indio_dev);
0206     if (ret < 0)
0207         return ret;
0208     dac->max_ohms = ret;
0209 
0210     ret = regulator_enable(dac->vref);
0211     if (ret) {
0212         dev_err(dev, "failed to enable the vref regulator\n");
0213         return ret;
0214     }
0215 
0216     ret = iio_device_register(indio_dev);
0217     if (ret) {
0218         dev_err(dev, "failed to register iio device\n");
0219         goto disable_reg;
0220     }
0221 
0222     return 0;
0223 
0224 disable_reg:
0225     regulator_disable(dac->vref);
0226     return ret;
0227 }
0228 
0229 static int dpot_dac_remove(struct platform_device *pdev)
0230 {
0231     struct iio_dev *indio_dev = platform_get_drvdata(pdev);
0232     struct dpot_dac *dac = iio_priv(indio_dev);
0233 
0234     iio_device_unregister(indio_dev);
0235     regulator_disable(dac->vref);
0236 
0237     return 0;
0238 }
0239 
0240 static const struct of_device_id dpot_dac_match[] = {
0241     { .compatible = "dpot-dac" },
0242     { /* sentinel */ }
0243 };
0244 MODULE_DEVICE_TABLE(of, dpot_dac_match);
0245 
0246 static struct platform_driver dpot_dac_driver = {
0247     .probe = dpot_dac_probe,
0248     .remove = dpot_dac_remove,
0249     .driver = {
0250         .name = "iio-dpot-dac",
0251         .of_match_table = dpot_dac_match,
0252     },
0253 };
0254 module_platform_driver(dpot_dac_driver);
0255 
0256 MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer");
0257 MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
0258 MODULE_LICENSE("GPL v2");