Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc/kernel: Add doc and macros for MFDs #48932

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/kernel/index.rst
Original file line number Diff line number Diff line change
@@ -16,3 +16,4 @@ Kernel
util/index.rst
iterable_sections/index.rst
code-relocation.rst
multi_functional_devices/index.rst
177 changes: 177 additions & 0 deletions doc/kernel/multi_functional_devices/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
.. _multi_functional_devices:

Multi-functional devices
########################

Introduction
************

Multi-functional devices (MFDs) are devices which require two or more distinct
interfaces to be completely utilized.

The :ref:`device_model_api` presents only one interface per device,
consequenctly, MFDs must consist of multiple devices. The following segments
describe the design guidelines for creating MFD drivers, bindings and devicetree
layouts.

MFD devicetree layout
*********************

An MFD consists of one parent device and one to multiple child devices. The
parent and child devices are individually accessible from the application,
and each provide a single interface to be used from it. In the device tree,
all child device nodes are subnodes of the single parent device node.

.. code-block:: devicetree

bus {
parent {
child1 {
};
child2 {
};
child3 {
};
};
};

MFD bindings
************

In the parent device's bindings file, the ``bus`` property is assigned the
parent's ``compatible`` property value.

.. code-block:: yaml

# Parent bindings file

compatible: "foo-company,parent"

bus: foo-company,parent

In the child devices bindings files, the ``on-bus`` property is assigned the
parent's ``compatible`` property value.

.. code-block:: yaml

# Child bindings file

compatible: "foo-company,parent-child"

on-bus: foo-company,parent

The childs ``compatible`` properties are assigned the parent's ``compatible``
property value, dash, the childs name.

.. note::

Properties specific to a child device must be placed in the child's bindings
file.

MFD devicetree layout in bindings file
**************************************

The MFDs devicetree layout must be presented in a commented out section at the
end of the parent device's bindings file. The layout must display at minimum all
required subnodes and properties with example values to be used as reference.

.. code-block:: yaml

# Parent bindings file

compatible: "foo-company,parent"

properties:
a:
type: phandle
required: true

bus: foo-company,parent

on-bus: bus

# Example
#
# bus {
# parent: parent {
# compatible = "foo-company,parent";
# a = <100>;
#
# child1: child1 {
# compatible = "foo-company,parent-child1";
# b = <200>;
# };
#
# child2: child2 {
# compatible = "foo-company,parent-child2";
# c;
# };
#
# child3: child3 {
# compatible = "foo-company,parent-child3";
# };
# };
# };

MFD instances
*************

Each MFD has a single driver which supports multiple interfaces. This driver
first instanciates all child devices, followed by the parent device. All child
devices store a pointer to the parent's struct device in the :c:struct:`device`
data member.

.. note::

Use the :c:macro:`MFD_DT_PARENT(node_id, compat)` and
:c:macro:`MFD_DT_CHILD(node_id, child, compat)` macros to validate and get
the parent and child nodes.

MFD API implementation
**********************

The parent and child API implementations only differ in how the parent
instance data is retrieved.

.. code-block:: c

int parent_api_func1_impl(const struct device *dev, uint8_t value)
{
struct parent_data *data = (struct parent_data *)dev->data;
...
}

int child1_api_func1_impl(const struct device *dev, int8_t value)
{
/* Pointer to parent stored in child1's data pointer */
const struct device *parent = (const struct device *)dev->data;
struct parent_data *data = (struct parent_data *)parent->data;
...
}

For MFDs that have multiple child devices implementing the same API,
an index can be stored in the child devices config struct.

.. code-block:: c

int child_api_func1_impl(const struct device *dev, int8_t value)
{
/* Pointer to parent stored in child's data pointer */
const struct device *parent = (const struct device *)dev->data;
struct parent_data *data = (struct parent_data *)parent->data;

/* Index stored in child cfg */
const struct child_cfg *child_cfg = (const struct child_cfg *)dev->cfg;
uint8_t child_index = child_cfg->index;
...
}

MFD power management
********************

All devices which constitude the MFD are registered as pm devices. See
:ref:`pm-device-runtime-pm` and :ref:`pm-device-system-pm`. The application
interacts with each device independantly while the driver internally manages
potential dependencies between the devices during runtime.

.. doxygengroup:: device_model
68 changes: 68 additions & 0 deletions include/zephyr/devicetree/mfd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2022 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @brief Devicetree macros for multi-functional devices.
*/

#ifndef ZEPHYR_INCLUDE_DEVICETREE_MFD_H_
#define ZEPHYR_INCLUDE_DEVICETREE_MFD_H_

/**
* @brief Multi-functional device devicetree macros
* @defgroup multi_functional_devices Multi-functional devices
* @ingroup multi_functional_devices
* @{
*/

/**
* @def MFD_DT_PARENT_GET
*
* @brief Validate and get parent devicetree node
*
* @details Expands to the parent devicetree node if the parent node
* is enabled and its compatible property value matches the compat
* parameter. Otherwise expands to ()
*
* @param node_id The child's devicetree node identifier
* @param compat The parent node's expected compatible property value
* @return Parent struct device
*/
#define MFD_DT_PARENT_GET(node_id, compat) \
COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_PARENT(node_id), compat), \
(DEVICE_DT_GET(DT_PARENT(node_id))), ())

/**
* @def MFD_DT_CHILD_GET
*
* @brief Validate and get child devicetree node
*
* @details Expands to the child devicetree node if the child node's
* compatible property value matches the compat parameter. Otherwise
* expands to ()
*
* @param node_id The parent's devicetree node identifier
* @param child The child's devicetree node identifier
* @param compat The child node's expected compatible property value
* @return Child struct device
*/
#define MFD_DT_CHILD_GET(node_id, child, compat) \
COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_CHILD(node_id, child), compat), \
(DEVICE_DT_GET(DT_CHILD(node_id, child))), ())

/**
* @def MFD_DT_CHILD_GET_OR_NULL
*/
#define MFD_DT_CHILD_GET_OR_NULL(node_id, child, compat) \
COND_CODE_1( DT_NODE_EXISTS(DT_CHILD(node_id, child)), \
(DT_HAS_COMPAT_STATUS_OKAY(DT_CHILD(node_id, child), compat), \
DT_CHILD(node_id, child), (NULL)), (NULL))

/**
* @}
*/

#endif /* ZEPHYR_INCLUDE_DEVICETREE_MFD_H_ */