@@ -146,6 +146,239 @@ struct adc_channel_cfg {
146146#endif /* CONFIG_ADC_CONFIGURABLE_INPUTS */
147147};
148148
149+ /**
150+ * @brief Get ADC channel configuration from a given devicetree node.
151+ *
152+ * This returns a static initializer for a <tt>struct adc_channel_cfg</tt>
153+ * filled with data from a given devicetree node.
154+ *
155+ * Example devicetree fragment:
156+ *
157+ * @code{.dts}
158+ * &adc {
159+ * #address-cells = <1>;
160+ * #size-cells = <0>;
161+ *
162+ * channel@0 {
163+ * reg = <0>;
164+ * zephyr,gain = "ADC_GAIN_1_6";
165+ * zephyr,reference = "ADC_REF_INTERNAL";
166+ * zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20)>;
167+ * zephyr,input-positive = <NRF_SAADC_AIN6>;
168+ * zephyr,input-negative = <NRF_SAADC_AIN7>;
169+ * };
170+ *
171+ * channel@1 {
172+ * reg = <1>;
173+ * zephyr,gain = "ADC_GAIN_1_6";
174+ * zephyr,reference = "ADC_REF_INTERNAL";
175+ * zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
176+ * zephyr,input-positive = <NRF_SAADC_AIN0>;
177+ * };
178+ * };
179+ * @endcode
180+ *
181+ * Example usage:
182+ *
183+ * @code{.c}
184+ * static const struct adc_channel_cfg ch0_cfg_dt =
185+ * ADC_CHANNEL_CFG_DT(DT_CHILD(DT_NODELABEL(adc), channel_0));
186+ * static const struct adc_channel_cfg ch1_cfg_dt =
187+ * ADC_CHANNEL_CFG_DT(DT_CHILD(DT_NODELABEL(adc), channel_1));
188+ *
189+ * // Initializes 'ch0_cfg_dt' to:
190+ * // {
191+ * // .channel_id = 0,
192+ * // .gain = ADC_GAIN_1_6,
193+ * // .reference = ADC_REF_INTERNAL,
194+ * // .acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20),
195+ * // .differential = true,
196+ * // .input_positive = NRF_SAADC_AIN6,
197+ * // .input-negative = NRF_SAADC_AIN7,
198+ * // }
199+ * // and 'ch1_cfg_dt' to:
200+ * // {
201+ * // .channel_id = 1,
202+ * // .gain = ADC_GAIN_1_6,
203+ * // .reference = ADC_REF_INTERNAL,
204+ * // .acquisition_time = ADC_ACQ_TIME_DEFAULT,
205+ * // .input_positive = NRF_SAADC_AIN0,
206+ * // }
207+ * @endcode
208+ *
209+ * @param node_id Devicetree node identifier.
210+ *
211+ * @return Static initializer for an adc_channel_cfg structure.
212+ */
213+ #define ADC_CHANNEL_CFG_DT (node_id ) { \
214+ .channel_id = DT_REG_ADDR(node_id), \
215+ .gain = DT_STRING_TOKEN(node_id, zephyr_gain), \
216+ .reference = DT_STRING_TOKEN(node_id, zephyr_reference), \
217+ .acquisition_time = DT_PROP(node_id, zephyr_acquisition_time), \
218+ IF_ENABLED(CONFIG_ADC_CONFIGURABLE_INPUTS, \
219+ (COND_CODE_1(DT_NODE_HAS_PROP(node_id, zephyr_input_negative), \
220+ (.differential = true, \
221+ .input_positive = DT_PROP(node_id, zephyr_input_positive), \
222+ .input_negative = DT_PROP(node_id, zephyr_input_negative),), \
223+ (.input_positive = DT_PROP(node_id, zephyr_input_positive),)))) \
224+ }
225+
226+ /**
227+ * @brief Container for ADC channel information specified in devicetree.
228+ *
229+ * @see ADC_DT_SPEC_GET_BY_IDX
230+ * @see ADC_DT_SPEC_GET
231+ */
232+ struct adc_dt_spec {
233+ /**
234+ * Pointer to the device structure for the ADC driver instance
235+ * used by this io-channel.
236+ */
237+ const struct device * dev ;
238+
239+ /** ADC channel identifier used by this io-channel. */
240+ uint8_t channel_id ;
241+
242+ /**
243+ * Flag indicating whether configuration of the associated ADC channel
244+ * is provided as a child node of the corresponding ADC controller in
245+ * devicetree.
246+ */
247+ bool channel_cfg_dt_node_exists ;
248+
249+ /**
250+ * Configuration of the associated ADC channel specified in devicetree.
251+ * This field is valid only when @a channel_cfg_dt_node_exists is set
252+ * to @a true.
253+ */
254+ struct adc_channel_cfg channel_cfg ;
255+
256+ /**
257+ * Voltage of the reference selected for the channel or 0 if this
258+ * value is not provided in devicetree.
259+ * This field is valid only when @a channel_cfg_dt_node_exists is set
260+ * to @a true.
261+ */
262+ uint16_t vref_mv ;
263+
264+ /**
265+ * ADC resolution to be used for that channel.
266+ * This field is valid only when @a channel_cfg_dt_node_exists is set
267+ * to @a true.
268+ */
269+ uint8_t resolution ;
270+
271+ /**
272+ * Oversampling setting to be used for that channel.
273+ * This field is valid only when @a channel_cfg_dt_node_exists is set
274+ * to @a true.
275+ */
276+ uint8_t oversampling ;
277+ };
278+
279+ /** @cond INTERNAL_HIDDEN */
280+
281+ #define ADC_DT_SPEC_STRUCT (ctlr , input ) { \
282+ .dev = DEVICE_DT_GET(ctlr), \
283+ .channel_id = input, \
284+ ADC_CHANNEL_CFG_FROM_DT_NODE( \
285+ DT_CHILD(ctlr, UTIL_CAT(channel_, input))) \
286+ }
287+
288+ #define ADC_CHANNEL_CFG_FROM_DT_NODE (node_id ) \
289+ IF_ENABLED(DT_NODE_EXISTS(node_id), \
290+ (.channel_cfg_dt_node_exists = true, \
291+ .channel_cfg = ADC_CHANNEL_CFG_DT(node_id), \
292+ .vref_mv = DT_PROP_OR(node_id, zephyr_vref_mv, 0), \
293+ .resolution = DT_PROP_OR(node_id, zephyr_resolution, 0), \
294+ .oversampling = DT_PROP_OR(node_id, zephyr_oversampling, 0),))
295+
296+ /** @endcond */
297+
298+ /**
299+ * @brief Get ADC io-channel information from devicetree.
300+ *
301+ * This returns a static initializer for an @p adc_dt_spec structure
302+ * given a devicetree node and a channel index. The node must have
303+ * the "io-channels" property defined.
304+ *
305+ * Example devicetree fragment:
306+ *
307+ * @code{.dts}
308+ * / {
309+ * zephyr,user {
310+ * io-channels = <&adc0 1>, <&adc0 3>;
311+ * };
312+ * };
313+ *
314+ * &adc0 {
315+ * #address-cells = <1>;
316+ * #size-cells = <0>;
317+ *
318+ * channel@3 {
319+ * reg = <3>;
320+ * zephyr,gain = "ADC_GAIN_1_5";
321+ * zephyr,reference = "ADC_REF_VDD_1_4";
322+ * zephyr,vref-mv = <750>;
323+ * zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
324+ * zephyr,resolution = <12>;
325+ * zephyr,oversampling = <4>;
326+ * };
327+ * };
328+ * @endcode
329+ *
330+ * Example usage:
331+ *
332+ * @code{.c}
333+ * static const struct adc_dt_spec adc_chan0 =
334+ * ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0);
335+ * static const struct adc_dt_spec adc_chan1 =
336+ * ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 1);
337+ *
338+ * // Initializes 'adc_chan0' to:
339+ * // {
340+ * // .dev = DEVICE_DT_GET(DT_NODELABEL(adc0)),
341+ * // .channel_id = 1,
342+ * // }
343+ * // and 'adc_chan1' to:
344+ * // {
345+ * // .dev = DEVICE_DT_GET(DT_NODELABEL(adc0)),
346+ * // .channel_id = 3,
347+ * // .channel_cfg_dt_node_exists = true,
348+ * // .channel_cfg = {
349+ * // .channel_id = 3,
350+ * // .gain = ADC_GAIN_1_5,
351+ * // .reference = ADC_REF_VDD_1_4,
352+ * // .acquisition_time = ADC_ACQ_TIME_DEFAULT,
353+ * // },
354+ * // .vref_mv = 750,
355+ * // .resolution = 12,
356+ * // .oversampling = 4,
357+ * // }
358+ * @endcode
359+ *
360+ * @see ADC_DT_SPEC_GET()
361+ *
362+ * @param node_id Devicetree node identifier.
363+ * @param idx Channel index.
364+ *
365+ * @return Static initializer for an adc_dt_spec structure.
366+ */
367+ #define ADC_DT_SPEC_GET_BY_IDX (node_id , idx ) \
368+ ADC_DT_SPEC_STRUCT(DT_IO_CHANNELS_CTLR_BY_IDX(node_id, idx), \
369+ DT_IO_CHANNELS_INPUT_BY_IDX(node_id, idx))
370+
371+ /**
372+ * @brief Equivalent to ADC_DT_SPEC_GET_BY_IDX(node_id, 0).
373+ *
374+ * @see ADC_DT_SPEC_GET_BY_IDX()
375+ *
376+ * @param node_id Devicetree node identifier.
377+ *
378+ * @return Static initializer for an adc_dt_spec structure.
379+ */
380+ #define ADC_DT_SPEC_GET (node_id ) ADC_DT_SPEC_GET_BY_IDX(node_id, 0)
381+
149382/**
150383 * @brief Convert a raw ADC value to millivolts.
151384 *
0 commit comments