zephyr:device_drivers
Table of Contents
Zephyr Device Drivers
Brief
During the development the structure might differ, as many of the following pieces will be in your project directory. But it is good to know when browsing around the drivers already in upstream Zephyr.
A custom driver will probably involve
| Driver C source code | The structure does not seem to be unified. Most drivers are stored in a flat structure under zephyr/drivers/<type>/<type>_<device>.c/h but some, at least sensors, are stored in a tree structure: zephyr/drivers/sensor/<mfr>/<device>/<device>.c/h or similar. It's seemingly random.. |
| KConfig source file | Placed together with the C source, declaring the CONFIG_* that CMakeLists.txt will listen for |
| CMakeLists.txt entry | Either a zephyr_library_sources_ifdef(CONFIG_MY_AWESOME_DRIVER my_awesome_driver.c) or a add_subdirectory_ifdef(CONFIG_MY_AWESOME_DRIVER my_awesome_driver) depending on file structure in the drivers folder |
| Device tree binding file | zephyr/dts/bindings/<feature>/<compatible>.yaml |
| Possibly som API header | zephyr/include/zephyr/drivers/<type>/<device>.h if not using any of Zephyrs generic API |
| Preferrably some sample code | Somehow sorted under zephyr/samples/ |
Using it will involve
| Device tree entry | in a device tree overlay file, the line compatible: my,awesome-driver will tell Zephyr what C driver to use and what device tree binding to use when validating the entry |
| KConfig config | An entry in you projects settings along the lines CONFIG_MY_AWESOME_DRIVER=y |
The connections between the different files are
| Device tree | compatible="my,awesome-driver | Can be several entries on this line, not sure exactly what happens then. I think it can be used to have common C drivers but separate binding files |
| Device tree binding | compatible: "my,awesome-driver" | Also the file is named my,awesome-driver.yaml but it has no magic meaning like the rest, this is just by convention |
| C Source | #define DT_DRV_COMPAT adi_ad5691 | This is used during the building step somehow to tie the source code in |
| C Source | DEVICE_DT_INST_DEFINE and other macros | This is probably what actually ties the driver to the device instances |
DT Macros
Many macros exist in two versions, DT_ and DT_INST_. The relation is for example:
// Get property from node identifier #define DT_PROP(node_id, prop) DT_CAT3(node_id, _P_, prop) // Get node identifier from instance number #define DT_DRV_INST(inst) DT_INST(inst, DT_DRV_COMPAT) // Get property from instance number #define DT_INST_PROP(inst, prop) DT_PROP(DT_DRV_INST(inst), prop)
The instance number is, according to comment in devicetree.h:
/*
* All nodes with a particular compatible property value are assigned
* instance numbers, which are zero-based indexes specific to that
* compatible. You can get a node identifier for these nodes by
* passing DT_INST() an instance number, @p inst, along with the
* lowercase-and-underscores version of the compatible, @p compat.
*
* Instance numbers have the following properties:
*
* - for each compatible, instance numbers start at 0 and are contiguous
* - exactly one instance number is assigned for each node with a compatible,
* **including disabled nodes**
* - enabled nodes (status property is `okay` or missing) are assigned the
* instance numbers starting from 0, and disabled nodes have instance
* numbers which are greater than those of any enabled node
* ...
* Nodes whose `compatible` property has multiple values are assigned
* independent instance numbers for each compatible.
*/
So the instance nubmer is an index of all nodes with a particular compatible. Node identifier, I assume, is an index over ALL device tree nodes. So like a local vs global index.
By checking the build folder and the generated include files, one can observe that
// An instance number is a number 0 through n // But the node identifier will be something along the lines of DT_N_S_soc_S_i2c_40005400_S_si5351_60 // The macro DT_INST(0, skyworks_si5351) would retrieve this define from the generated source #define DT_N_INST_0_skyworks_si5351 DT_N_S_soc_S_i2c_40005400_S_si5351_60 // The DT_DRV_COMPAT that you define to something along the lines skyworks_si5351 will be just that, a token. Not anything magical. // But it will be used by the zephyr provided helper macros to construct the names for the defines generated by the build system to the devicetree_generated.h so that you can retrieve them // For example, the macro used to retrieve a property DT_INST_PROP(inst, plla_p1) // would expand to DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_plla_p1 // According to the generated header: /* * Devicetree node: /soc/i2c@40005400/si5351@60 * * Node identifier: DT_N_S_soc_S_i2c_40005400_S_si5351_60 * * Binding (compatible = skyworks,si5351): * /workspace/modules/si5351/dts/bindings/clock/skyworks,si5351.yaml * * (Descriptions have moved to the Devicetree Bindings Index * in the documentation.) */ // Some of the generated properties looks like this /* Generic property macros: */ #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_clkin_div 1 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_clkin_div_IDX_0_ENUM_IDX 0 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_clkin_div_IDX_0_EXISTS 1 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_clkin_div_IDX_0_ENUM_VAL_1_EXISTS 1 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_clkin_div_EXISTS 1 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_plla_p1 1638 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_plla_p1_EXISTS 1 // For things like string enum there are a whole bunch of lines generated #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source "XTAL" #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_STRING_UNQUOTED XTAL #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_STRING_TOKEN XTAL #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_STRING_UPPER_TOKEN XTAL #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_IDX_0 "XTAL" #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_IDX_0_EXISTS 1 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_IDX_0_ENUM_IDX 0 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_IDX_0_ENUM_VAL_XTAL_EXISTS 1 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_FOREACH_PROP_ELEM(fn) fn(DT_N_S_soc_S_i2c_40005400_S_si5351_60, pllb_clock_source, 0) #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_FOREACH_PROP_ELEM_SEP(fn, sep) fn(DT_N_S_soc_S_i2c_40005400_S_si5351_60, pllb_clock_source, 0) #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_FOREACH_PROP_ELEM_VARGS(fn, ...) fn(DT_N_S_soc_S_i2c_40005400_S_si5351_60, pllb_clock_source, 0, __VA_ARGS__) #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_FOREACH_PROP_ELEM_SEP_VARGS(fn, sep, ...) fn(DT_N_S_soc_S_i2c_40005400_S_si5351_60, pllb_clock_source, 0, __VA_ARGS__) #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_LEN 1 #define DT_N_S_soc_S_i2c_40005400_S_si5351_60_P_pllb_clock_source_EXISTS 1
zephyr/device_drivers.txt · Last modified: 2025/07/08 19:08 by utedass
