diff options
| author | Neil Armstrong <[email protected]> | 2025-11-20 09:12:52 +0100 |
|---|---|---|
| committer | Neil Armstrong <[email protected]> | 2025-11-20 09:17:58 +0100 |
| commit | 60a99d5ca374c83c0118f2634a7fcf4cc707b965 (patch) | |
| tree | 245cf232f7a3e37f522ab84a22d4fecd79cee9d6 /include | |
| parent | a264c0454b8ddd8c5ac0ed29623c72f65ec4d53f (diff) | |
Introduce the Generic System Interconnect Subsystem
Let's introduce the Generic System Interconnect subsystem based on
the counterpart Linux framework which is used to vote for bandwidth
across multiple SoC busses.
Documentation for the Linux Generic System Interconnect Subsystem can
be found at [1].
Each bus endpoints are materialised as "nodes" which are linked together,
and the DT will specify a pair of nodes to enable and set a bandwidth
on the route between those endpoints.
The hardware resources that provide those nodes and provides the way
to vote for the bandwidth are called "providers".
The Interconnect uclass code is heavily based on the Linux one, with
some small differences:
- nodes are allocated as udevices instead of Linux idr_alloc()
- tag management is minimal, only normal xlate is supported
- getting nodes states at probe is not implemented
- providers are probed on demand while the nodes links are traversed
- nodes are populated on bind
- id management is simplified, static IDs and dynamics IDs can be used
- identical consume API as Linux, only implementation differs
Fully tested with associated DM test suite.
[1] https://docs.kernel.org/driver-api/interconnect.html
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Neil Armstrong <[email protected]>
Diffstat (limited to 'include')
| -rw-r--r-- | include/dm/uclass-id.h | 2 | ||||
| -rw-r--r-- | include/interconnect-uclass.h | 136 | ||||
| -rw-r--r-- | include/interconnect.h | 155 |
3 files changed, 293 insertions, 0 deletions
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index eb6416b5917..36b5d87c304 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -83,6 +83,8 @@ enum uclass_id { UCLASS_I3C, /* I3C bus */ UCLASS_IDE, /* IDE device */ UCLASS_IOMMU, /* IOMMU */ + UCLASS_INTERCONNECT, /* Interconnect */ + UCLASS_ICC_NODE, /* Interconnect Node */ UCLASS_IRQ, /* Interrupt controller */ UCLASS_KEYBOARD, /* Keyboard input device */ UCLASS_LED, /* Light-emitting diode (LED) */ diff --git a/include/interconnect-uclass.h b/include/interconnect-uclass.h new file mode 100644 index 00000000000..d942ddec41b --- /dev/null +++ b/include/interconnect-uclass.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2025 Linaro Limited + */ + +#ifndef _INTERCONNECT_UCLASS_H +#define _INTERCONNECT_UCLASS_H + +#include <interconnect.h> + +#define icc_units_to_bps(bw) ((bw) * 1000ULL) + +struct udevice; + +/** + * struct icc_req - constraints that are attached to each node + * + * @req_node: entry in list of requests for the particular @node + * @node: the interconnect node to which this constraint applies + * @enabled: indicates whether the path with this request is enabled + * @tag: path tag (optional) + * @avg_bw: an integer describing the average bandwidth in kBps + * @peak_bw: an integer describing the peak bandwidth in kBps + */ +struct icc_req { + struct hlist_node req_node; + struct icc_node *node; + bool enabled; + u32 tag; + u32 avg_bw; + u32 peak_bw; +}; + +/** + * struct icc_path - An interconnect path + * + * @dev: Device who requested the path + * @num_nodes: number of nodes (hops) in the path + * @reqs: array of the requests applicable to this path of nodes + */ +struct icc_path { + struct udevice *dev; + size_t num_nodes; + struct icc_req reqs[]; +}; + +/** + * struct icc_provider - interconnect provider (controller) entity that might + * provide multiple interconnect controls + * + * @inter_set: whether inter-provider pairs will be configured with @set + * @xlate_num_nodes: provider-specific nodes counts for mapping nodes from phandle arguments + * @xlate_nodes: provider-specific array for mapping nodes from phandle arguments + */ +struct icc_provider { + bool inter_set; + unsigned int xlate_num_nodes; + struct icc_node **xlate_nodes; +}; + +/** + * struct icc_node - entity that is part of the interconnect topology + * + * @dev: points to the interconnect provider of this node + * @links: a list of targets pointing to where we can go next when traversing + * @num_links: number of links to other interconnect nodes + * @users: count of active users + * @node_list: the list entry in the parent provider's "nodes" list + * @search_list: list used when walking the nodes graph + * @reverse: pointer to previous node when walking the nodes graph + * @is_traversed: flag that is used when walking the nodes graph + * @req_list: a list of QoS constraint requests associated with this node + * @avg_bw: aggregated value of average bandwidth requests from all consumers + * @peak_bw: aggregated value of peak bandwidth requests from all consumers + * @data: pointer to private data + */ +struct icc_node { + struct udevice *dev; + ulong *links; + size_t num_links; + int users; + + struct list_head node_list; + struct list_head search_list; + struct icc_node *reverse; + u8 is_traversed:1; + struct hlist_head req_list; + u32 avg_bw; + u32 peak_bw; + void *data; +}; + +/** + * struct interconnect_ops - Interconnect uclass operations + * + * @of_xlate: provider-specific callback for mapping nodes from phandle arguments + * @set: pointer to device specific set operation function + * @pre_aggregate: pointer to device specific function that is called + * before the aggregation begins (optional) + * @aggregate: pointer to device specific aggregate operation function + */ +struct interconnect_ops { + struct icc_node *(*of_xlate)(struct udevice *dev, + const struct ofnode_phandle_args *args); + int (*set)(struct icc_node *src, struct icc_node *dst); + void (*pre_aggregate)(struct icc_node *node); + int (*aggregate)(struct icc_node *node, u32 tag, u32 avg_bw, + u32 peak_bw, u32 *agg_avg, u32 *agg_peak); +}; + +/** + * icc_node_create() - create a node + * + * @dev: Provider device + * @id: node id, can be a numeric ID or pointer casted to ulong + * @name: node name + * + * Return: icc_node pointer on success, or ERR_PTR() on error + */ +struct icc_node *icc_node_create(struct udevice *dev, + ulong id, const char *name); + +/** + * icc_link_create() - create a link between two nodes + * @node: source node id + * @dst_id: destination node id + * + * Create a link between two nodes. The nodes might belong to different + * interconnect providers and the @dst_id node might not exist, the link + * will be done at runtime in `icc_path_find()`. + * + * Return: 0 on success, or an error code otherwise + */ +int icc_link_create(struct icc_node *node, const ulong dst_id); + +#endif diff --git a/include/interconnect.h b/include/interconnect.h new file mode 100644 index 00000000000..11ed30d79d6 --- /dev/null +++ b/include/interconnect.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2025 Linaro Limited + */ + +#ifndef _INTERCONNECT_H +#define _INTERCONNECT_H + +#include <linux/errno.h> + +struct udevice; + +/* macros for converting to icc units */ +#define Bps_to_icc(x) ((x) / 1000) +#define kBps_to_icc(x) (x) +#define MBps_to_icc(x) ((x) * 1000) +#define GBps_to_icc(x) ((x) * 1000 * 1000) +#define bps_to_icc(x) (1) +#define kbps_to_icc(x) ((x) / 8 + ((x) % 8 ? 1 : 0)) +#define Mbps_to_icc(x) ((x) * 1000 / 8) +#define Gbps_to_icc(x) ((x) * 1000 * 1000 / 8) + +struct icc_path; + +/** + * of_icc_get - Get an Interconnect path from a DT node based on name + * + * This function will search for a path between two endpoints and return an + * icc_path handle on success. Use icc_put() to release constraints when they + * are not needed anymore. + * If the interconnect API is disabled, NULL is returned and the consumer + * drivers will still build. Drivers are free to handle this specifically, + * but they don't have to. + * + * @dev: The client device. + * @name: Name of the interconnect endpoint pair. + * Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned + * when the API is disabled or the "interconnects" DT property is missing. + */ +#if CONFIG_IS_ENABLED(INTERCONNECT) +struct icc_path *of_icc_get(struct udevice *dev, const char *name); +#else +static inline +struct icc_path *of_icc_get(struct udevice *dev, const char *name) +{ + return NULL; +} +#endif + +/** + * of_icc_get - Get an Interconnect path from a DT node based on index + * + * This function will search for a path between two endpoints and return an + * icc_path handle on success. Use icc_put() to release constraints when they + * are not needed anymore. + * If the interconnect API is disabled, NULL is returned and the consumer + * drivers will still build. Drivers are free to handle this specifically, + * but they don't have to. + * + * @dev: The client device. + * @idx: Index of the interconnect endpoint pair. + * Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned + * when the API is disabled or the "interconnects" DT property is missing. + */ +#if CONFIG_IS_ENABLED(INTERCONNECT) +struct icc_path *of_icc_get_by_index(struct udevice *dev, int idx); +#else +static inline +struct icc_path *of_icc_get_by_index(struct udevice *dev, int idx) +{ + return NULL; +} +#endif + +/** + * icc_put - release the reference to the Interconnect path. + * + * Use this function to release the constraints on a path when the path is + * no longer needed. The constraints will be re-aggregated. + * + * @path: An interconnect path + * Return: 0 if OK, or a negative error code. + */ +#if CONFIG_IS_ENABLED(INTERCONNECT) +int icc_put(struct icc_path *path); +#else +static inline int icc_put(struct icc_path *path) +{ + return 0; +} +#endif + +/** + * icc_enable - Enable an Interconnect path. + * + * This will enable all the endpoints in the path, using the + * bandwidth set by the `icc_set_bw()` call. Otherwise a zero + * bandwidth will be set. Usually used after a call to `icc_disable()`. + * + * @path: An interconnect path + * Return: 0 if OK, or a negative error code. -ENOSYS if not implemented. + */ +#if CONFIG_IS_ENABLED(INTERCONNECT) +int icc_enable(struct icc_path *path); +#else +static inline int icc_enable(struct icc_path *path) +{ + return -ENOSYS; +} +#endif + +/** + * icc_disable - Disable an Interconnect path. + * + * This will disable all the endpoints in the path, effectively setting + * a zero bandwidth. Calling `icc_enable()` will restore the bandwidth set + * by calling `icc_set_bw()`. + * + * @path: An interconnect path + * Return: 0 if OK, or a negative error code. -ENOSYS if not implemented. + */ +#if CONFIG_IS_ENABLED(INTERCONNECT) +int icc_disable(struct icc_path *path); +#else +static inline int icc_disable(struct icc_path *path) +{ + return -ENOSYS; +} +#endif + +/** + * icc_set_bw - set bandwidth constraints on an interconnect path. + * + * This function is used by an interconnect consumer to express its own needs + * in terms of bandwidth for a previously requested path between two endpoints. + * The requests are aggregated and each node is updated accordingly. The entire + * path is locked by a mutex to ensure that the set() is completed. + * The @path can be NULL when the "interconnects" DT properties is missing, + * which will mean that no constraints will be set. + * + * @path: An interconnect path + * @avg_bw: Average bandwidth request in kBps + * @peak_bw: Peak bandwidth in request kBps + * Return: 0 if OK, or a negative error code. -ENOSYS if not implemented. + */ +#if CONFIG_IS_ENABLED(INTERCONNECT) +int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw); +#else +static inline int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw) +{ + return -ENOSYS; +} +#endif + +#endif |
