Commit 5decbf53006c8e2aed8e5506b3961810c1544b3c
Committed by
Minkyu Kang
1 parent
7c816e24a4
Exists in
v2017.01-smarct4x
and in
30 other branches
dm: adc: add simple ADC uclass implementation
This commit adds: - new uclass id: UCLASS_ADC - new uclass driver: drivers/adc/adc-uclass.c The new uclass's API allows for ADC operation on: * single-channel with channel selection by a number * multti-channel with channel selection by bit mask ADC uclass's functions: * single-channel: - adc_start_channel() - start channel conversion - adc_channel_data() - get conversion data - adc_channel_single_shot() - start/get conversion data * multi-channel: - adc_start_channels() - start selected channels conversion - adc_channels_data() - get conversion data - adc_channels_single_shot() - start/get conversion data for channels selected by bit mask * general: - adc_stop() - stop the conversion - adc_vdd_value() - positive reference Voltage value with polarity [uV] - adc_vss_value() - negative reference Voltage value with polarity [uV] - adc_data_mask() - conversion data bit mask The device tree can provide below constraints/properties: - vdd-polarity-negative: if true: Vdd = vdd-microvolts * (-1) - vss-polarity-negative: if true: Vss = vss-microvolts * (-1) - vdd-supply: phandle to Vdd regulator's node - vss-supply: phandle to Vss regulator's node And optional, checked only if the above corresponding, doesn't exist: - vdd-microvolts: positive reference Voltage [uV] - vss-microvolts: negative reference Voltage [uV] Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com> Cc: Simon Glass <sjg@chromium.org> Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
Showing 8 changed files with 783 additions and 0 deletions Side-by-side Diff
doc/device-tree-bindings/adc/adc.txt
1 | +ADC device binding | |
2 | + | |
3 | +There are no mandatory properties for ADC. However, if Voltage info is required, | |
4 | +then there are two options: | |
5 | +- use microvolts constraint or | |
6 | +- use regulator phandle to enable/read supply's Voltage | |
7 | + | |
8 | +Properties and constraints: | |
9 | +*optional and always checked, Voltage polarity info: | |
10 | +- vdd-polarity-negative: positive reference Voltage has a negative polarity | |
11 | +- vss-polarity-negative: negative reference Voltage has a negative polarity | |
12 | + | |
13 | +Chose one option, for each supply (Vdd/Vss): | |
14 | + | |
15 | +*optional and always checked, supply Voltage constants: | |
16 | +- vdd-supply: phandle to Vdd regulator's node | |
17 | +- vss-supply: phandle to Vss regulator's node | |
18 | + | |
19 | +*optional and checked only if the above corresponding, doesn't exist: | |
20 | +- vdd-microvolts: positive reference Voltage value [uV] | |
21 | +- vss-microvolts: negative reference Voltage value [uV] | |
22 | + | |
23 | +Example with constant 'Vdd' value: | |
24 | +adc@1000000 { | |
25 | + compatible = "some-adc"; | |
26 | + reg = <0xaabb000 0x100>; | |
27 | + status = "enabled"; | |
28 | + vdd-microvolts = <1800000>; | |
29 | +}; | |
30 | + | |
31 | +Example of supply phandle usage, for the ADC's VDD/VSS references as below: | |
32 | + _______ _______ | |
33 | + |Sandbox| |Sandbox| | |
34 | + : PMIC : : ADC : | |
35 | + . . . . | |
36 | + | | (Vdd) | AIN0|--> | |
37 | + | BUCK2|-------|VDDref | | |
38 | + | (3.3V)| _|VSSref | | |
39 | + |_______| | |_______| | |
40 | + _|_ | |
41 | + | |
42 | +For the above PMIC, the node can be defined as follows: | |
43 | +sandbox_pmic { | |
44 | + compatible = "sandbox,pmic"; | |
45 | + ... | |
46 | + buck2: buck2 { | |
47 | + regulator-name = "SUPPLY_3.3V"; | |
48 | + regulator-min-microvolt = <3300000>; | |
49 | + regulator-max-microvolt = <3300000>; | |
50 | + }; | |
51 | + ... | |
52 | +}; | |
53 | + | |
54 | +For the above ADC, the node can be defined as follows: | |
55 | +adc@0 { | |
56 | + compatible = "sandbox,adc"; | |
57 | + vdd-supply = <&buck2>; | |
58 | + vss-microvolts = <0>; | |
59 | +}; | |
60 | + | |
61 | +The ADC uclass code, will enable the supply before start of the conversion, | |
62 | +but it will not configure the regulator settings. |
drivers/Kconfig
drivers/Makefile
drivers/adc/Kconfig
1 | +config ADC | |
2 | + bool "Enable ADC drivers using Driver Model" | |
3 | + help | |
4 | + This enables ADC API for drivers, which allows driving ADC features | |
5 | + by single and multi-channel methods for: | |
6 | + - start/stop/get data for conversion of a single-channel selected by | |
7 | + a number or multi-channels selected by a bitmask | |
8 | + - get data mask (ADC resolution) | |
9 | + ADC reference Voltage supply options: | |
10 | + - methods for get Vdd/Vss reference Voltage values with polarity | |
11 | + - support supply's phandle with auto-enable | |
12 | + - supply polarity setting in fdt |
drivers/adc/Makefile
drivers/adc/adc-uclass.c
1 | +/* | |
2 | + * Copyright (C) 2015 Samsung Electronics | |
3 | + * Przemyslaw Marczak <p.marczak@samsung.com> | |
4 | + * | |
5 | + * SPDX-License-Identifier: GPL-2.0+ | |
6 | + */ | |
7 | + | |
8 | +#include <common.h> | |
9 | +#include <errno.h> | |
10 | +#include <dm.h> | |
11 | +#include <dm/lists.h> | |
12 | +#include <dm/device-internal.h> | |
13 | +#include <dm/uclass-internal.h> | |
14 | +#include <adc.h> | |
15 | +#include <power/regulator.h> | |
16 | + | |
17 | +DECLARE_GLOBAL_DATA_PTR; | |
18 | + | |
19 | +#define ADC_UCLASS_PLATDATA_SIZE sizeof(struct adc_uclass_platdata) | |
20 | +#define CHECK_NUMBER true | |
21 | +#define CHECK_MASK (!CHECK_NUMBER) | |
22 | + | |
23 | +/* TODO: add support for timer uclass (for early calls) */ | |
24 | +#ifdef CONFIG_SANDBOX_ARCH | |
25 | +#define sdelay(x) udelay(x) | |
26 | +#else | |
27 | +extern void sdelay(unsigned long loops); | |
28 | +#endif | |
29 | + | |
30 | +static int check_channel(struct udevice *dev, int value, bool number_or_mask, | |
31 | + const char *caller_function) | |
32 | +{ | |
33 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
34 | + unsigned mask = number_or_mask ? (1 << value) : value; | |
35 | + | |
36 | + /* For the real ADC hardware, some ADC channels can be inactive. | |
37 | + * For example if device has 4 analog channels, and only channels | |
38 | + * 1-st and 3-rd are valid, then channel mask is: 0b1010, so request | |
39 | + * with mask 0b1110 should return an error. | |
40 | + */ | |
41 | + if ((uc_pdata->channel_mask >= mask) && (uc_pdata->channel_mask & mask)) | |
42 | + return 0; | |
43 | + | |
44 | + printf("Error in %s/%s().\nWrong channel selection for device: %s\n", | |
45 | + __FILE__, caller_function, dev->name); | |
46 | + | |
47 | + return -EINVAL; | |
48 | +} | |
49 | + | |
50 | +static int adc_supply_enable(struct udevice *dev) | |
51 | +{ | |
52 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
53 | + const char *supply_type; | |
54 | + int ret = 0; | |
55 | + | |
56 | + if (uc_pdata->vdd_supply) { | |
57 | + supply_type = "vdd"; | |
58 | + ret = regulator_set_enable(uc_pdata->vdd_supply, true); | |
59 | + } | |
60 | + | |
61 | + if (!ret && uc_pdata->vss_supply) { | |
62 | + supply_type = "vss"; | |
63 | + ret = regulator_set_enable(uc_pdata->vss_supply, true); | |
64 | + } | |
65 | + | |
66 | + if (ret) | |
67 | + error("%s: can't enable %s-supply!", dev->name, supply_type); | |
68 | + | |
69 | + return ret; | |
70 | +} | |
71 | + | |
72 | +int adc_data_mask(struct udevice *dev, unsigned int *data_mask) | |
73 | +{ | |
74 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
75 | + | |
76 | + if (!uc_pdata) | |
77 | + return -ENOSYS; | |
78 | + | |
79 | + *data_mask = uc_pdata->data_mask; | |
80 | + return 0; | |
81 | +} | |
82 | + | |
83 | +int adc_stop(struct udevice *dev) | |
84 | +{ | |
85 | + const struct adc_ops *ops = dev_get_driver_ops(dev); | |
86 | + | |
87 | + if (!ops->stop) | |
88 | + return -ENOSYS; | |
89 | + | |
90 | + return ops->stop(dev); | |
91 | +} | |
92 | + | |
93 | +int adc_start_channel(struct udevice *dev, int channel) | |
94 | +{ | |
95 | + const struct adc_ops *ops = dev_get_driver_ops(dev); | |
96 | + int ret; | |
97 | + | |
98 | + if (!ops->start_channel) | |
99 | + return -ENOSYS; | |
100 | + | |
101 | + ret = check_channel(dev, channel, CHECK_NUMBER, __func__); | |
102 | + if (ret) | |
103 | + return ret; | |
104 | + | |
105 | + ret = adc_supply_enable(dev); | |
106 | + if (ret) | |
107 | + return ret; | |
108 | + | |
109 | + return ops->start_channel(dev, channel); | |
110 | +} | |
111 | + | |
112 | +int adc_start_channels(struct udevice *dev, unsigned int channel_mask) | |
113 | +{ | |
114 | + const struct adc_ops *ops = dev_get_driver_ops(dev); | |
115 | + int ret; | |
116 | + | |
117 | + if (!ops->start_channels) | |
118 | + return -ENOSYS; | |
119 | + | |
120 | + ret = check_channel(dev, channel_mask, CHECK_MASK, __func__); | |
121 | + if (ret) | |
122 | + return ret; | |
123 | + | |
124 | + ret = adc_supply_enable(dev); | |
125 | + if (ret) | |
126 | + return ret; | |
127 | + | |
128 | + return ops->start_channels(dev, channel_mask); | |
129 | +} | |
130 | + | |
131 | +int adc_channel_data(struct udevice *dev, int channel, unsigned int *data) | |
132 | +{ | |
133 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
134 | + const struct adc_ops *ops = dev_get_driver_ops(dev); | |
135 | + unsigned int timeout_us = uc_pdata->data_timeout_us; | |
136 | + int ret; | |
137 | + | |
138 | + if (!ops->channel_data) | |
139 | + return -ENOSYS; | |
140 | + | |
141 | + ret = check_channel(dev, channel, CHECK_NUMBER, __func__); | |
142 | + if (ret) | |
143 | + return ret; | |
144 | + | |
145 | + do { | |
146 | + ret = ops->channel_data(dev, channel, data); | |
147 | + if (!ret || ret != -EBUSY) | |
148 | + break; | |
149 | + | |
150 | + /* TODO: use timer uclass (for early calls). */ | |
151 | + sdelay(5); | |
152 | + } while (timeout_us--); | |
153 | + | |
154 | + return ret; | |
155 | +} | |
156 | + | |
157 | +int adc_channels_data(struct udevice *dev, unsigned int channel_mask, | |
158 | + struct adc_channel *channels) | |
159 | +{ | |
160 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
161 | + unsigned int timeout_us = uc_pdata->multidata_timeout_us; | |
162 | + const struct adc_ops *ops = dev_get_driver_ops(dev); | |
163 | + int ret; | |
164 | + | |
165 | + if (!ops->channels_data) | |
166 | + return -ENOSYS; | |
167 | + | |
168 | + ret = check_channel(dev, channel_mask, CHECK_MASK, __func__); | |
169 | + if (ret) | |
170 | + return ret; | |
171 | + | |
172 | + do { | |
173 | + ret = ops->channels_data(dev, channel_mask, channels); | |
174 | + if (!ret || ret != -EBUSY) | |
175 | + break; | |
176 | + | |
177 | + /* TODO: use timer uclass (for early calls). */ | |
178 | + sdelay(5); | |
179 | + } while (timeout_us--); | |
180 | + | |
181 | + return ret; | |
182 | +} | |
183 | + | |
184 | +int adc_channel_single_shot(const char *name, int channel, unsigned int *data) | |
185 | +{ | |
186 | + struct udevice *dev; | |
187 | + int ret; | |
188 | + | |
189 | + ret = uclass_get_device_by_name(UCLASS_ADC, name, &dev); | |
190 | + if (ret) | |
191 | + return ret; | |
192 | + | |
193 | + ret = adc_start_channel(dev, channel); | |
194 | + if (ret) | |
195 | + return ret; | |
196 | + | |
197 | + ret = adc_channel_data(dev, channel, data); | |
198 | + if (ret) | |
199 | + return ret; | |
200 | + | |
201 | + return 0; | |
202 | +} | |
203 | + | |
204 | +static int _adc_channels_single_shot(struct udevice *dev, | |
205 | + unsigned int channel_mask, | |
206 | + struct adc_channel *channels) | |
207 | +{ | |
208 | + unsigned int data; | |
209 | + int channel, ret; | |
210 | + | |
211 | + for (channel = 0; channel <= ADC_MAX_CHANNEL; channel++) { | |
212 | + /* Check channel bit. */ | |
213 | + if (!((channel_mask >> channel) & 0x1)) | |
214 | + continue; | |
215 | + | |
216 | + ret = adc_start_channel(dev, channel); | |
217 | + if (ret) | |
218 | + return ret; | |
219 | + | |
220 | + ret = adc_channel_data(dev, channel, &data); | |
221 | + if (ret) | |
222 | + return ret; | |
223 | + | |
224 | + channels->id = channel; | |
225 | + channels->data = data; | |
226 | + channels++; | |
227 | + } | |
228 | + | |
229 | + return 0; | |
230 | +} | |
231 | + | |
232 | +int adc_channels_single_shot(const char *name, unsigned int channel_mask, | |
233 | + struct adc_channel *channels) | |
234 | +{ | |
235 | + struct udevice *dev; | |
236 | + int ret; | |
237 | + | |
238 | + ret = uclass_get_device_by_name(UCLASS_ADC, name, &dev); | |
239 | + if (ret) | |
240 | + return ret; | |
241 | + | |
242 | + ret = adc_start_channels(dev, channel_mask); | |
243 | + if (ret) | |
244 | + goto try_manual; | |
245 | + | |
246 | + ret = adc_channels_data(dev, channel_mask, channels); | |
247 | + if (ret) | |
248 | + return ret; | |
249 | + | |
250 | + return 0; | |
251 | + | |
252 | +try_manual: | |
253 | + if (ret != -ENOSYS) | |
254 | + return ret; | |
255 | + | |
256 | + return _adc_channels_single_shot(dev, channel_mask, channels); | |
257 | +} | |
258 | + | |
259 | +static int adc_vdd_platdata_update(struct udevice *dev) | |
260 | +{ | |
261 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
262 | + int ret; | |
263 | + | |
264 | + /* Warning! | |
265 | + * This function can't return supply device before its bind. | |
266 | + * Please pay attention to proper fdt scan sequence. If ADC device | |
267 | + * will bind before its supply regulator device, then the below 'get' | |
268 | + * will return an error. | |
269 | + */ | |
270 | + ret = device_get_supply_regulator(dev, "vdd-supply", | |
271 | + &uc_pdata->vdd_supply); | |
272 | + if (ret) | |
273 | + return ret; | |
274 | + | |
275 | + ret = regulator_get_value(uc_pdata->vdd_supply); | |
276 | + if (ret < 0) | |
277 | + return ret; | |
278 | + | |
279 | + uc_pdata->vdd_microvolts = ret; | |
280 | + | |
281 | + return 0; | |
282 | +} | |
283 | + | |
284 | +static int adc_vss_platdata_update(struct udevice *dev) | |
285 | +{ | |
286 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
287 | + int ret; | |
288 | + | |
289 | + ret = device_get_supply_regulator(dev, "vss-supply", | |
290 | + &uc_pdata->vss_supply); | |
291 | + if (ret) | |
292 | + return ret; | |
293 | + | |
294 | + ret = regulator_get_value(uc_pdata->vss_supply); | |
295 | + if (ret < 0) | |
296 | + return ret; | |
297 | + | |
298 | + uc_pdata->vss_microvolts = ret; | |
299 | + | |
300 | + return 0; | |
301 | +} | |
302 | + | |
303 | +int adc_vdd_value(struct udevice *dev, int *uV) | |
304 | +{ | |
305 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
306 | + int ret, value_sign = uc_pdata->vdd_polarity_negative ? -1 : 1; | |
307 | + | |
308 | + if (!uc_pdata->vdd_supply) | |
309 | + goto nodev; | |
310 | + | |
311 | + /* Update the regulator Value. */ | |
312 | + ret = adc_vdd_platdata_update(dev); | |
313 | + if (ret) | |
314 | + return ret; | |
315 | +nodev: | |
316 | + if (uc_pdata->vdd_microvolts == -ENODATA) | |
317 | + return -ENODATA; | |
318 | + | |
319 | + *uV = uc_pdata->vdd_microvolts * value_sign; | |
320 | + | |
321 | + return 0; | |
322 | +} | |
323 | + | |
324 | +int adc_vss_value(struct udevice *dev, int *uV) | |
325 | +{ | |
326 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
327 | + int ret, value_sign = uc_pdata->vss_polarity_negative ? -1 : 1; | |
328 | + | |
329 | + if (!uc_pdata->vss_supply) | |
330 | + goto nodev; | |
331 | + | |
332 | + /* Update the regulator Value. */ | |
333 | + ret = adc_vss_platdata_update(dev); | |
334 | + if (ret) | |
335 | + return ret; | |
336 | +nodev: | |
337 | + if (uc_pdata->vss_microvolts == -ENODATA) | |
338 | + return -ENODATA; | |
339 | + | |
340 | + *uV = uc_pdata->vss_microvolts * value_sign; | |
341 | + | |
342 | + return 0; | |
343 | +} | |
344 | + | |
345 | +static int adc_vdd_platdata_set(struct udevice *dev) | |
346 | +{ | |
347 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
348 | + int ret, offset = dev->of_offset; | |
349 | + const void *fdt = gd->fdt_blob; | |
350 | + char *prop; | |
351 | + | |
352 | + prop = "vdd-polarity-negative"; | |
353 | + uc_pdata->vdd_polarity_negative = fdtdec_get_bool(fdt, offset, prop); | |
354 | + | |
355 | + ret = adc_vdd_platdata_update(dev); | |
356 | + if (ret != -ENOENT) | |
357 | + return ret; | |
358 | + | |
359 | + /* No vdd-supply phandle. */ | |
360 | + prop = "vdd-microvolts"; | |
361 | + uc_pdata->vdd_microvolts = fdtdec_get_int(fdt, offset, prop, -ENODATA); | |
362 | + | |
363 | + return 0; | |
364 | +} | |
365 | + | |
366 | +static int adc_vss_platdata_set(struct udevice *dev) | |
367 | +{ | |
368 | + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
369 | + int ret, offset = dev->of_offset; | |
370 | + const void *fdt = gd->fdt_blob; | |
371 | + char *prop; | |
372 | + | |
373 | + prop = "vss-polarity-negative"; | |
374 | + uc_pdata->vss_polarity_negative = fdtdec_get_bool(fdt, offset, prop); | |
375 | + | |
376 | + ret = adc_vss_platdata_update(dev); | |
377 | + if (ret != -ENOENT) | |
378 | + return ret; | |
379 | + | |
380 | + /* No vss-supply phandle. */ | |
381 | + prop = "vss-microvolts"; | |
382 | + uc_pdata->vss_microvolts = fdtdec_get_int(fdt, offset, prop, -ENODATA); | |
383 | + | |
384 | + return 0; | |
385 | +} | |
386 | + | |
387 | +static int adc_pre_probe(struct udevice *dev) | |
388 | +{ | |
389 | + int ret; | |
390 | + | |
391 | + /* Set ADC VDD platdata: polarity, uV, regulator (phandle). */ | |
392 | + ret = adc_vdd_platdata_set(dev); | |
393 | + if (ret) | |
394 | + error("%s: Can't update Vdd. Error: %d", dev->name, ret); | |
395 | + | |
396 | + /* Set ADC VSS platdata: polarity, uV, regulator (phandle). */ | |
397 | + ret = adc_vss_platdata_set(dev); | |
398 | + if (ret) | |
399 | + error("%s: Can't update Vss. Error: %d", dev->name, ret); | |
400 | + | |
401 | + return 0; | |
402 | +} | |
403 | + | |
404 | +UCLASS_DRIVER(adc) = { | |
405 | + .id = UCLASS_ADC, | |
406 | + .name = "adc", | |
407 | + .pre_probe = adc_pre_probe, | |
408 | + .per_device_platdata_auto_alloc_size = ADC_UCLASS_PLATDATA_SIZE, | |
409 | +}; |
include/adc.h
1 | +/* | |
2 | + * Copyright (C) 2015 Samsung Electronics | |
3 | + * Przemyslaw Marczak <p.marczak@samsung.com> | |
4 | + * | |
5 | + * SPDX-License-Identifier: GPL-2.0+ | |
6 | + */ | |
7 | + | |
8 | +#ifndef _ADC_H_ | |
9 | +#define _ADC_H_ | |
10 | + | |
11 | +/* ADC_CHANNEL() - ADC channel bit mask, to select only required channels */ | |
12 | +#define ADC_CHANNEL(x) (1 << x) | |
13 | + | |
14 | +/* The last possible selected channel with 32-bit mask */ | |
15 | +#define ADC_MAX_CHANNEL 31 | |
16 | + | |
17 | +/** | |
18 | + * adc_data_format: define the ADC output data format, can be useful when | |
19 | + * the device's input Voltage range is bipolar. | |
20 | + * - ADC_DATA_FORMAT_BIN - binary offset | |
21 | + * - ADC_DATA_FORMAT_2S - two's complement | |
22 | + * | |
23 | + * Note: Device's driver should fill the 'data_format' field of its uclass's | |
24 | + * platform data using one of the above data format types. | |
25 | + */ | |
26 | +enum adc_data_format { | |
27 | + ADC_DATA_FORMAT_BIN, | |
28 | + ADC_DATA_FORMAT_2S, | |
29 | +}; | |
30 | + | |
31 | +/** | |
32 | + * struct adc_channel - structure to hold channel conversion data. | |
33 | + * Useful to keep the result of a multi-channel conversion output. | |
34 | + * | |
35 | + * @id - channel id | |
36 | + * @data - channel conversion data | |
37 | + */ | |
38 | +struct adc_channel { | |
39 | + int id; | |
40 | + unsigned int data; | |
41 | +}; | |
42 | + | |
43 | +/** | |
44 | + * struct adc_uclass_platdata - basic ADC info | |
45 | + * | |
46 | + * Note: The positive/negative reference Voltage is only a name and it doesn't | |
47 | + * provide an information about the value polarity. It is possible, for both | |
48 | + * values to be a negative or positive. For this purpose the uclass's platform | |
49 | + * data provides a bool fields: 'vdd/vss_supply_is_negative'. This is useful, | |
50 | + * since the regulator API returns only a positive Voltage values. | |
51 | + * | |
52 | + * To get the reference Voltage values with polarity, use functions: | |
53 | + * - adc_vdd_value() | |
54 | + * - adc_vss_value() | |
55 | + * Those are useful for some cases of ADC's references, e.g.: | |
56 | + * * Vdd: +3.3V; Vss: -3.3V -> 6.6 Vdiff | |
57 | + * * Vdd: +3.3V; Vss: +0.3V -> 3.0 Vdiff | |
58 | + * * Vdd: +3.3V; Vss: 0.0V -> 3.3 Vdiff | |
59 | + * The last one is usually standard and doesn't require the fdt polarity info. | |
60 | + * | |
61 | + * For more informations read binding info: | |
62 | + * - doc/device-tree-bindings/adc/adc.txt | |
63 | + * | |
64 | + * @data_mask - conversion output data mask | |
65 | + * @data_timeout_us - single channel conversion timeout | |
66 | + * @multidata_timeout_us - multi channel conversion timeout | |
67 | + * @channel_mask - bit mask of available channels [0:31] | |
68 | + * @vdd_supply - positive reference Voltage supply (regulator) | |
69 | + * @vss_supply - negative reference Voltage supply (regulator) | |
70 | + * @vdd_polarity_negative - positive reference Voltage has negative polarity | |
71 | + * @vss_polarity_negative - negative reference Voltage has negative polarity | |
72 | + * @vdd_microvolts - positive reference Voltage value | |
73 | + * @vss_microvolts - negative reference Voltage value | |
74 | + */ | |
75 | +struct adc_uclass_platdata { | |
76 | + int data_format; | |
77 | + unsigned int data_mask; | |
78 | + unsigned int data_timeout_us; | |
79 | + unsigned int multidata_timeout_us; | |
80 | + unsigned int channel_mask; | |
81 | + struct udevice *vdd_supply; | |
82 | + struct udevice *vss_supply; | |
83 | + bool vdd_polarity_negative; | |
84 | + bool vss_polarity_negative; | |
85 | + int vdd_microvolts; | |
86 | + int vss_microvolts; | |
87 | +}; | |
88 | + | |
89 | +/** | |
90 | + * struct adc_ops - ADC device operations for single/multi-channel operation. | |
91 | + */ | |
92 | +struct adc_ops { | |
93 | + /** | |
94 | + * start_channel() - start conversion with its default parameters | |
95 | + * for the given channel number. | |
96 | + * | |
97 | + * @dev: ADC device to init | |
98 | + * @channel: analog channel number | |
99 | + * @return: 0 if OK, -ve on error | |
100 | + */ | |
101 | + int (*start_channel)(struct udevice *dev, int channel); | |
102 | + | |
103 | + /** | |
104 | + * start_channels() - start conversion with its default parameters | |
105 | + * for the channel numbers selected by the bit mask. | |
106 | + * | |
107 | + * This is optional, useful when the hardware supports multichannel | |
108 | + * conversion by the single software trigger. | |
109 | + * | |
110 | + * @dev: ADC device to init | |
111 | + * @channel_mask: bit mask of selected analog channels | |
112 | + * @return: 0 if OK, -ve on error | |
113 | + */ | |
114 | + int (*start_channels)(struct udevice *dev, unsigned int channel_mask); | |
115 | + | |
116 | + /** | |
117 | + * channel_data() - get conversion output data for the given channel. | |
118 | + * | |
119 | + * Note: The implementation of this function should only check, that | |
120 | + * the conversion data is available at the call time. If the hardware | |
121 | + * requires some delay to get the data, then this function should | |
122 | + * return with -EBUSY value. The ADC API will call it in a loop, | |
123 | + * until the data is available or the timeout expires. The maximum | |
124 | + * timeout for this operation is defined by the field 'data_timeout_us' | |
125 | + * in ADC uclasses platform data structure. | |
126 | + * | |
127 | + * @dev: ADC device to trigger | |
128 | + * @channel: selected analog channel number | |
129 | + * @data: returned pointer to selected channel's output data | |
130 | + * @return: 0 if OK, -EBUSY if busy, and other negative on error | |
131 | + */ | |
132 | + int (*channel_data)(struct udevice *dev, int channel, | |
133 | + unsigned int *data); | |
134 | + | |
135 | + /** | |
136 | + * channels_data() - get conversion data for the selected channels. | |
137 | + * | |
138 | + * This is optional, useful when multichannel conversion is supported | |
139 | + * by the hardware, by the single software trigger. | |
140 | + * | |
141 | + * For the proper implementation, please look at the 'Note' for the | |
142 | + * above method. The only difference is in used timeout value, which | |
143 | + * is defined by field 'multidata_timeout_us'. | |
144 | + * | |
145 | + * @dev: ADC device to trigger | |
146 | + * @channel_mask: bit mask of selected analog channels | |
147 | + * @channels: returned pointer to array of output data for channels | |
148 | + * selected by the given mask | |
149 | + * @return: 0 if OK, -ve on error | |
150 | + */ | |
151 | + int (*channels_data)(struct udevice *dev, unsigned int channel_mask, | |
152 | + struct adc_channel *channels); | |
153 | + | |
154 | + /** | |
155 | + * stop() - stop conversion of the given ADC device | |
156 | + * | |
157 | + * @dev: ADC device to stop | |
158 | + * @return: 0 if OK, -ve on error | |
159 | + */ | |
160 | + int (*stop)(struct udevice *dev); | |
161 | +}; | |
162 | + | |
163 | +/** | |
164 | + * adc_start_channel() - start conversion for given device/channel and exit. | |
165 | + * | |
166 | + * @dev: ADC device | |
167 | + * @channel: analog channel number | |
168 | + * @return: 0 if OK, -ve on error | |
169 | + */ | |
170 | +int adc_start_channel(struct udevice *dev, int channel); | |
171 | + | |
172 | +/** | |
173 | + * adc_start_channels() - start conversion for given device/channels and exit. | |
174 | + * | |
175 | + * Note: | |
176 | + * To use this function, device must implement method: start_channels(). | |
177 | + * | |
178 | + * @dev: ADC device to start | |
179 | + * @channel_mask: channel selection - a bit mask | |
180 | + * @channel_mask: bit mask of analog channels | |
181 | + * @return: 0 if OK, -ve on error | |
182 | + */ | |
183 | +int adc_start_channels(struct udevice *dev, unsigned int channel_mask); | |
184 | + | |
185 | +/** | |
186 | + * adc_channel_data() - get conversion data for the given device channel number. | |
187 | + * | |
188 | + * @dev: ADC device to read | |
189 | + * @channel: analog channel number | |
190 | + * @data: pointer to returned channel's data | |
191 | + * @return: 0 if OK, -ve on error | |
192 | + */ | |
193 | +int adc_channel_data(struct udevice *dev, int channel, unsigned int *data); | |
194 | + | |
195 | +/** | |
196 | + * adc_channels_data() - get conversion data for the channels selected by mask | |
197 | + * | |
198 | + * Note: | |
199 | + * To use this function, device must implement methods: | |
200 | + * - start_channels() | |
201 | + * - channels_data() | |
202 | + * | |
203 | + * @dev: ADC device to read | |
204 | + * @channel_mask: channel selection - a bit mask | |
205 | + * @channels: pointer to structure array of returned data for each channel | |
206 | + * @return: 0 if OK, -ve on error | |
207 | + */ | |
208 | +int adc_channels_data(struct udevice *dev, unsigned int channel_mask, | |
209 | + struct adc_channel *channels); | |
210 | + | |
211 | +/** | |
212 | + * adc_data_mask() - get data mask (ADC resolution bitmask) for given ADC device | |
213 | + * | |
214 | + * This can be used if adc uclass platform data is filled. | |
215 | + * | |
216 | + * @dev: ADC device to check | |
217 | + * @data_mask: pointer to the returned data bitmask | |
218 | + * @return: 0 if OK, -ve on error | |
219 | + */ | |
220 | +int adc_data_mask(struct udevice *dev, unsigned int *data_mask); | |
221 | + | |
222 | +/** | |
223 | + * adc_channel_single_shot() - get output data of conversion for the ADC | |
224 | + * device's channel. This function searches for the device with the given name, | |
225 | + * starts the given channel conversion and returns the output data. | |
226 | + * | |
227 | + * Note: To use this function, device must implement metods: | |
228 | + * - start_channel() | |
229 | + * - channel_data() | |
230 | + * | |
231 | + * @name: device's name to search | |
232 | + * @channel: device's input channel to init | |
233 | + * @data: pointer to conversion output data | |
234 | + * @return: 0 if OK, -ve on error | |
235 | + */ | |
236 | +int adc_channel_single_shot(const char *name, int channel, unsigned int *data); | |
237 | + | |
238 | +/** | |
239 | + * adc_channels_single_shot() - get ADC conversion output data for the selected | |
240 | + * device's channels. This function searches for the device by the given name, | |
241 | + * starts the selected channels conversion and returns the output data as array | |
242 | + * of type 'struct adc_channel'. | |
243 | + * | |
244 | + * Note: This function can be used if device implements one of ADC's single | |
245 | + * or multi-channel operation API. If multi-channel operation is not supported, | |
246 | + * then each selected channel is triggered by the sequence start/data in a loop. | |
247 | + * | |
248 | + * @name: device's name to search | |
249 | + * @channel_mask: channel selection - a bit mask | |
250 | + * @channels: pointer to conversion output data for the selected channels | |
251 | + * @return: 0 if OK, -ve on error | |
252 | + */ | |
253 | +int adc_channels_single_shot(const char *name, unsigned int channel_mask, | |
254 | + struct adc_channel *channels); | |
255 | + | |
256 | +/** | |
257 | + * adc_vdd_value() - get the ADC device's positive reference Voltage value | |
258 | + * | |
259 | + * Note: Depending on bool value 'vdd_supply_is_negative' of platform data, | |
260 | + * the returned uV value can be negative, and it's not an error. | |
261 | + * | |
262 | + * @dev: ADC device to check | |
263 | + * @uV: Voltage value with polarization sign (uV) | |
264 | + * @return: 0 on success or -ve on error | |
265 | +*/ | |
266 | +int adc_vdd_value(struct udevice *dev, int *uV); | |
267 | + | |
268 | +/** | |
269 | + * adc_vss_value() - get the ADC device's negative reference Voltage value | |
270 | + * | |
271 | + * Note: Depending on bool value 'vdd_supply_is_negative' of platform data, | |
272 | + * the returned uV value can be negative, and it's not an error. | |
273 | + * | |
274 | + * @dev: ADC device to check | |
275 | + * @uV: Voltage value with polarization sign (uV) | |
276 | + * @return: 0 on success or -ve on error | |
277 | +*/ | |
278 | +int adc_vss_value(struct udevice *dev, int *uV); | |
279 | + | |
280 | +/** | |
281 | + * adc_stop() - stop operation for given ADC device. | |
282 | + * | |
283 | + * @dev: ADC device to stop | |
284 | + * @return: 0 if OK, -ve on error | |
285 | + */ | |
286 | +int adc_stop(struct udevice *dev); | |
287 | + | |
288 | +#endif |
include/dm/uclass-id.h
... | ... | @@ -25,6 +25,7 @@ |
25 | 25 | UCLASS_SIMPLE_BUS, /* bus with child devices */ |
26 | 26 | |
27 | 27 | /* U-Boot uclasses start here - in alphabetical order */ |
28 | + UCLASS_ADC, /* Analog-to-digital converter */ | |
28 | 29 | UCLASS_CLK, /* Clock source, e.g. used by peripherals */ |
29 | 30 | UCLASS_CPU, /* CPU, typically part of an SoC */ |
30 | 31 | UCLASS_CROS_EC, /* Chrome OS EC */ |