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