User Tools

Site Tools


zephyr:device_drivers

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

Except where otherwise noted, content on this wiki is licensed under the following license: CC0 1.0 Universal
CC0 1.0 Universal Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki