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

iio: dpot-dac: DAC driver based on a digital potentiometer

It is assumed that the dpot is used as a voltage divider between the
current dpot wiper setting and the maximum resistance of the dpot. The
divided voltage is provided by a vref regulator.

.------.
.-----------. | |
| vref |--' .---.
| regulator |--. | |
'-----------' | | d |
| | p |
| | o | wiper
| | t |<---------+
| | |
| '---' dac output voltage
| |
'------+------------+

Signed-off-by: Peter Rosin <peda@axentia.se>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>

authored by

Peter Rosin and committed by
Jonathan Cameron
7fde1484 ed13134b

+287
+8
Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
··· 1 + What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw_available 2 + Date: October 2016 3 + KernelVersion: 4.9 4 + Contact: Peter Rosin <peda@axentia.se> 5 + Description: 6 + The range of available values represented as the minimum value, 7 + the step and the maximum value, all enclosed in square brackets. 8 + Example: [0 1 256]
+2
MAINTAINERS
··· 6123 6123 M: Peter Rosin <peda@axentia.se> 6124 6124 L: linux-iio@vger.kernel.org 6125 6125 S: Maintained 6126 + F: Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac 6126 6127 F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt 6128 + F: drivers/iio/dac/dpot-dac.c 6127 6129 6128 6130 IIO SUBSYSTEM AND DRIVERS 6129 6131 M: Jonathan Cameron <jic23@kernel.org>
+10
drivers/iio/dac/Kconfig
··· 200 200 To compile this driver as a module choose M here: the module will be called 201 201 ad8801. 202 202 203 + config DPOT_DAC 204 + tristate "DAC emulation using a DPOT" 205 + depends on OF 206 + help 207 + Say yes here to build support for DAC emulation using a digital 208 + potentiometer. 209 + 210 + To compile this driver as a module, choose M here: the module will be 211 + called dpot-dac. 212 + 203 213 config LPC18XX_DAC 204 214 tristate "NXP LPC18xx DAC driver" 205 215 depends on ARCH_LPC18XX || COMPILE_TEST
+1
drivers/iio/dac/Makefile
··· 22 22 obj-$(CONFIG_AD7303) += ad7303.o 23 23 obj-$(CONFIG_AD8801) += ad8801.o 24 24 obj-$(CONFIG_CIO_DAC) += cio-dac.o 25 + obj-$(CONFIG_DPOT_DAC) += dpot-dac.o 25 26 obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o 26 27 obj-$(CONFIG_M62332) += m62332.o 27 28 obj-$(CONFIG_MAX517) += max517.o
+266
drivers/iio/dac/dpot-dac.c
··· 1 + /* 2 + * IIO DAC emulation driver using a digital potentiometer 3 + * 4 + * Copyright (C) 2016 Axentia Technologies AB 5 + * 6 + * Author: Peter Rosin <peda@axentia.se> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + /* 14 + * It is assumed that the dpot is used as a voltage divider between the 15 + * current dpot wiper setting and the maximum resistance of the dpot. The 16 + * divided voltage is provided by a vref regulator. 17 + * 18 + * .------. 19 + * .-----------. | | 20 + * | vref |--' .---. 21 + * | regulator |--. | | 22 + * '-----------' | | d | 23 + * | | p | 24 + * | | o | wiper 25 + * | | t |<---------+ 26 + * | | | 27 + * | '---' dac output voltage 28 + * | | 29 + * '------+------------+ 30 + */ 31 + 32 + #include <linux/err.h> 33 + #include <linux/iio/consumer.h> 34 + #include <linux/iio/iio.h> 35 + #include <linux/module.h> 36 + #include <linux/of.h> 37 + #include <linux/platform_device.h> 38 + #include <linux/regulator/consumer.h> 39 + 40 + struct dpot_dac { 41 + struct regulator *vref; 42 + struct iio_channel *dpot; 43 + u32 max_ohms; 44 + }; 45 + 46 + static const struct iio_chan_spec dpot_dac_iio_channel = { 47 + .type = IIO_VOLTAGE, 48 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) 49 + | BIT(IIO_CHAN_INFO_SCALE), 50 + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), 51 + .output = 1, 52 + .indexed = 1, 53 + }; 54 + 55 + static int dpot_dac_read_raw(struct iio_dev *indio_dev, 56 + struct iio_chan_spec const *chan, 57 + int *val, int *val2, long mask) 58 + { 59 + struct dpot_dac *dac = iio_priv(indio_dev); 60 + int ret; 61 + unsigned long long tmp; 62 + 63 + switch (mask) { 64 + case IIO_CHAN_INFO_RAW: 65 + return iio_read_channel_raw(dac->dpot, val); 66 + 67 + case IIO_CHAN_INFO_SCALE: 68 + ret = iio_read_channel_scale(dac->dpot, val, val2); 69 + switch (ret) { 70 + case IIO_VAL_FRACTIONAL_LOG2: 71 + tmp = *val * 1000000000LL; 72 + do_div(tmp, dac->max_ohms); 73 + tmp *= regulator_get_voltage(dac->vref) / 1000; 74 + do_div(tmp, 1000000000LL); 75 + *val = tmp; 76 + return ret; 77 + case IIO_VAL_INT: 78 + /* 79 + * Convert integer scale to fractional scale by 80 + * setting the denominator (val2) to one... 81 + */ 82 + *val2 = 1; 83 + ret = IIO_VAL_FRACTIONAL; 84 + /* ...and fall through. */ 85 + case IIO_VAL_FRACTIONAL: 86 + *val *= regulator_get_voltage(dac->vref) / 1000; 87 + *val2 *= dac->max_ohms; 88 + break; 89 + } 90 + 91 + return ret; 92 + } 93 + 94 + return -EINVAL; 95 + } 96 + 97 + static int dpot_dac_read_avail(struct iio_dev *indio_dev, 98 + struct iio_chan_spec const *chan, 99 + const int **vals, int *type, int *length, 100 + long mask) 101 + { 102 + struct dpot_dac *dac = iio_priv(indio_dev); 103 + 104 + switch (mask) { 105 + case IIO_CHAN_INFO_RAW: 106 + *type = IIO_VAL_INT; 107 + return iio_read_avail_channel_raw(dac->dpot, vals, length); 108 + } 109 + 110 + return -EINVAL; 111 + } 112 + 113 + static int dpot_dac_write_raw(struct iio_dev *indio_dev, 114 + struct iio_chan_spec const *chan, 115 + int val, int val2, long mask) 116 + { 117 + struct dpot_dac *dac = iio_priv(indio_dev); 118 + 119 + switch (mask) { 120 + case IIO_CHAN_INFO_RAW: 121 + return iio_write_channel_raw(dac->dpot, val); 122 + } 123 + 124 + return -EINVAL; 125 + } 126 + 127 + static const struct iio_info dpot_dac_info = { 128 + .read_raw = dpot_dac_read_raw, 129 + .read_avail = dpot_dac_read_avail, 130 + .write_raw = dpot_dac_write_raw, 131 + .driver_module = THIS_MODULE, 132 + }; 133 + 134 + static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev) 135 + { 136 + struct device *dev = &indio_dev->dev; 137 + struct dpot_dac *dac = iio_priv(indio_dev); 138 + unsigned long long tmp; 139 + int ret; 140 + int val; 141 + int val2; 142 + int max; 143 + 144 + ret = iio_read_max_channel_raw(dac->dpot, &max); 145 + if (ret < 0) { 146 + dev_err(dev, "dpot does not indicate its raw maximum value\n"); 147 + return ret; 148 + } 149 + 150 + switch (iio_read_channel_scale(dac->dpot, &val, &val2)) { 151 + case IIO_VAL_INT: 152 + return max * val; 153 + case IIO_VAL_FRACTIONAL: 154 + tmp = (unsigned long long)max * val; 155 + do_div(tmp, val2); 156 + return tmp; 157 + case IIO_VAL_FRACTIONAL_LOG2: 158 + tmp = val * 1000000000LL * max >> val2; 159 + do_div(tmp, 1000000000LL); 160 + return tmp; 161 + default: 162 + dev_err(dev, "dpot has a scale that is too weird\n"); 163 + } 164 + 165 + return -EINVAL; 166 + } 167 + 168 + static int dpot_dac_probe(struct platform_device *pdev) 169 + { 170 + struct device *dev = &pdev->dev; 171 + struct iio_dev *indio_dev; 172 + struct dpot_dac *dac; 173 + enum iio_chan_type type; 174 + int ret; 175 + 176 + indio_dev = devm_iio_device_alloc(dev, sizeof(*dac)); 177 + if (!indio_dev) 178 + return -ENOMEM; 179 + 180 + platform_set_drvdata(pdev, indio_dev); 181 + dac = iio_priv(indio_dev); 182 + 183 + indio_dev->name = dev_name(dev); 184 + indio_dev->dev.parent = dev; 185 + indio_dev->info = &dpot_dac_info; 186 + indio_dev->modes = INDIO_DIRECT_MODE; 187 + indio_dev->channels = &dpot_dac_iio_channel; 188 + indio_dev->num_channels = 1; 189 + 190 + dac->vref = devm_regulator_get(dev, "vref"); 191 + if (IS_ERR(dac->vref)) { 192 + if (PTR_ERR(dac->vref) != -EPROBE_DEFER) 193 + dev_err(&pdev->dev, "failed to get vref regulator\n"); 194 + return PTR_ERR(dac->vref); 195 + } 196 + 197 + dac->dpot = devm_iio_channel_get(dev, "dpot"); 198 + if (IS_ERR(dac->dpot)) { 199 + if (PTR_ERR(dac->dpot) != -EPROBE_DEFER) 200 + dev_err(dev, "failed to get dpot input channel\n"); 201 + return PTR_ERR(dac->dpot); 202 + } 203 + 204 + ret = iio_get_channel_type(dac->dpot, &type); 205 + if (ret < 0) 206 + return ret; 207 + 208 + if (type != IIO_RESISTANCE) { 209 + dev_err(dev, "dpot is of the wrong type\n"); 210 + return -EINVAL; 211 + } 212 + 213 + ret = dpot_dac_channel_max_ohms(indio_dev); 214 + if (ret < 0) 215 + return ret; 216 + dac->max_ohms = ret; 217 + 218 + ret = regulator_enable(dac->vref); 219 + if (ret) { 220 + dev_err(dev, "failed to enable the vref regulator\n"); 221 + return ret; 222 + } 223 + 224 + ret = iio_device_register(indio_dev); 225 + if (ret) { 226 + dev_err(dev, "failed to register iio device\n"); 227 + goto disable_reg; 228 + } 229 + 230 + return 0; 231 + 232 + disable_reg: 233 + regulator_disable(dac->vref); 234 + return ret; 235 + } 236 + 237 + static int dpot_dac_remove(struct platform_device *pdev) 238 + { 239 + struct iio_dev *indio_dev = platform_get_drvdata(pdev); 240 + struct dpot_dac *dac = iio_priv(indio_dev); 241 + 242 + iio_device_unregister(indio_dev); 243 + regulator_disable(dac->vref); 244 + 245 + return 0; 246 + } 247 + 248 + static const struct of_device_id dpot_dac_match[] = { 249 + { .compatible = "dpot-dac" }, 250 + { /* sentinel */ } 251 + }; 252 + MODULE_DEVICE_TABLE(of, dpot_dac_match); 253 + 254 + static struct platform_driver dpot_dac_driver = { 255 + .probe = dpot_dac_probe, 256 + .remove = dpot_dac_remove, 257 + .driver = { 258 + .name = "iio-dpot-dac", 259 + .of_match_table = dpot_dac_match, 260 + }, 261 + }; 262 + module_platform_driver(dpot_dac_driver); 263 + 264 + MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer"); 265 + MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); 266 + MODULE_LICENSE("GPL v2");