doc: dts: update howtos with new API

Document the use of DEVICE_DT_GET() to fetch device pointers at
compile time, and update the documentation on defining device
instances to use the devicetree macros.

Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
This commit is contained in:
Peter Bigot 2021-01-06 08:39:01 -06:00 committed by Kumar Gala
commit 92324d9a4d

View file

@ -88,12 +88,13 @@ works best for your requirements. Here are some examples:
/* Option 4: by path */ /* Option 4: by path */
#define MY_SERIAL DT_PATH(soc, serial_40002000) #define MY_SERIAL DT_PATH(soc, serial_40002000)
Once you have a node identifier, get the ``struct device`` by combining Once you have a node identifier there are two ways to proceed. The
classic way is to get the ``struct device`` by combining
:c:func:`DT_LABEL` with :c:func:`device_get_binding`: :c:func:`DT_LABEL` with :c:func:`device_get_binding`:
.. code-block:: c .. code-block:: c
struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL)); const struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL));
You can then use ``uart_dev`` with :ref:`uart_api` API functions like You can then use ``uart_dev`` with :ref:`uart_api` API functions like
:c:func:`uart_configure`. Similar code will work for other device types; just :c:func:`uart_configure`. Similar code will work for other device types; just
@ -103,6 +104,23 @@ There's no need to override the ``label`` property to something else: just make
a node identifier and pass it to ``DT_LABEL`` to get the right string to pass a node identifier and pass it to ``DT_LABEL`` to get the right string to pass
to ``device_get_binding()``. to ``device_get_binding()``.
The second way to get a device is to use :c:func:`DEVICE_DT_GET`:
.. code-block:: c
const struct device *uart_dev = DEVICE_DT_GET(MY_SERIAL);
if (!device_is_ready(uart_dev)) {
/* Not ready, do not use */
return -ENODEV;
}
This idiom fetches the device pointer at build-time, which is useful when you
want to store the device pointer as configuration data. But because the
device may not be initialized, or may have failed to initialize, you must
verify that the device is ready to be used before passing it to any API
functions. (This check is done for you by :c:func:`device_get_binding`.)
If you're having trouble, see :ref:`dt-trouble`. The first thing to check is If you're having trouble, see :ref:`dt-trouble`. The first thing to check is
that the node has ``status = "okay"``, like this: that the node has ``status = "okay"``, like this:
@ -111,7 +129,7 @@ that the node has ``status = "okay"``, like this:
#define MY_SERIAL DT_NODELABEL(my_serial) #define MY_SERIAL DT_NODELABEL(my_serial)
#if DT_NODE_HAS_STATUS(MY_SERIAL, okay) #if DT_NODE_HAS_STATUS(MY_SERIAL, okay)
struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL)); const struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL));
#else #else
#error "Node is disabled" #error "Node is disabled"
#endif #endif
@ -432,17 +450,17 @@ using instance numbers. Do this after defining ``my_api_funcs``.
/* initialize ROM values as needed. */ \ /* initialize ROM values as needed. */ \
}; \ }; \
DEVICE_DT_INST_DEFINE(inst, \ DEVICE_DT_INST_DEFINE(inst, \
my_dev_init_function, \ my_dev_init_function, \
device_pm_control_nop, \ device_pm_control_nop, \
&my_data_##inst, \ &my_data_##inst, \
&my_cfg_##inst, \ &my_cfg_##inst, \
MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY, \ MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY, \
&my_api_funcs); &my_api_funcs);
Notice the use of APIs like :c:func:`DT_INST_LABEL` and :c:func:`DT_INST_PROP` Notice the use of APIs like :c:func:`DT_INST_PROP` and
to access devicetree node data. These APIs retrieve data from the devicetree :c:func:`DEVICE_DT_INST_DEFINE` to access devicetree node data. These
for instance number ``inst`` of the node with compatible determined by APIs retrieve data from the devicetree for instance number ``inst`` of
``DT_DRV_COMPAT``. the node with compatible determined by ``DT_DRV_COMPAT``.
Finally, pass the instantiation macro to :c:func:`DT_INST_FOREACH_STATUS_OKAY`: Finally, pass the instantiation macro to :c:func:`DT_INST_FOREACH_STATUS_OKAY`:
@ -508,16 +526,16 @@ devicetree to operate on specific device nodes:
.freq = DT_PROP(MYDEV(idx), clock_frequency), \ .freq = DT_PROP(MYDEV(idx), clock_frequency), \
}; \ }; \
static const struct my_dev_cfg my_cfg_##idx = { /* ... */ }; \ static const struct my_dev_cfg my_cfg_##idx = { /* ... */ }; \
DEVICE_DT_INST_DEFINE(idx, \ DEVICE_DT_DEFINE(MYDEV(idx), \
my_dev_init_function, \ my_dev_init_function, \
device_pm_control_nop, \ device_pm_control_nop, \
&my_data_##idx, \ &my_data_##idx, \
&my_cfg_##idx, \ &my_cfg_##idx, \
MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY, \ MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY, \
&my_api_funcs) &my_api_funcs)
Notice the use of APIs like :c:func:`DT_LABEL` and :c:func:`DT_PROP` to access Notice the use of APIs like :c:func:`DT_PROP` and
devicetree node data. :c:func:`DEVICE_DT_DEFINE` to access devicetree node data.
Finally, manually detect each enabled devicetree node and use Finally, manually detect each enabled devicetree node and use
``CREATE_MY_DEVICE`` to instantiate each ``struct device``: ``CREATE_MY_DEVICE`` to instantiate each ``struct device``: