1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * IIO DAC emulation driver using a digital potentiometer 4 * 5 * Copyright (C) 2016 Axentia Technologies AB 6 * 7 * Author: Peter Rosin <peda@axentia.se> 8 */ 9 10 /* 11 * It is assumed that the dpot is used as a voltage divider between the 12 * current dpot wiper setting and the maximum resistance of the dpot. The 13 * divided voltage is provided by a vref regulator. 14 * 15 * .------. 16 * .-----------. | | 17 * | vref |--' .---. 18 * | regulator |--. | | 19 * '-----------' | | d | 20 * | | p | 21 * | | o | wiper 22 * | | t |<---------+ 23 * | | | 24 * | '---' dac output voltage 25 * | | 26 * '------+------------+ 27 */ 28 29 #include <linux/err.h> 30 #include <linux/iio/consumer.h> 31 #include <linux/iio/iio.h> 32 #include <linux/module.h> 33 #include <linux/platform_device.h> 34 #include <linux/regulator/consumer.h> 35 36 struct dpot_dac { 37 struct regulator *vref; 38 struct iio_channel *dpot; 39 u32 max_ohms; 40 }; 41 42 static const struct iio_chan_spec dpot_dac_iio_channel = { 43 .type = IIO_VOLTAGE, 44 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) 45 | BIT(IIO_CHAN_INFO_SCALE), 46 .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), 47 .output = 1, 48 .indexed = 1, 49 }; 50 51 static int dpot_dac_read_raw(struct iio_dev *indio_dev, 52 struct iio_chan_spec const *chan, 53 int *val, int *val2, long mask) 54 { 55 struct dpot_dac *dac = iio_priv(indio_dev); 56 int ret; 57 unsigned long long tmp; 58 59 switch (mask) { 60 case IIO_CHAN_INFO_RAW: 61 return iio_read_channel_raw(dac->dpot, val); 62 63 case IIO_CHAN_INFO_SCALE: 64 ret = iio_read_channel_scale(dac->dpot, val, val2); 65 switch (ret) { 66 case IIO_VAL_FRACTIONAL_LOG2: 67 tmp = *val * 1000000000LL; 68 do_div(tmp, dac->max_ohms); 69 tmp *= regulator_get_voltage(dac->vref) / 1000; 70 do_div(tmp, 1000000000LL); 71 *val = tmp; 72 return ret; 73 case IIO_VAL_INT: 74 /* 75 * Convert integer scale to fractional scale by 76 * setting the denominator (val2) to one... 77 */ 78 *val2 = 1; 79 ret = IIO_VAL_FRACTIONAL; 80 /* ...and fall through. Say it again for GCC. */ 81 fallthrough; 82 case IIO_VAL_FRACTIONAL: 83 *val *= regulator_get_voltage(dac->vref) / 1000; 84 *val2 *= dac->max_ohms; 85 break; 86 } 87 88 return ret; 89 } 90 91 return -EINVAL; 92 } 93 94 static int dpot_dac_read_avail(struct iio_dev *indio_dev, 95 struct iio_chan_spec const *chan, 96 const int **vals, int *type, int *length, 97 long mask) 98 { 99 struct dpot_dac *dac = iio_priv(indio_dev); 100 101 switch (mask) { 102 case IIO_CHAN_INFO_RAW: 103 *type = IIO_VAL_INT; 104 return iio_read_avail_channel_raw(dac->dpot, vals, length); 105 } 106 107 return -EINVAL; 108 } 109 110 static int dpot_dac_write_raw(struct iio_dev *indio_dev, 111 struct iio_chan_spec const *chan, 112 int val, int val2, long mask) 113 { 114 struct dpot_dac *dac = iio_priv(indio_dev); 115 116 switch (mask) { 117 case IIO_CHAN_INFO_RAW: 118 return iio_write_channel_raw(dac->dpot, val); 119 } 120 121 return -EINVAL; 122 } 123 124 static const struct iio_info dpot_dac_info = { 125 .read_raw = dpot_dac_read_raw, 126 .read_avail = dpot_dac_read_avail, 127 .write_raw = dpot_dac_write_raw, 128 }; 129 130 static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev) 131 { 132 struct device *dev = &indio_dev->dev; 133 struct dpot_dac *dac = iio_priv(indio_dev); 134 unsigned long long tmp; 135 int ret; 136 int val; 137 int val2; 138 int max; 139 140 ret = iio_read_max_channel_raw(dac->dpot, &max); 141 if (ret < 0) { 142 dev_err(dev, "dpot does not indicate its raw maximum value\n"); 143 return ret; 144 } 145 146 switch (iio_read_channel_scale(dac->dpot, &val, &val2)) { 147 case IIO_VAL_INT: 148 return max * val; 149 case IIO_VAL_FRACTIONAL: 150 tmp = (unsigned long long)max * val; 151 do_div(tmp, val2); 152 return tmp; 153 case IIO_VAL_FRACTIONAL_LOG2: 154 tmp = val * 1000000000LL * max >> val2; 155 do_div(tmp, 1000000000LL); 156 return tmp; 157 default: 158 dev_err(dev, "dpot has a scale that is too weird\n"); 159 } 160 161 return -EINVAL; 162 } 163 164 static int dpot_dac_probe(struct platform_device *pdev) 165 { 166 struct device *dev = &pdev->dev; 167 struct iio_dev *indio_dev; 168 struct dpot_dac *dac; 169 enum iio_chan_type type; 170 int ret; 171 172 indio_dev = devm_iio_device_alloc(dev, sizeof(*dac)); 173 if (!indio_dev) 174 return -ENOMEM; 175 176 platform_set_drvdata(pdev, indio_dev); 177 dac = iio_priv(indio_dev); 178 179 indio_dev->name = dev_name(dev); 180 indio_dev->info = &dpot_dac_info; 181 indio_dev->modes = INDIO_DIRECT_MODE; 182 indio_dev->channels = &dpot_dac_iio_channel; 183 indio_dev->num_channels = 1; 184 185 dac->vref = devm_regulator_get(dev, "vref"); 186 if (IS_ERR(dac->vref)) 187 return dev_err_probe(&pdev->dev, PTR_ERR(dac->vref), 188 "failed to get vref regulator\n"); 189 190 dac->dpot = devm_iio_channel_get(dev, "dpot"); 191 if (IS_ERR(dac->dpot)) 192 return dev_err_probe(&pdev->dev, PTR_ERR(dac->dpot), 193 "failed to get dpot input channel\n"); 194 195 ret = iio_get_channel_type(dac->dpot, &type); 196 if (ret < 0) 197 return ret; 198 199 if (type != IIO_RESISTANCE) { 200 dev_err(dev, "dpot is of the wrong type\n"); 201 return -EINVAL; 202 } 203 204 ret = dpot_dac_channel_max_ohms(indio_dev); 205 if (ret < 0) 206 return ret; 207 dac->max_ohms = ret; 208 209 ret = regulator_enable(dac->vref); 210 if (ret) { 211 dev_err(dev, "failed to enable the vref regulator\n"); 212 return ret; 213 } 214 215 ret = iio_device_register(indio_dev); 216 if (ret) { 217 dev_err(dev, "failed to register iio device\n"); 218 goto disable_reg; 219 } 220 221 return 0; 222 223 disable_reg: 224 regulator_disable(dac->vref); 225 return ret; 226 } 227 228 static void dpot_dac_remove(struct platform_device *pdev) 229 { 230 struct iio_dev *indio_dev = platform_get_drvdata(pdev); 231 struct dpot_dac *dac = iio_priv(indio_dev); 232 233 iio_device_unregister(indio_dev); 234 regulator_disable(dac->vref); 235 } 236 237 static const struct of_device_id dpot_dac_match[] = { 238 { .compatible = "dpot-dac" }, 239 { } 240 }; 241 MODULE_DEVICE_TABLE(of, dpot_dac_match); 242 243 static struct platform_driver dpot_dac_driver = { 244 .probe = dpot_dac_probe, 245 .remove = dpot_dac_remove, 246 .driver = { 247 .name = "iio-dpot-dac", 248 .of_match_table = dpot_dac_match, 249 }, 250 }; 251 module_platform_driver(dpot_dac_driver); 252 253 MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer"); 254 MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); 255 MODULE_LICENSE("GPL v2"); 256 MODULE_IMPORT_NS("IIO_CONSUMER"); 257