2018-03-09 21:13:53

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 0/7] Introduce on-chip interconnect API

Modern SoCs have multiple processors and various dedicated cores (video, gpu,
graphics, modem). These cores are talking to each other and can generate a lot
of data flowing through the on-chip interconnects. These interconnect buses
could form different topologies such as crossbar, point to point buses,
hierarchical buses or use the network-on-chip concept.

These buses have been sized usually to handle use cases with high data
throughput but it is not necessary all the time and consume a lot of power.
Furthermore, the priority between masters can vary depending on the running
use case like video playback or cpu intensive tasks.

Having an API to control the requirement of the system in term of bandwidth
and QoS, so we can adapt the interconnect configuration to match those by
scaling the frequencies, setting link priority and tuning QoS parameters.
This configuration can be a static, one-time operation done at boot for some
platforms or a dynamic set of operations that happen at run-time.

This patchset introduce a new API to get the requirement and configure the
interconnect buses across the entire chipset to fit with the current demand.
The API is NOT for changing the performance of the endpoint devices, but only
the interconnect path in between them.

The API is using a consumer/provider-based model, where the providers are
the interconnect buses and the consumers could be various drivers.
The consumers request interconnect resources (path) to an endpoint and set
the desired constraints on this data flow path. The provider(s) receive
requests from consumers and aggregate these requests for all master-slave
pairs on that path. Then the providers configure each participating in the
topology node according to the requested data flow path, physical links and
constraints. The topology could be complicated and multi-tiered and is SoC
specific.

Below is a simplified diagram of a real-world SoC topology. The interconnect
providers are the NoCs.

+----------------+ +----------------+
| HW Accelerator |--->| M NoC |<---------------+
+----------------+ +----------------+ |
| | +------------+
+-----+ +-------------+ V +------+ | |
| DDR | | +--------+ | PCIe | | |
+-----+ | | Slaves | +------+ | |
^ ^ | +--------+ | | C NoC |
| | V V | |
+------------------+ +------------------------+ | | +-----+
| |-->| |-->| |-->| CPU |
| |-->| |<--| | +-----+
| Mem NoC | | S NoC | +------------+
| |<--| |---------+ |
| |<--| |<------+ | | +--------+
+------------------+ +------------------------+ | | +-->| Slaves |
^ ^ ^ ^ ^ | | +--------+
| | | | | | V
+------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
| CPUs | | | GPU | | DSP | | Masters |-->| P NoC |-->| Slaves |
+------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
|
+-------+
| Modem |
+-------+

TODO:
* Create icc_set_extended() to handle parameters such as latency and other QoS
values.
* Convert from using global node identifiers to local per provider identifiers.
* Cache the path between the nodes instead of walking the graph on each get().
* Sync interconnect requests with the idle state of the device.

Changes since patchset v3 (https://lkml.org/lkml/2017/9/8/544)
* Refactored the constraints aggregation.
* Use the IDR API.
* Split the provider and consumer bindings into separate patches and propose
new bindings for consumers, which allows to specify the local source port.
* Adopted the icc_ prefix for API functions.
* Introduced separate API functions for creating interconnect nodes and links.
* Added DT lookup support in addition to platform data.
* Dropped the event tracing patch for now.
* Added a patch to provide summary via debugfs.
* Use macro for the list of topology definitions in the platform driver.
* Various minor changes.

Changes since patchset v2 (https://lkml.org/lkml/2017/7/20/825)
* Split the aggregation into per node and per provider. Cache the
aggregated values.
* Various small refactorings and cleanups in the framework.
* Added a patch introducing basic tracepoint support for monitoring
the time required to update the interconnect nodes.

Changes since patchset v1 (https://lkml.org/lkml/2017/6/27/890)
* Updates in the documentation.
* Changes in request aggregation, locking.
* Dropped the aggregate() callback and use the default as it currently
sufficient for the single vendor driver. Will add it later when needed.
* Dropped the dt-bindings draft patch for now.

Changes since RFC v2 (https://lkml.org/lkml/2017/6/12/316)
* Converted documentation to rst format.
* Fixed an incorrect call to mutex_lock. Renamed max_bw to peak_bw.

Changes since RFC v1 (https://lkml.org/lkml/2017/5/15/605)
* Refactored code into shorter functions.
* Added a new aggregate() API function.
* Rearranged some structs to reduce padding bytes.

Changes since RFC v0 (https://lkml.org/lkml/2017/3/1/599)
* Removed DT support and added optional Patch 3 with new bindings proposal.
* Converted the topology into internal driver data.
* Made the framework modular.
* interconnect_get() now takes (src and dst ports as arguments).
* Removed public declarations of some structs.
* Now passing prev/next nodes to the vendor driver.
* Properly remove requests on _put().
* Added refcounting.
* Updated documentation.
* Changed struct interconnect_path to use array instead of linked list.

Georgi Djakov (7):
interconnect: Add generic on-chip interconnect API
dt-bindings: Introduce interconnect provider bindings
interconnect: Add debugfs support
interconnect: qcom: Add RPM communication
interconnect: qcom: Add msm8916 interconnect provider driver
dt-bindings: Introduce interconnect consumers bindings
interconnect: Allow endpoints translation via DT

.../bindings/interconnect/interconnect.txt | 70 +++
.../devicetree/bindings/interconnect/qcom-smd.txt | 31 ++
Documentation/interconnect/interconnect.rst | 96 ++++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/interconnect/Kconfig | 15 +
drivers/interconnect/Makefile | 2 +
drivers/interconnect/core.c | 597 +++++++++++++++++++++
drivers/interconnect/qcom/Kconfig | 11 +
drivers/interconnect/qcom/Makefile | 3 +
drivers/interconnect/qcom/msm8916.c | 482 +++++++++++++++++
drivers/interconnect/qcom/smd-rpm.c | 90 ++++
drivers/interconnect/qcom/smd-rpm.h | 15 +
include/linux/interconnect-provider.h | 109 ++++
include/linux/interconnect.h | 46 ++
include/linux/interconnect/qcom.h | 350 ++++++++++++
16 files changed, 1920 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interconnect/interconnect.txt
create mode 100644 Documentation/devicetree/bindings/interconnect/qcom-smd.txt
create mode 100644 Documentation/interconnect/interconnect.rst
create mode 100644 drivers/interconnect/Kconfig
create mode 100644 drivers/interconnect/Makefile
create mode 100644 drivers/interconnect/core.c
create mode 100644 drivers/interconnect/qcom/Kconfig
create mode 100644 drivers/interconnect/qcom/Makefile
create mode 100644 drivers/interconnect/qcom/msm8916.c
create mode 100644 drivers/interconnect/qcom/smd-rpm.c
create mode 100644 drivers/interconnect/qcom/smd-rpm.h
create mode 100644 include/linux/interconnect-provider.h
create mode 100644 include/linux/interconnect.h
create mode 100644 include/linux/interconnect/qcom.h



2018-03-09 21:11:42

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 6/7] dt-bindings: Introduce interconnect consumers bindings

Add documentation for the interconnect consumer bindings, that will allow
to link a device node (consumer) to its interconnect controller hardware.

Tha aim is to enable drivers to request a framework API to configure an
interconnect path by providing their struct device pointer and a name.

Signed-off-by: Georgi Djakov <[email protected]>
---
.../bindings/interconnect/interconnect.txt | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)

diff --git a/Documentation/devicetree/bindings/interconnect/interconnect.txt b/Documentation/devicetree/bindings/interconnect/interconnect.txt
index 70612bb201e4..7935abf10c4b 100644
--- a/Documentation/devicetree/bindings/interconnect/interconnect.txt
+++ b/Documentation/devicetree/bindings/interconnect/interconnect.txt
@@ -45,3 +45,26 @@ Examples:
status = "okay";
};

+= interconnect consumers =
+
+The interconnect consumers are device nodes which consume the interconnect
+path(s) provided by the interconnect provider. There can be multiple
+interconnect providers on a SoC and the consumer may consume multiple paths
+from different providers depending on usecase and the components it has to
+interact with.
+
+Required-properties:
+interconnects: Pairs of phandles and interconnect provider specifier to denote
+ the source and the destination port of the interconnect path.
+interconnect-names: List of interconnect path name strings sorted in the same
+ order as the interconnects property. Consumers drivers will use
+ interconnect-names to match interconnect paths with interconnect
+ specifiers.
+
+Example:
+
+ sdhci@7864000 {
+ ...
+ interconnects = <&pnoc 78 &bimc 512>
+ interconnect-names = "memory";
+ };

2018-03-09 21:12:17

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 7/7] interconnect: Allow endpoints translation via DT

Currently we support only platform data for specifying the interconnect
endpoints. As now the endpoints are hard-coded into the consumer driver
this may leed to complications when a single driver is used by multiple
SoCs, which may have different interconnect topology.
To avoid cluttering the consumer drivers, introduce a translation function
to help us get the board specific interconnect data from device-tree.

Signed-off-by: Georgi Djakov <[email protected]>
---
drivers/interconnect/core.c | 38 ++++++++++++++++++++++++++++++++++++++
include/linux/interconnect.h | 6 ++++++
2 files changed, 44 insertions(+)

diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index a06f752a6aaa..014993473763 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <linux/uaccess.h>

static DEFINE_IDR(icc_idr);
@@ -297,6 +298,43 @@ static int constraints_apply(struct icc_path *path)
return 0;
}

+struct icc_path *of_icc_get(struct device *dev, const char *name)
+{
+ struct device_node *np;
+ u32 src_id, dst_id;
+ int index, ret;
+
+ if (!dev || !name)
+ return NULL;
+
+ np = dev->of_node;
+
+ index = of_property_match_string(np, "interconnect-names", name);
+ if (index < 0)
+ return ERR_PTR(index);
+
+ /*
+ * We use a combination of phandle and specifier for endpoint. For now
+ * lets support only global ids and extend this is the future if needed
+ * without breaking DT compatibility.
+ */
+ ret = of_property_read_u32_index(np, "interconnects", index * 2 + 1,
+ &src_id);
+ if (ret) {
+ dev_err(dev, "interconnect src port is invalid (%d)\n", ret);
+ return ERR_PTR(ret);
+ }
+ ret = of_property_read_u32_index(np, "interconnects", index * 2 + 3,
+ &dst_id);
+ if (ret) {
+ dev_err(dev, "interconnect dst port is invalid (%d)\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ return icc_get(src_id, dst_id);
+}
+EXPORT_SYMBOL_GPL(of_icc_get);
+
/**
* icc_set() - set constraints on an interconnect path between two endpoints
* @path: reference to the path returned by icc_get()
diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h
index 5a7cf72b76a5..996c48ea67d5 100644
--- a/include/linux/interconnect.h
+++ b/include/linux/interconnect.h
@@ -16,6 +16,7 @@ struct device;
#if IS_ENABLED(CONFIG_INTERCONNECT)

struct icc_path *icc_get(const int src_id, const int dst_id);
+struct icc_path *of_icc_get(struct device *dev, const char *name);
void icc_put(struct icc_path *path);
int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw);

@@ -26,6 +27,11 @@ static inline struct icc_path *icc_get(const int src_id, const int dst_id)
return NULL;
}

+static inline struct icc_path *of_icc_get(struct device *dev, const char *name)
+{
+ return NULL;
+}
+
static inline void icc_put(struct icc_path *path)
{
}

2018-03-09 21:13:07

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 2/7] dt-bindings: Introduce interconnect provider bindings

This binding is intended to represent the interconnect hardware present
in some of the modern SoCs. Currently it consists only of a binding for
the interconnect hardware devices (provider).

Signed-off-by: Georgi Djakov <[email protected]>
---
.../bindings/interconnect/interconnect.txt | 47 ++++++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interconnect/interconnect.txt

diff --git a/Documentation/devicetree/bindings/interconnect/interconnect.txt b/Documentation/devicetree/bindings/interconnect/interconnect.txt
new file mode 100644
index 000000000000..70612bb201e4
--- /dev/null
+++ b/Documentation/devicetree/bindings/interconnect/interconnect.txt
@@ -0,0 +1,47 @@
+Interconnect Provider Device Tree Bindings
+=========================================
+
+The purpose of this document is to define a common set of generic interconnect
+providers/consumers properties.
+
+
+= interconnect providers =
+
+The interconnect provider binding is intended to represent the interconnect
+controllers in the system. Each provider registers a set of interconnect
+nodes, which expose the interconnect related capabilities of the interconnect
+to consumer drivers. These capabilities can be throughput, latency, priority
+etc. The consumer drivers set constraints on interconnect path (or endpoints)
+depending on the usecase. Interconnect providers can also be interconnect
+consumers, such as in the case where two network-on-chip fabrics interface
+directly
+
+Required properties:
+- compatible : contains the interconnect provider vendor specific compatible
+ string
+- reg : register space of the interconnect controller hardware
+
+Examples:
+
+ snoc: snoc@580000 {
+ compatible = "qcom,msm8916-snoc";
+ reg = <0x580000 0x14000>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&rpmcc RPM_SMD_SNOC_CLK>, <&rpmcc RPM_SMD_SNOC_A_CLK>;
+ status = "okay";
+ };
+ bimc: bimc@400000 {
+ compatible = "qcom,msm8916-bimc";
+ reg = <0x400000 0x62000>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&rpmcc RPM_SMD_BIMC_CLK>, <&rpmcc RPM_SMD_BIMC_A_CLK>;
+ status = "okay";
+ };
+ pnoc: pnoc@500000 {
+ compatible = "qcom,msm8916-pnoc";
+ reg = <0x500000 0x11000>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&rpmcc RPM_SMD_PCNOC_CLK>, <&rpmcc RPM_SMD_PCNOC_A_CLK>;
+ status = "okay";
+ };
+

2018-03-09 21:13:29

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 3/7] interconnect: Add debugfs support

Add a functionality to provide information about the current constraints
per each node and provider.

Signed-off-by: Georgi Djakov <[email protected]>
---
drivers/interconnect/core.c | 70 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)

diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index 6306e258b9b9..a06f752a6aaa 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -6,6 +6,7 @@
* Author: Georgi Djakov <[email protected]>
*/

+#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/init.h>
@@ -15,11 +16,13 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>

static DEFINE_IDR(icc_idr);
static LIST_HEAD(icc_provider_list);
static DEFINE_MUTEX(icc_provider_list_mutex);
static DEFINE_MUTEX(icc_path_mutex);
+static struct dentry *icc_debugfs_dir;

/**
* struct icc_req - constraints that are attached to each node
@@ -46,6 +49,73 @@ struct icc_path {
struct icc_req reqs[0];
};

+#ifdef CONFIG_DEBUG_FS
+
+static void icc_summary_show_one(struct seq_file *s, struct icc_node *n)
+{
+ if (!n)
+ return;
+
+ seq_printf(s, "%-30s %12d %12d\n",
+ n->name, n->avg_bw, n->peak_bw);
+}
+
+static int icc_summary_show(struct seq_file *s, void *data)
+{
+ struct icc_provider *provider;
+
+ seq_puts(s, " node avg peak\n");
+ seq_puts(s, "--------------------------------------------------------\n");
+
+ mutex_lock(&icc_provider_list_mutex);
+
+ list_for_each_entry(provider, &icc_provider_list, provider_list) {
+ struct icc_node *n;
+
+ mutex_lock(&provider->lock);
+ list_for_each_entry(n, &provider->nodes, node_list) {
+ icc_summary_show_one(s, n);
+ }
+ mutex_unlock(&provider->lock);
+ }
+
+ mutex_unlock(&icc_provider_list_mutex);
+
+ return 0;
+}
+
+static int icc_summary_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, icc_summary_show, inode->i_private);
+}
+
+static const struct file_operations icc_summary_fops = {
+ .open = icc_summary_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init icc_debugfs_init(void)
+{
+ struct dentry *file;
+
+ icc_debugfs_dir = debugfs_create_dir("interconnect", NULL);
+ if (!icc_debugfs_dir) {
+ pr_err("interconnect: error creating debugfs directory\n");
+ return -ENODEV;
+ }
+
+ file = debugfs_create_file("interconnect_summary", 0444,
+ icc_debugfs_dir, NULL, &icc_summary_fops);
+ if (!file)
+ return -ENODEV;
+
+ return 0;
+}
+late_initcall(icc_debugfs_init);
+#endif
+
static struct icc_node *node_find(const int id)
{
struct icc_node *node;

2018-03-09 21:13:48

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 4/7] interconnect: qcom: Add RPM communication

On some Qualcomm SoCs, there is a remote processor, which controls some of
the Network-On-Chip interconnect resources. Other CPUs express their needs
by communicating with this processor. Add a driver to handle comminication
with this remote processor.

Signed-off-by: Georgi Djakov <[email protected]>
---
.../devicetree/bindings/interconnect/qcom-smd.txt | 31 ++++++++
drivers/interconnect/qcom/Makefile | 1 +
drivers/interconnect/qcom/smd-rpm.c | 90 ++++++++++++++++++++++
drivers/interconnect/qcom/smd-rpm.h | 15 ++++
4 files changed, 137 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interconnect/qcom-smd.txt
create mode 100644 drivers/interconnect/qcom/Makefile
create mode 100644 drivers/interconnect/qcom/smd-rpm.c
create mode 100644 drivers/interconnect/qcom/smd-rpm.h

diff --git a/Documentation/devicetree/bindings/interconnect/qcom-smd.txt b/Documentation/devicetree/bindings/interconnect/qcom-smd.txt
new file mode 100644
index 000000000000..14e83ed7019b
--- /dev/null
+++ b/Documentation/devicetree/bindings/interconnect/qcom-smd.txt
@@ -0,0 +1,31 @@
+Qualcomm SMD-RPM interconnect driver binding
+------------------------------------------------
+The RPM is a dedicated hardware engine for managing the shared
+SoC resources in order to keep the lowest power profile. It
+communicates with other hardware subsystems via shared memory
+and accepts requests for various resources.
+
+Required properties :
+- compatible : shall contain only one of the following:
+ "qcom,interconnect-smd-rpm"
+
+Example:
+ smd {
+ compatible = "qcom,smd";
+
+ rpm {
+ interrupts = <0 168 1>;
+ qcom,ipc = <&apcs 8 0>;
+ qcom,smd-edge = <15>;
+
+ rpm_requests {
+ compatible = "qcom,rpm-msm8916";
+ qcom,smd-channels = "rpm_requests";
+
+ interconnect-smd-rpm {
+ compatible = "qcom,interconnect-smd-rpm";
+ };
+
+ };
+ };
+ };
diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
new file mode 100644
index 000000000000..095bdef1ee6e
--- /dev/null
+++ b/drivers/interconnect/qcom/Makefile
@@ -0,0 +1 @@
+obj-y += smd-rpm.o
diff --git a/drivers/interconnect/qcom/smd-rpm.c b/drivers/interconnect/qcom/smd-rpm.c
new file mode 100644
index 000000000000..0cf772f51642
--- /dev/null
+++ b/drivers/interconnect/qcom/smd-rpm.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RPM over SMD communication wrapper for interconects
+ *
+ * Copyright (C) 2018 Linaro Ltd
+ * Author: Georgi Djakov <[email protected]>
+ */
+
+#include <linux/interconnect-provider.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/smd-rpm.h>
+#include "smd-rpm.h"
+
+#define RPM_KEY_BW 0x00007762
+
+static struct qcom_icc_rpm {
+ struct qcom_smd_rpm *rpm;
+} icc_rpm_smd;
+
+struct icc_rpm_smd_req {
+ __le32 key;
+ __le32 nbytes;
+ __le32 value;
+};
+
+bool qcom_icc_rpm_smd_available(void)
+{
+ if (!icc_rpm_smd.rpm)
+ return false;
+
+ return true;
+}
+
+int qcom_icc_rpm_smd_send(int ctx, int rsc_type, int id, u32 val)
+{
+ struct icc_rpm_smd_req req = {
+ .key = cpu_to_le32(RPM_KEY_BW),
+ .nbytes = cpu_to_le32(sizeof(u32)),
+ .value = cpu_to_le32(val),
+ };
+
+ return qcom_rpm_smd_write(icc_rpm_smd.rpm, ctx, rsc_type, id, &req,
+ sizeof(req));
+}
+EXPORT_SYMBOL(qcom_icc_rpm_smd_send);
+
+static int qcom_icc_rpm_smd_probe(struct platform_device *pdev)
+{
+ icc_rpm_smd.rpm = dev_get_drvdata(pdev->dev.parent);
+ if (!icc_rpm_smd.rpm) {
+ dev_err(&pdev->dev, "unable to retrieve handle to RPM\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id qcom_icc_rpm_smd_dt_match[] = {
+ { .compatible = "qcom,interconnect-smd-rpm", },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, qcom_interconnect_rpm_smd_dt_match);
+
+static struct platform_driver qcom_interconnect_rpm_smd_driver = {
+ .driver = {
+ .name = "qcom-interconnect-smd-rpm",
+ .of_match_table = qcom_icc_rpm_smd_dt_match,
+ },
+ .probe = qcom_icc_rpm_smd_probe,
+};
+
+static int __init rpm_smd_interconnect_init(void)
+{
+ return platform_driver_register(&qcom_interconnect_rpm_smd_driver);
+}
+subsys_initcall(rpm_smd_interconnect_init);
+
+static void __exit rpm_smd_interconnect_exit(void)
+{
+ platform_driver_unregister(&qcom_interconnect_rpm_smd_driver);
+}
+module_exit(rpm_smd_interconnect_exit)
+
+MODULE_AUTHOR("Georgi Djakov <[email protected]>");
+MODULE_DESCRIPTION("Qualcomm SMD RPM interconnect driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/interconnect/qcom/smd-rpm.h b/drivers/interconnect/qcom/smd-rpm.h
new file mode 100644
index 000000000000..0f4a3da31cf6
--- /dev/null
+++ b/drivers/interconnect/qcom/smd-rpm.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018, Linaro Ltd.
+ * Author: Georgi Djakov <[email protected]>
+ */
+
+#ifndef __LINUX_INTERCONNECT_QCOM_RPM_H
+#define __LINUX_INTERCONNECT_QCOM_RPM_H
+
+#include <linux/soc/qcom/smd-rpm.h>
+
+bool qcom_icc_rpm_smd_available(void);
+int qcom_icc_rpm_smd_send(int ctx, int rsc_type, int id, u32 val);
+
+#endif

2018-03-09 21:14:00

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 5/7] interconnect: qcom: Add msm8916 interconnect provider driver

Add driver for the Qualcomm interconnect buses found in msm8916 based
platforms.

Signed-off-by: Georgi Djakov <[email protected]>
---
drivers/interconnect/Kconfig | 5 +
drivers/interconnect/Makefile | 1 +
drivers/interconnect/qcom/Kconfig | 11 +
drivers/interconnect/qcom/Makefile | 2 +
drivers/interconnect/qcom/msm8916.c | 482 ++++++++++++++++++++++++++++++++++++
include/linux/interconnect/qcom.h | 350 ++++++++++++++++++++++++++
6 files changed, 851 insertions(+)
create mode 100644 drivers/interconnect/qcom/Kconfig
create mode 100644 drivers/interconnect/qcom/msm8916.c
create mode 100644 include/linux/interconnect/qcom.h

diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
index a261c7d41deb..07a8276fa35a 100644
--- a/drivers/interconnect/Kconfig
+++ b/drivers/interconnect/Kconfig
@@ -8,3 +8,8 @@ menuconfig INTERCONNECT

If unsure, say no.

+if INTERCONNECT
+
+source "drivers/interconnect/qcom/Kconfig"
+
+endif
diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
index 5edf0ae80818..5971b811c2d7 100644
--- a/drivers/interconnect/Makefile
+++ b/drivers/interconnect/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_INTERCONNECT) += core.o
+obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/
diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
new file mode 100644
index 000000000000..86465dc37bd4
--- /dev/null
+++ b/drivers/interconnect/qcom/Kconfig
@@ -0,0 +1,11 @@
+config INTERCONNECT_QCOM
+ bool "Qualcomm Network-on-Chip interconnect drivers"
+ depends on INTERCONNECT
+ depends on ARCH_QCOM || COMPILE_TEST
+ default y
+
+config INTERCONNECT_QCOM_MSM8916
+ tristate "Qualcomm MSM8916 interconnect driver"
+ depends on INTERCONNECT_QCOM
+ help
+ This is a driver for the Qualcomm Network-on-Chip on msm8916-based platforms.
diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
index 095bdef1ee6e..a0c13a25e8db 100644
--- a/drivers/interconnect/qcom/Makefile
+++ b/drivers/interconnect/qcom/Makefile
@@ -1 +1,3 @@
obj-y += smd-rpm.o
+
+obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += msm8916.o
diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c
new file mode 100644
index 000000000000..d5b54f8261c8
--- /dev/null
+++ b/drivers/interconnect/qcom/msm8916.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Linaro Ltd
+ * Author: Georgi Djakov <[email protected]>
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/interconnect-provider.h>
+#include <linux/interconnect/qcom.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "smd-rpm.h"
+
+#define RPM_MASTER_FIELD_BW 0x00007762
+#define RPM_BUS_MASTER_REQ 0x73616d62
+#define RPM_BUS_SLAVE_REQ 0x766c7362
+
+#define to_qcom_provider(_provider) \
+ container_of(_provider, struct qcom_icc_provider, provider)
+
+#define DEFINE_QNODE(_name, _id, _port, _buswidth, _ap_owned, \
+ _mas_rpm_id, _slv_rpm_id, _qos_mode, \
+ _numlinks, ...) \
+ static struct qcom_icc_node _name = { \
+ .id = _id, \
+ .name = #_name, \
+ .port = _port, \
+ .buswidth = _buswidth, \
+ .qos_mode = _qos_mode, \
+ .ap_owned = _ap_owned, \
+ .mas_rpm_id = _mas_rpm_id, \
+ .slv_rpm_id = _slv_rpm_id, \
+ .num_links = _numlinks, \
+ .links = { __VA_ARGS__ }, \
+ }
+
+enum qcom_qos_mode {
+ QCOM_QOS_MODE_BYPASS = 0,
+ QCOM_QOS_MODE_FIXED,
+ QCOM_QOS_MODE_MAX,
+};
+
+struct qcom_icc_provider {
+ struct icc_provider provider;
+ void __iomem *base;
+ struct clk *bus_clk;
+ struct clk *bus_a_clk;
+};
+
+#define MSM8916_MAX_LINKS 8
+
+/**
+ * struct qcom_icc_node - Qualcomm specific interconnect nodes
+ * @name: the node name used in debugfs
+ * @links: an array of nodes where we can go next while traversing
+ * @id: a unique node identifier
+ * @num_links: the total number of @links
+ * @port: the offset index into the masters QoS register space
+ * @buswidth: width of the interconnect between a node and the bus
+ * @ap_owned: the AP CPU does the writing to QoS registers
+ * @rpm: reference to the RPM SMD driver
+ * @qos_mode: QoS mode for ap_owned resources
+ * @mas_rpm_id: RPM id for devices that are bus masters
+ * @slv_rpm_id: RPM id for devices that are bus slaves
+ * @rate: current bus clock rate in Hz
+ */
+struct qcom_icc_node {
+ unsigned char *name;
+ u16 links[MSM8916_MAX_LINKS];
+ u16 id;
+ u16 num_links;
+ u16 port;
+ u16 buswidth;
+ bool ap_owned;
+ struct qcom_smd_rpm *rpm;
+ enum qcom_qos_mode qos_mode;
+ int mas_rpm_id;
+ int slv_rpm_id;
+ u64 rate;
+};
+
+struct qcom_icc_desc {
+ struct qcom_icc_node **nodes;
+ size_t num_nodes;
+};
+
+DEFINE_QNODE(mas_video, 63, 8, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
+DEFINE_QNODE(mas_jpeg, 62, 6, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
+DEFINE_QNODE(mas_vfe, 29, 9, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10001, 10002);
+DEFINE_QNODE(mas_mdp, 22, 7, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
+DEFINE_QNODE(mas_qdss_bam, 53, 11, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10009);
+DEFINE_QNODE(mas_snoc_cfg, 54, 0, 16, 0, 20, -1, QCOM_QOS_MODE_BYPASS, 1, 10009);
+DEFINE_QNODE(mas_qdss_etr, 60, 10, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10009);
+DEFINE_QNODE(mm_int_0, 10000, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10003);
+DEFINE_QNODE(mm_int_1, 10001, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10003);
+DEFINE_QNODE(mm_int_2, 10002, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10004);
+DEFINE_QNODE(mm_int_bimc, 10003, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10008);
+DEFINE_QNODE(snoc_int_0, 10004, 0, 8, 0, 99, 130, QCOM_QOS_MODE_FIXED, 3, 588, 519, 10027);
+DEFINE_QNODE(snoc_int_1, 10005, 0, 8, 0, 100, 131, QCOM_QOS_MODE_FIXED, 3, 517, 663, 664);
+DEFINE_QNODE(snoc_int_bimc, 10006, 0, 8, 0, 101, 132, QCOM_QOS_MODE_FIXED, 1, 10007);
+DEFINE_QNODE(snoc_bimc_0_mas, 10007, 0, 8, 0, 3, -1, QCOM_QOS_MODE_FIXED, 1, 10025);
+DEFINE_QNODE(snoc_bimc_1_mas, 10008, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10026);
+DEFINE_QNODE(qdss_int, 10009, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 2, 10004, 10006);
+DEFINE_QNODE(bimc_snoc_slv, 10017, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 2, 10004, 10005);
+DEFINE_QNODE(snoc_pnoc_mas, 10027, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10028);
+DEFINE_QNODE(pnoc_snoc_slv, 10011, 0, 8, 0, -1, 45, QCOM_QOS_MODE_FIXED, 3, 10004, 10006, 10005);
+DEFINE_QNODE(slv_srvc_snoc, 587, 0, 8, 0, -1, 29, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_qdss_stm, 588, 0, 4, 0, -1, 30, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_imem, 519, 0, 8, 0, -1, 26, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_apss, 517, 0, 4, 0, -1, 20, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_cats_0, 663, 0, 16, 0, -1, 106, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_cats_1, 664, 0, 8, 0, -1, 107, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(mas_apss, 1, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
+DEFINE_QNODE(mas_tcu0, 104, 5, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
+DEFINE_QNODE(mas_tcu1, 105, 6, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
+DEFINE_QNODE(mas_gfx, 26, 2, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
+DEFINE_QNODE(bimc_snoc_mas, 10016, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10017);
+DEFINE_QNODE(snoc_bimc_0_slv, 10025, 0, 8, 0, -1, 24, QCOM_QOS_MODE_FIXED, 1, 512);
+DEFINE_QNODE(snoc_bimc_1_slv, 10026, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 512);
+DEFINE_QNODE(slv_ebi_ch0, 512, 0, 8, 0, -1, 0, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_apps_l2, 514, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(snoc_pnoc_slv, 10028, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10012);
+DEFINE_QNODE(pnoc_int_0, 10012, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 8, 10010, 10018, 10019, 10020, 10021, 10022, 10023, 10024);
+DEFINE_QNODE(pnoc_int_1, 10013, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10010);
+DEFINE_QNODE(pnoc_m_0, 10014, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10012);
+DEFINE_QNODE(pnoc_m_1, 10015, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10010);
+DEFINE_QNODE(pnoc_s_0, 10018, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 620, 624, 579, 622, 521);
+DEFINE_QNODE(pnoc_s_1, 10019, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 627, 625, 535, 577, 618);
+DEFINE_QNODE(pnoc_s_2, 10020, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 533, 630, 629, 641, 632);
+DEFINE_QNODE(pnoc_s_3, 10021, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 536, 647, 636, 635, 634);
+DEFINE_QNODE(pnoc_s_4, 10022, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 596, 589, 590);
+DEFINE_QNODE(pnoc_s_8, 10023, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 614, 606, 613);
+DEFINE_QNODE(pnoc_s_9, 10024, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 609, 522, 598);
+DEFINE_QNODE(slv_imem_cfg, 627, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_crypto_0_cfg, 625, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_msg_ram, 535, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_pdm, 577, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_prng, 618, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_clk_ctl, 620, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_mss, 521, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_tlmm, 624, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_tcsr, 579, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_security, 622, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_spdm, 533, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_pnoc_cfg, 641, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_pmic_arb, 632, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_bimc_cfg, 629, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_boot_rom, 630, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_mpm, 536, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_qdss_cfg, 635, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_rbcpr_cfg, 636, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_snoc_cfg, 647, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_dehr_cfg, 634, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_venus_cfg, 596, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_display_cfg, 590, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_camera_cfg, 589, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_usb_hs, 614, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_sdcc_1, 606, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_blsp_1, 613, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_sdcc_2, 609, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_gfx_cfg, 598, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(slv_audio, 522, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
+DEFINE_QNODE(mas_blsp_1, 86, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10015);
+DEFINE_QNODE(mas_spdm, 36, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
+DEFINE_QNODE(mas_dehr, 75, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
+DEFINE_QNODE(mas_audio, 15, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
+DEFINE_QNODE(mas_usb_hs, 87, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10015);
+DEFINE_QNODE(mas_pnoc_crypto_0, 55, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
+DEFINE_QNODE(mas_pnoc_sdcc_1, 78, 7, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
+DEFINE_QNODE(mas_pnoc_sdcc_2, 81, 8, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
+DEFINE_QNODE(pnoc_snoc_mas, 10010, 0, 8, 0, 29, -1, QCOM_QOS_MODE_FIXED, 1, 10011);
+
+static struct qcom_icc_node *msm8916_snoc_nodes[] = {
+ &mas_video,
+ &mas_jpeg,
+ &mas_vfe,
+ &mas_mdp,
+ &mas_qdss_bam,
+ &mas_snoc_cfg,
+ &mas_qdss_etr,
+ &mm_int_0,
+ &mm_int_1,
+ &mm_int_2,
+ &mm_int_bimc,
+ &snoc_int_0,
+ &snoc_int_1,
+ &snoc_int_bimc,
+ &snoc_bimc_0_mas,
+ &snoc_bimc_1_mas,
+ &qdss_int,
+ &bimc_snoc_slv,
+ &snoc_pnoc_mas,
+ &pnoc_snoc_slv,
+ &slv_srvc_snoc,
+ &slv_qdss_stm,
+ &slv_imem,
+ &slv_apss,
+ &slv_cats_0,
+ &slv_cats_1,
+};
+
+static struct qcom_icc_desc msm8916_snoc = {
+ .nodes = msm8916_snoc_nodes,
+ .num_nodes = ARRAY_SIZE(msm8916_snoc_nodes),
+};
+
+static struct qcom_icc_node *msm8916_bimc_nodes[] = {
+ &mas_apss,
+ &mas_tcu0,
+ &mas_tcu1,
+ &mas_gfx,
+ &bimc_snoc_mas,
+ &snoc_bimc_0_slv,
+ &snoc_bimc_1_slv,
+ &slv_ebi_ch0,
+ &slv_apps_l2,
+};
+
+static struct qcom_icc_desc msm8916_bimc = {
+ .nodes = msm8916_bimc_nodes,
+ .num_nodes = ARRAY_SIZE(msm8916_bimc_nodes),
+};
+
+static struct qcom_icc_node *msm8916_pnoc_nodes[] = {
+ &snoc_pnoc_slv,
+ &pnoc_int_0,
+ &pnoc_int_1,
+ &pnoc_m_0,
+ &pnoc_m_1,
+ &pnoc_s_0,
+ &pnoc_s_1,
+ &pnoc_s_2,
+ &pnoc_s_3,
+ &pnoc_s_4,
+ &pnoc_s_8,
+ &pnoc_s_9,
+ &slv_imem_cfg,
+ &slv_crypto_0_cfg,
+ &slv_msg_ram,
+ &slv_pdm,
+ &slv_prng,
+ &slv_clk_ctl,
+ &slv_mss,
+ &slv_tlmm,
+ &slv_tcsr,
+ &slv_security,
+ &slv_spdm,
+ &slv_pnoc_cfg,
+ &slv_pmic_arb,
+ &slv_bimc_cfg,
+ &slv_boot_rom,
+ &slv_mpm,
+ &slv_qdss_cfg,
+ &slv_rbcpr_cfg,
+ &slv_snoc_cfg,
+ &slv_dehr_cfg,
+ &slv_venus_cfg,
+ &slv_display_cfg,
+ &slv_camera_cfg,
+ &slv_usb_hs,
+ &slv_sdcc_1,
+ &slv_blsp_1,
+ &slv_sdcc_2,
+ &slv_gfx_cfg,
+ &slv_audio,
+ &mas_blsp_1,
+ &mas_spdm,
+ &mas_dehr,
+ &mas_audio,
+ &mas_usb_hs,
+ &mas_pnoc_crypto_0,
+ &mas_pnoc_sdcc_1,
+ &mas_pnoc_sdcc_2,
+ &pnoc_snoc_mas,
+};
+
+static struct qcom_icc_desc msm8916_pnoc = {
+ .nodes = msm8916_pnoc_nodes,
+ .num_nodes = ARRAY_SIZE(msm8916_pnoc_nodes),
+};
+
+static int qcom_icc_init(struct icc_node *node)
+{
+ struct qcom_icc_provider *qp = to_qcom_provider(node->provider);
+ /* TODO: init qos and priority */
+
+ clk_prepare_enable(qp->bus_clk);
+ clk_prepare_enable(qp->bus_a_clk);
+
+ return 0;
+}
+
+static int qcom_icc_set(struct icc_node *src, struct icc_node *dst,
+ u32 avg, u32 peak)
+{
+ struct qcom_icc_provider *qp;
+ struct qcom_icc_node *qn;
+ struct icc_node *node;
+ struct icc_provider *provider;
+ u64 avg_bw;
+ u64 peak_bw;
+ u64 rate = 0;
+ int ret = 0;
+
+ if (!src)
+ node = dst;
+ else
+ node = src;
+
+ qn = node->data;
+ provider = node->provider;
+ qp = to_qcom_provider(node->provider);
+
+ /* convert from kbps to bps */
+ avg_bw = avg * 1000ULL;
+ peak_bw = peak * 1000ULL;
+
+ /* set bandwidth */
+ if (qn->ap_owned) {
+ /* TODO: set QoS */
+ } else {
+ /* send message to the RPM processor */
+ if (qn->mas_rpm_id != -1) {
+ ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
+ RPM_BUS_MASTER_REQ,
+ qn->mas_rpm_id,
+ avg_bw);
+ }
+
+ if (qn->slv_rpm_id != -1) {
+ ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
+ RPM_BUS_SLAVE_REQ,
+ qn->slv_rpm_id,
+ avg_bw);
+ }
+ }
+
+ rate = max(avg_bw, peak_bw);
+
+ do_div(rate, qn->buswidth);
+
+ if (qn->rate != rate) {
+ ret = clk_set_rate(qp->bus_clk, rate);
+ if (ret) {
+ pr_err("set clk rate %lld error %d\n", rate, ret);
+ return ret;
+ }
+
+ ret = clk_set_rate(qp->bus_a_clk, rate);
+ if (ret) {
+ pr_err("set clk rate %lld error %d\n", rate, ret);
+ return ret;
+ }
+
+ qn->rate = rate;
+ }
+
+ return ret;
+}
+
+static int qnoc_probe(struct platform_device *pdev)
+{
+ const struct qcom_icc_desc *desc;
+ struct qcom_icc_node **qnodes;
+ struct qcom_icc_provider *qp;
+ struct resource *res;
+ struct icc_provider *provider;
+ size_t num_nodes, i;
+ int ret;
+
+ /* wait for RPM */
+ if (!qcom_icc_rpm_smd_available())
+ return -EPROBE_DEFER;
+
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ qnodes = desc->nodes;
+ num_nodes = desc->num_nodes;
+
+ qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
+ if (!qp)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ qp->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(qp->base))
+ return PTR_ERR(qp->base);
+
+ qp->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
+ if (IS_ERR(qp->bus_clk))
+ return PTR_ERR(qp->bus_clk);
+
+ qp->bus_a_clk = devm_clk_get(&pdev->dev, "bus_a_clk");
+ if (IS_ERR(qp->bus_a_clk))
+ return PTR_ERR(qp->bus_a_clk);
+
+ provider = &qp->provider;
+ provider->dev = &pdev->dev;
+ provider->set = &qcom_icc_set;
+ INIT_LIST_HEAD(&provider->nodes);
+ provider->data = qp;
+
+ ret = icc_add_provider(provider);
+ if (ret) {
+ dev_err(&pdev->dev, "error adding interconnect provider\n");
+ return ret;
+ }
+
+ for (i = 0; i < num_nodes; i++) {
+ struct icc_node *node;
+ int ret;
+ size_t j;
+
+ node = icc_node_create(qnodes[i]->id);
+ if (IS_ERR(node)) {
+ ret = PTR_ERR(node);
+ goto err;
+ }
+
+ node->name = qnodes[i]->name;
+ node->data = qnodes[i];
+ icc_node_add(node, provider);
+
+ dev_dbg(&pdev->dev, "registered node %p %s %d\n", node,
+ qnodes[i]->name, node->id);
+
+ /* populate links */
+ for (j = 0; j < qnodes[i]->num_links; j++)
+ if (qnodes[i]->links[j])
+ icc_link_create(node, qnodes[i]->links[j]);
+
+ ret = qcom_icc_init(node);
+ if (ret)
+ dev_err(&pdev->dev, "%s init error (%d)\n", node->name,
+ ret);
+ }
+
+ platform_set_drvdata(pdev, provider);
+
+ return ret;
+err:
+ icc_del_provider(provider);
+ return ret;
+}
+
+static int qnoc_remove(struct platform_device *pdev)
+{
+ struct icc_provider *provider = platform_get_drvdata(pdev);
+
+ icc_del_provider(provider);
+
+ return 0;
+}
+
+static const struct of_device_id qnoc_of_match[] = {
+ { .compatible = "qcom,msm8916-pnoc", .data = &msm8916_pnoc },
+ { .compatible = "qcom,msm8916-snoc", .data = &msm8916_snoc },
+ { .compatible = "qcom,msm8916-bimc", .data = &msm8916_bimc },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qnoc_of_match);
+
+static struct platform_driver qnoc_driver = {
+ .probe = qnoc_probe,
+ .remove = qnoc_remove,
+ .driver = {
+ .name = "qnoc-msm8916",
+ .of_match_table = qnoc_of_match,
+ },
+};
+module_platform_driver(qnoc_driver);
+MODULE_AUTHOR("Georgi Djakov <[email protected]>");
+MODULE_DESCRIPTION("Qualcomm msm8916 NoC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/interconnect/qcom.h b/include/linux/interconnect/qcom.h
new file mode 100644
index 000000000000..2cd378d2f575
--- /dev/null
+++ b/include/linux/interconnect/qcom.h
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Qualcomm interconnect IDs
+ *
+ * Copyright (c) 2018, Linaro Ltd.
+ * Author: Georgi Djakov <[email protected]>
+ */
+
+#ifndef __QCOM_INTERCONNECT_IDS_H
+#define __QCOM_INTERCONNECT_IDS_H
+
+#define FAB_BIMC 0
+#define FAB_SYS_NOC 1024
+#define FAB_MMSS_NOC 2048
+#define FAB_OCMEM_NOC 3072
+#define FAB_PERIPH_NOC 4096
+#define FAB_CONFIG_NOC 5120
+#define FAB_OCMEM_VNOC 6144
+
+#define MASTER_AMPSS_M0 1
+#define MASTER_AMPSS_M1 2
+#define APPSS_MASTER_FAB_MMSS 3
+#define APPSS_MASTER_FAB_SYSTEM 4
+#define SYSTEM_MASTER_FAB_APPSS 5
+#define MASTER_SPS 6
+#define MASTER_ADM_PORT0 7
+#define MASTER_ADM_PORT1 8
+#define SYSTEM_MASTER_ADM1_PORT0 9
+#define MASTER_ADM1_PORT1 10
+#define MASTER_LPASS_PROC 11
+#define MASTER_MSS_PROCI 12
+#define MASTER_MSS_PROCD 13
+#define MASTER_MSS_MDM_PORT0 14
+#define MASTER_LPASS 15
+#define SYSTEM_MASTER_CPSS_FPB 16
+#define SYSTEM_MASTER_SYSTEM_FPB 17
+#define SYSTEM_MASTER_MMSS_FPB 18
+#define MASTER_ADM1_CI 19
+#define MASTER_ADM0_CI 20
+#define MASTER_MSS_MDM_PORT1 21
+#define MASTER_MDP_PORT0 22
+#define MASTER_MDP_PORT1 23
+#define MMSS_MASTER_ADM1_PORT0 24
+#define MASTER_ROTATOR 25
+#define MASTER_GRAPHICS_3D 26
+#define MASTER_JPEG_DEC 27
+#define MASTER_GRAPHICS_2D_CORE0 28
+#define MASTER_VFE 29
+#define MASTER_VPE 30
+#define MASTER_JPEG_ENC 31
+#define MASTER_GRAPHICS_2D_CORE1 32
+#define MMSS_MASTER_APPS_FAB 33
+#define MASTER_HD_CODEC_PORT0 34
+#define MASTER_HD_CODEC_PORT1 35
+#define MASTER_SPDM 36
+#define MASTER_RPM 37
+#define MASTER_MSS 38
+#define MASTER_RIVA 39
+#define SYSTEM_MASTER_UNUSED_6 40
+#define MASTER_MSS_SW_PROC 41
+#define MASTER_MSS_FW_PROC 42
+#define MMSS_MASTER_UNUSED_2 43
+#define MASTER_GSS_NAV 44
+#define MASTER_PCIE 45
+#define MASTER_SATA 46
+#define MASTER_CRYPTO 47
+#define MASTER_VIDEO_CAP 48
+#define MASTER_GRAPHICS_3D_PORT1 49
+#define MASTER_VIDEO_ENC 50
+#define MASTER_VIDEO_DEC 51
+#define MASTER_LPASS_AHB 52
+#define MASTER_QDSS_BAM 53
+#define MASTER_SNOC_CFG 54
+#define MASTER_CRYPTO_CORE0 55
+#define MASTER_CRYPTO_CORE1 56
+#define MASTER_MSS_NAV 57
+#define MASTER_OCMEM_DMA 58
+#define MASTER_WCSS 59
+#define MASTER_QDSS_ETR 60
+#define MASTER_USB3 61
+#define MASTER_JPEG 62
+#define MASTER_VIDEO_P0 63
+#define MASTER_VIDEO_P1 64
+#define MASTER_MSS_PROC 65
+#define MASTER_JPEG_OCMEM 66
+#define MASTER_MDP_OCMEM 67
+#define MASTER_VIDEO_P0_OCMEM 68
+#define MASTER_VIDEO_P1_OCMEM 69
+#define MASTER_VFE_OCMEM 70
+#define MASTER_CNOC_ONOC_CFG 71
+#define MASTER_RPM_INST 72
+#define MASTER_RPM_DATA 73
+#define MASTER_RPM_SYS 74
+#define MASTER_DEHR 75
+#define MASTER_QDSS_DAP 76
+#define MASTER_TIC 77
+#define MASTER_SDCC_1 78
+#define MASTER_SDCC_3 79
+#define MASTER_SDCC_4 80
+#define MASTER_SDCC_2 81
+#define MASTER_TSIF 82
+#define MASTER_BAM_DMA 83
+#define MASTER_BLSP_2 84
+#define MASTER_USB_HSIC 85
+#define MASTER_BLSP_1 86
+#define MASTER_USB_HS 87
+#define MASTER_PNOC_CFG 88
+#define MASTER_V_OCMEM_GFX3D 89
+#define MASTER_IPA 90
+#define MASTER_QPIC 91
+#define MASTER_MDPE 92
+#define MASTER_USB_HS2 93
+#define MASTER_VPU 94
+#define MASTER_UFS 95
+#define MASTER_BCAST 96
+#define MASTER_CRYPTO_CORE2 97
+#define MASTER_EMAC 98
+#define MASTER_VPU_1 99
+#define MASTER_PCIE_1 100
+#define MASTER_USB3_1 101
+#define MASTER_CNOC_MNOC_MMSS_CFG 102
+#define MASTER_CNOC_MNOC_CFG 103
+#define MASTER_TCU_0 104
+#define MASTER_TCU_1 105
+#define MASTER_CPP 106
+#define MASTER_AUDIO 107
+
+#define SNOC_MM_INT_0 10000
+#define SNOC_MM_INT_1 10001
+#define SNOC_MM_INT_2 10002
+#define SNOC_MM_INT_BIMC 10003
+#define SNOC_INT_0 10004
+#define SNOC_INT_1 10005
+#define SNOC_INT_BIMC 10006
+#define SNOC_BIMC_0_MAS 10007
+#define SNOC_BIMC_1_MAS 10008
+#define SNOC_QDSS_INT 10009
+#define PNOC_SNOC_MAS 10010
+#define PNOC_SNOC_SLV 10011
+#define PNOC_INT_0 10012
+#define PNOC_INT_1 10013
+#define PNOC_M_0 10014
+#define PNOC_M_1 10015
+#define BIMC_SNOC_MAS 10016
+#define BIMC_SNOC_SLV 10017
+#define PNOC_SLV_0 10018
+#define PNOC_SLV_1 10019
+#define PNOC_SLV_2 10020
+#define PNOC_SLV_3 10021
+#define PNOC_SLV_4 10022
+#define PNOC_SLV_8 10023
+#define PNOC_SLV_9 10024
+#define SNOC_BIMC_0_SLV 10025
+#define SNOC_BIMC_1_SLV 10026
+#define MNOC_BIMC_MAS 10027
+#define MNOC_BIMC_SLV 10028
+#define BIMC_MNOC_MAS 10029
+#define BIMC_MNOC_SLV 10030
+#define SNOC_BIMC_MAS 10031
+#define SNOC_BIMC_SLV 10032
+#define CNOC_SNOC_MAS 10033
+#define CNOC_SNOC_SLV 10034
+#define SNOC_CNOC_MAS 10035
+#define SNOC_CNOC_SLV 10036
+#define OVNOC_SNOC_MAS 10037
+#define OVNOC_SNOC_SLV 10038
+#define SNOC_OVNOC_MAS 10039
+#define SNOC_OVNOC_SLV 10040
+#define SNOC_PNOC_MAS 10041
+#define SNOC_PNOC_SLV 10042
+#define BIMC_INT_APPS_EBI 10043
+#define BIMC_INT_APPS_SNOC 10044
+#define SNOC_BIMC_2_MAS 10045
+#define SNOC_BIMC_2_SLV 10046
+#define PNOC_SLV_5 10047
+#define PNOC_SLV_7 10048
+#define PNOC_INT_2 10049
+#define PNOC_INT_3 10050
+#define PNOC_INT_4 10051
+#define PNOC_INT_5 10052
+#define PNOC_INT_6 10053
+#define PNOC_INT_7 10054
+
+#define SLAVE_EBI_CH0 512
+#define SLAVE_EBI_CH1 513
+#define SLAVE_AMPSS_L2 514
+#define APPSS_SLAVE_FAB_MMSS 515
+#define APPSS_SLAVE_FAB_SYSTEM 516
+#define SYSTEM_SLAVE_FAB_APPS 517
+#define SLAVE_SPS 518
+#define SLAVE_SYSTEM_IMEM 519
+#define SLAVE_AMPSS 520
+#define SLAVE_MSS 521
+#define SLAVE_LPASS 522
+#define SYSTEM_SLAVE_CPSS_FPB 523
+#define SYSTEM_SLAVE_SYSTEM_FPB 524
+#define SYSTEM_SLAVE_MMSS_FPB 525
+#define SLAVE_CORESIGHT 526
+#define SLAVE_RIVA 527
+#define SLAVE_SMI 528
+#define MMSS_SLAVE_FAB_APPS 529
+#define MMSS_SLAVE_FAB_APPS_1 530
+#define SLAVE_MM_IMEM 531
+#define SLAVE_CRYPTO 532
+#define SLAVE_SPDM 533
+#define SLAVE_RPM 534
+#define SLAVE_RPM_MSG_RAM 535
+#define SLAVE_MPM 536
+#define SLAVE_PMIC1_SSBI1_A 537
+#define SLAVE_PMIC1_SSBI1_B 538
+#define SLAVE_PMIC1_SSBI1_C 539
+#define SLAVE_PMIC2_SSBI2_A 540
+#define SLAVE_PMIC2_SSBI2_B 541
+#define SLAVE_GSBI1_UART 542
+#define SLAVE_GSBI2_UART 543
+#define SLAVE_GSBI3_UART 544
+#define SLAVE_GSBI4_UART 545
+#define SLAVE_GSBI5_UART 546
+#define SLAVE_GSBI6_UART 547
+#define SLAVE_GSBI7_UART 548
+#define SLAVE_GSBI8_UART 549
+#define SLAVE_GSBI9_UART 550
+#define SLAVE_GSBI10_UART 551
+#define SLAVE_GSBI11_UART 552
+#define SLAVE_GSBI12_UART 553
+#define SLAVE_GSBI1_QUP 554
+#define SLAVE_GSBI2_QUP 555
+#define SLAVE_GSBI3_QUP 556
+#define SLAVE_GSBI4_QUP 557
+#define SLAVE_GSBI5_QUP 558
+#define SLAVE_GSBI6_QUP 559
+#define SLAVE_GSBI7_QUP 560
+#define SLAVE_GSBI8_QUP 561
+#define SLAVE_GSBI9_QUP 562
+#define SLAVE_GSBI10_QUP 563
+#define SLAVE_GSBI11_QUP 564
+#define SLAVE_GSBI12_QUP 565
+#define SLAVE_EBI2_NAND 566
+#define SLAVE_EBI2_CS0 567
+#define SLAVE_EBI2_CS1 568
+#define SLAVE_EBI2_CS2 569
+#define SLAVE_EBI2_CS3 570
+#define SLAVE_EBI2_CS4 571
+#define SLAVE_EBI2_CS5 572
+#define SLAVE_USB_FS1 573
+#define SLAVE_USB_FS2 574
+#define SLAVE_TSIF 575
+#define SLAVE_MSM_TSSC 576
+#define SLAVE_MSM_PDM 577
+#define SLAVE_MSM_DIMEM 578
+#define SLAVE_MSM_TCSR 579
+#define SLAVE_MSM_PRNG 580
+#define SLAVE_GSS 581
+#define SLAVE_SATA 582
+#define SLAVE_USB3 583
+#define SLAVE_WCSS 584
+#define SLAVE_OCIMEM 585
+#define SLAVE_SNOC_OCMEM 586
+#define SLAVE_SERVICE_SNOC 587
+#define SLAVE_QDSS_STM 588
+#define SLAVE_CAMERA_CFG 589
+#define SLAVE_DISPLAY_CFG 590
+#define SLAVE_OCMEM_CFG 591
+#define SLAVE_CPR_CFG 592
+#define SLAVE_CPR_XPU_CFG 593
+#define SLAVE_MISC_CFG 594
+#define SLAVE_MISC_XPU_CFG 595
+#define SLAVE_VENUS_CFG 596
+#define SLAVE_MISC_VENUS_CFG 597
+#define SLAVE_GRAPHICS_3D_CFG 598
+#define SLAVE_MMSS_CLK_CFG 599
+#define SLAVE_MMSS_CLK_XPU_CFG 600
+#define SLAVE_MNOC_MPU_CFG 601
+#define SLAVE_ONOC_MPU_CFG 602
+#define SLAVE_SERVICE_MNOC 603
+#define SLAVE_OCMEM 604
+#define SLAVE_SERVICE_ONOC 605
+#define SLAVE_SDCC_1 606
+#define SLAVE_SDCC_3 607
+#define SLAVE_SDCC_2 608
+#define SLAVE_SDCC_4 609
+#define SLAVE_BAM_DMA 610
+#define SLAVE_BLSP_2 611
+#define SLAVE_USB_HSIC 612
+#define SLAVE_BLSP_1 613
+#define SLAVE_USB_HS 614
+#define SLAVE_PDM 615
+#define SLAVE_PERIPH_APU_CFG 616
+#define SLAVE_PNOC_MPU_CFG 617
+#define SLAVE_PRNG 618
+#define SLAVE_SERVICE_PNOC 619
+#define SLAVE_CLK_CTL 620
+#define SLAVE_CNOC_MSS 621
+#define SLAVE_SECURITY 622
+#define SLAVE_TCSR 623
+#define SLAVE_TLMM 624
+#define SLAVE_CRYPTO_0_CFG 625
+#define SLAVE_CRYPTO_1_CFG 626
+#define SLAVE_IMEM_CFG 627
+#define SLAVE_MESSAGE_RAM 628
+#define SLAVE_BIMC_CFG 629
+#define SLAVE_BOOT_ROM 630
+#define SLAVE_CNOC_MNOC_MMSS_CFG 631
+#define SLAVE_PMIC_ARB 632
+#define SLAVE_SPDM_WRAPPER 633
+#define SLAVE_DEHR_CFG 634
+#define SLAVE_QDSS_CFG 635
+#define SLAVE_RBCPR_CFG 636
+#define SLAVE_RBCPR_QDSS_APU_CFG 637
+#define SLAVE_SNOC_MPU_CFG 638
+#define SLAVE_CNOC_ONOC_CFG 639
+#define SLAVE_CNOC_MNOC_CFG 640
+#define SLAVE_PNOC_CFG 641
+#define SLAVE_SNOC_CFG 642
+#define SLAVE_EBI1_DLL_CFG 643
+#define SLAVE_PHY_APU_CFG 644
+#define SLAVE_EBI1_PHY_CFG 645
+#define SLAVE_SERVICE_CNOC 646
+#define SLAVE_IPS_CFG 647
+#define SLAVE_QPIC 648
+#define SLAVE_DSI_CFG 649
+#define SLAVE_UFS_CFG 650
+#define SLAVE_RBCPR_CX_CFG 651
+#define SLAVE_RBCPR_MX_CFG 652
+#define SLAVE_PCIE_CFG 653
+#define SLAVE_USB_PHYS_CFG 654
+#define SLAVE_VIDEO_CAP_CFG 655
+#define SLAVE_AVSYNC_CFG 656
+#define SLAVE_CRYPTO_2_CFG 657
+#define SLAVE_VPU_CFG 658
+#define SLAVE_BCAST_CFG 659
+#define SLAVE_KLM_CFG 660
+#define SLAVE_GENI_IR_CFG 661
+#define SLAVE_OCMEM_GFX 662
+#define SLAVE_CATS_128 663
+#define SLAVE_OCMEM_64 664
+#define SLAVE_PCIE_0 665
+#define SLAVE_PCIE_1 666
+#define SLAVE_PCIE_0_CFG 667
+#define SLAVE_PCIE_1_CFG 668
+#define SLAVE_SRVC_MNOC 669
+#define SLAVE_USB_HS2 670
+#define SLAVE_AUDIO 671
+#define SLAVE_TCU 672
+#define SLAVE_APPSS 673
+#define SLAVE_PCIE_PARF 674
+#define SLAVE_USB3_PHY_CFG 675
+#define SLAVE_IPA_CFG 676
+
+#endif

2018-03-09 21:14:03

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

This patch introduce a new API to get requirements and configure the
interconnect buses across the entire chipset to fit with the current
demand.

The API is using a consumer/provider-based model, where the providers are
the interconnect buses and the consumers could be various drivers.
The consumers request interconnect resources (path) between endpoints and
set the desired constraints on this data flow path. The providers receive
requests from consumers and aggregate these requests for all master-slave
pairs on that path. Then the providers configure each participating in the
topology node according to the requested data flow path, physical links and
constraints. The topology could be complicated and multi-tiered and is SoC
specific.

Signed-off-by: Georgi Djakov <[email protected]>
---
Documentation/interconnect/interconnect.rst | 96 ++++++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/interconnect/Kconfig | 10 +
drivers/interconnect/Makefile | 1 +
drivers/interconnect/core.c | 489 ++++++++++++++++++++++++++++
include/linux/interconnect-provider.h | 109 +++++++
include/linux/interconnect.h | 40 +++
8 files changed, 748 insertions(+)
create mode 100644 Documentation/interconnect/interconnect.rst
create mode 100644 drivers/interconnect/Kconfig
create mode 100644 drivers/interconnect/Makefile
create mode 100644 drivers/interconnect/core.c
create mode 100644 include/linux/interconnect-provider.h
create mode 100644 include/linux/interconnect.h

diff --git a/Documentation/interconnect/interconnect.rst b/Documentation/interconnect/interconnect.rst
new file mode 100644
index 000000000000..23eba68e8424
--- /dev/null
+++ b/Documentation/interconnect/interconnect.rst
@@ -0,0 +1,96 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================================
+GENERIC SYSTEM INTERCONNECT SUBSYSTEM
+=====================================
+
+Introduction
+------------
+
+This framework is designed to provide a standard kernel interface to control
+the settings of the interconnects on a SoC. These settings can be throughput,
+latency and priority between multiple interconnected devices or functional
+blocks. This can be controlled dynamically in order to save power or provide
+maximum performance.
+
+The interconnect bus is a hardware with configurable parameters, which can be
+set on a data path according to the requests received from various drivers.
+An example of interconnect buses are the interconnects between various
+components or functional blocks in chipsets. There can be multiple interconnects
+on a SoC that can be multi-tiered.
+
+Below is a simplified diagram of a real-world SoC interconnect bus topology.
+
+::
+
+ +----------------+ +----------------+
+ | HW Accelerator |--->| M NoC |<---------------+
+ +----------------+ +----------------+ |
+ | | +------------+
+ +-----+ +-------------+ V +------+ | |
+ | DDR | | +--------+ | PCIe | | |
+ +-----+ | | Slaves | +------+ | |
+ ^ ^ | +--------+ | | C NoC |
+ | | V V | |
+ +------------------+ +------------------------+ | | +-----+
+ | |-->| |-->| |-->| CPU |
+ | |-->| |<--| | +-----+
+ | Mem NoC | | S NoC | +------------+
+ | |<--| |---------+ |
+ | |<--| |<------+ | | +--------+
+ +------------------+ +------------------------+ | | +-->| Slaves |
+ ^ ^ ^ ^ ^ | | +--------+
+ | | | | | | V
+ +------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
+ | CPUs | | | GPU | | DSP | | Masters |-->| P NoC |-->| Slaves |
+ +------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
+ |
+ +-------+
+ | Modem |
+ +-------+
+
+Terminology
+-----------
+
+Interconnect provider is the software definition of the interconnect hardware.
+The interconnect providers on the above diagram are M NoC, S NoC, C NoC and Mem
+NoC.
+
+Interconnect node is the software definition of the interconnect hardware
+port. Each interconnect provider consists of multiple interconnect nodes,
+which are connected to other SoC components including other interconnect
+providers. The point on the diagram where the CPUs connects to the memory is
+called an interconnect node, which belongs to the Mem NoC interconnect provider.
+
+Interconnect endpoints are the first or the last element of the path. Every
+endpoint is a node, but not every node is an endpoint.
+
+Interconnect path is everything between two endpoints including all the nodes
+that have to be traversed to reach from a source to destination node. It may
+include multiple master-slave pairs across several interconnect providers.
+
+Interconnect consumers are the entities which make use of the data paths exposed
+by the providers. The consumers send requests to providers requesting various
+throughput, latency and priority. Usually the consumers are device drivers, that
+send request based on their needs. An example for a consumer is a video decoder
+that supports various formats and image sizes.
+
+Interconnect providers
+----------------------
+
+Interconnect provider is an entity that implements methods to initialize and
+configure a interconnect bus hardware. The interconnect provider drivers should
+be registered with the interconnect provider core.
+
+The interconnect framework provider API functions are documented in
+.. kernel-doc:: include/linux/interconnect-provider.h
+
+Interconnect consumers
+----------------------
+
+Interconnect consumers are the clients which use the interconnect APIs to
+get paths between endpoints and set their bandwidth/latency/QoS requirements
+for these interconnect paths.
+
+The interconnect framework consumer API functions are documented in
+.. kernel-doc:: include/linux/interconnect.h
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 879dc0604cba..96a1db022cee 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -219,4 +219,6 @@ source "drivers/siox/Kconfig"

source "drivers/slimbus/Kconfig"

+source "drivers/interconnect/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 24cd47014657..0cca95740d9b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -185,3 +185,4 @@ obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MULTIPLEXER) += mux/
obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/
obj-$(CONFIG_SIOX) += siox/
+obj-$(CONFIG_INTERCONNECT) += interconnect/
diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
new file mode 100644
index 000000000000..a261c7d41deb
--- /dev/null
+++ b/drivers/interconnect/Kconfig
@@ -0,0 +1,10 @@
+menuconfig INTERCONNECT
+ tristate "On-Chip Interconnect management support"
+ help
+ Support for management of the on-chip interconnects.
+
+ This framework is designed to provide a generic interface for
+ managing the interconnects in a SoC.
+
+ If unsure, say no.
+
diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
new file mode 100644
index 000000000000..5edf0ae80818
--- /dev/null
+++ b/drivers/interconnect/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_INTERCONNECT) += core.o
diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
new file mode 100644
index 000000000000..6306e258b9b9
--- /dev/null
+++ b/drivers/interconnect/core.c
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Interconnect framework core driver
+ *
+ * Copyright (c) 2018, Linaro Ltd.
+ * Author: Georgi Djakov <[email protected]>
+ */
+
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/interconnect-provider.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+static DEFINE_IDR(icc_idr);
+static LIST_HEAD(icc_provider_list);
+static DEFINE_MUTEX(icc_provider_list_mutex);
+static DEFINE_MUTEX(icc_path_mutex);
+
+/**
+ * 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
+ * @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;
+ u32 avg_bw;
+ u32 peak_bw;
+};
+
+/**
+ * struct icc_path - interconnect path structure
+ * @num_nodes: number of hops (nodes)
+ * @reqs: array of the requests applicable to this path of nodes
+ */
+struct icc_path {
+ size_t num_nodes;
+ struct icc_req reqs[0];
+};
+
+static struct icc_node *node_find(const int id)
+{
+ struct icc_node *node;
+
+ node = idr_find(&icc_idr, id);
+
+ return node;
+}
+
+static struct icc_path *path_allocate(struct icc_node *node, ssize_t num_nodes)
+{
+ struct icc_path *path;
+ size_t i;
+
+ path = kzalloc(sizeof(*path) + num_nodes * sizeof(*path->reqs),
+ GFP_KERNEL);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+
+ path->num_nodes = num_nodes;
+
+ for (i = 0; i < num_nodes; i++) {
+ hlist_add_head(&path->reqs[i].req_node, &node->req_list);
+
+ path->reqs[i].node = node;
+ /* reference to previous node was saved during path traversal */
+ node = node->reverse;
+ }
+
+ return path;
+}
+
+static struct icc_path *path_find(struct icc_node *src, struct icc_node *dst)
+{
+ struct icc_node *node = NULL;
+ struct list_head traverse_list;
+ struct list_head edge_list;
+ struct list_head tmp_list;
+ size_t i, number = 0;
+ bool found = false;
+
+ INIT_LIST_HEAD(&traverse_list);
+ INIT_LIST_HEAD(&edge_list);
+ INIT_LIST_HEAD(&tmp_list);
+
+ list_add_tail(&src->search_list, &traverse_list);
+
+ do {
+ list_for_each_entry(node, &traverse_list, search_list) {
+ if (node == dst) {
+ found = true;
+ list_add(&node->search_list, &tmp_list);
+ break;
+ }
+ for (i = 0; i < node->num_links; i++) {
+ struct icc_node *tmp = node->links[i];
+
+ if (!tmp)
+ return ERR_PTR(-ENOENT);
+
+ if (tmp->is_traversed)
+ continue;
+
+ tmp->is_traversed = true;
+ tmp->reverse = node;
+ list_add_tail(&tmp->search_list, &edge_list);
+ }
+ }
+ if (found)
+ break;
+
+ list_splice_init(&traverse_list, &tmp_list);
+ list_splice_init(&edge_list, &traverse_list);
+
+ /* count the number of nodes */
+ number++;
+
+ } while (!list_empty(&traverse_list));
+
+ /* reset the traversed state */
+ list_for_each_entry(node, &tmp_list, search_list)
+ node->is_traversed = false;
+
+ if (found)
+ return path_allocate(dst, number);
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+static int path_init(struct icc_path *path)
+{
+ struct icc_node *node;
+ size_t i;
+
+ for (i = 0; i < path->num_nodes; i++) {
+ node = path->reqs[i].node;
+
+ mutex_lock(&node->provider->lock);
+ node->provider->users++;
+ mutex_unlock(&node->provider->lock);
+ }
+
+ return 0;
+}
+
+static void node_aggregate(struct icc_node *node)
+{
+ struct icc_req *r;
+ u32 agg_avg = 0;
+ u32 agg_peak = 0;
+
+ hlist_for_each_entry(r, &node->req_list, req_node) {
+ /* sum(averages) and max(peaks) */
+ agg_avg += r->avg_bw;
+ agg_peak = max(agg_peak, r->peak_bw);
+ }
+
+ node->avg_bw = agg_avg;
+ node->peak_bw = agg_peak;
+}
+
+static void provider_aggregate(struct icc_provider *provider, u32 *avg_bw,
+ u32 *peak_bw)
+{
+ struct icc_node *n;
+ u32 agg_avg = 0;
+ u32 agg_peak = 0;
+
+ /* aggregate for the interconnect provider */
+ list_for_each_entry(n, &provider->nodes, node_list) {
+ /* sum the average and max the peak */
+ agg_avg += n->avg_bw;
+ agg_peak = max(agg_peak, n->peak_bw);
+ }
+
+ *avg_bw = agg_avg;
+ *peak_bw = agg_peak;
+}
+
+static int constraints_apply(struct icc_path *path)
+{
+ struct icc_node *next, *prev = NULL;
+ int i;
+
+ for (i = 0; i < path->num_nodes; i++, prev = next) {
+ struct icc_provider *provider;
+ u32 avg_bw = 0;
+ u32 peak_bw = 0;
+ int ret;
+
+ next = path->reqs[i].node;
+ /*
+ * Both endpoints should be valid master-slave pairs of the
+ * same interconnect provider that will be configured.
+ */
+ if (!next || !prev)
+ continue;
+
+ if (next->provider != prev->provider)
+ continue;
+
+ provider = next->provider;
+ mutex_lock(&provider->lock);
+
+ /* aggregate requests for the provider */
+ provider_aggregate(provider, &avg_bw, &peak_bw);
+
+ if (provider->set) {
+ /* set the constraints */
+ ret = provider->set(prev, next, avg_bw, peak_bw);
+ }
+
+ mutex_unlock(&provider->lock);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * icc_set() - set constraints on an interconnect path between two endpoints
+ * @path: reference to the path returned by icc_get()
+ * @avg_bw: average bandwidth in kbps
+ * @peak_bw: peak bandwidth in kbps
+ *
+ * This function is used by an interconnect consumer to express its own needs
+ * in term of bandwidth and QoS for a previously requested path between two
+ * endpoints. The requests are aggregated and each node is updated accordingly.
+ *
+ * Returns 0 on success, or an approproate error code otherwise.
+ */
+int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
+{
+ struct icc_node *node;
+ size_t i;
+ int ret;
+
+ if (!path)
+ return 0;
+
+ for (i = 0; i < path->num_nodes; i++) {
+ node = path->reqs[i].node;
+
+ mutex_lock(&icc_path_mutex);
+
+ /* update the consumer request for this path */
+ path->reqs[i].avg_bw = avg_bw;
+ path->reqs[i].peak_bw = peak_bw;
+
+ /* aggregate requests for this node */
+ node_aggregate(node);
+
+ mutex_unlock(&icc_path_mutex);
+ }
+
+ ret = constraints_apply(path);
+ if (ret)
+ pr_err("interconnect: error applying constraints (%d)", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(icc_set);
+
+/**
+ * icc_get() - return a handle for path between two endpoints
+ * @src_id: source device port id
+ * @dst_id: destination device port id
+ *
+ * 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 the they are not needed anymore.
+ *
+ * Return: icc_path pointer on success, or ERR_PTR() on error
+ */
+struct icc_path *icc_get(const int src_id, const int dst_id)
+{
+ struct icc_node *src, *dst;
+ struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
+
+ src = node_find(src_id);
+ if (!src)
+ goto out;
+
+ dst = node_find(dst_id);
+ if (!dst)
+ goto out;
+
+ mutex_lock(&icc_path_mutex);
+ path = path_find(src, dst);
+ mutex_unlock(&icc_path_mutex);
+ if (IS_ERR(path))
+ goto out;
+
+ path_init(path);
+
+out:
+ return path;
+}
+EXPORT_SYMBOL_GPL(icc_get);
+
+/**
+ * icc_put() - release the reference to the icc_path
+ * @path: 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.
+ */
+void icc_put(struct icc_path *path)
+{
+ struct icc_node *node;
+ size_t i;
+ int ret;
+
+ if (!path || WARN_ON_ONCE(IS_ERR(path)))
+ return;
+
+ ret = icc_set(path, 0, 0);
+ if (ret)
+ pr_err("%s: error (%d)\n", __func__, ret);
+
+ for (i = 0; i < path->num_nodes; i++) {
+ node = path->reqs[i].node;
+ hlist_del(&path->reqs[i].req_node);
+
+ mutex_lock(&node->provider->lock);
+ node->provider->users--;
+ mutex_unlock(&node->provider->lock);
+ }
+
+ kfree(path);
+}
+EXPORT_SYMBOL_GPL(icc_put);
+
+/**
+ * icc_node_create() - create a node
+ * @id: node id
+ *
+ * Return: icc_node pointer on success, or ERR_PTR() on error
+ */
+struct icc_node *icc_node_create(int id)
+{
+ struct icc_node *node;
+
+ /* check if node already exists */
+ node = node_find(id);
+ if (node)
+ return node;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+
+ id = idr_alloc(&icc_idr, node, id, id + 1, GFP_KERNEL);
+ if (WARN(id < 0, "couldn't get idr"))
+ return ERR_PTR(id);
+
+ node->id = id;
+
+ return node;
+}
+EXPORT_SYMBOL_GPL(icc_node_create);
+
+/**
+ * icc_link_create() - create a link between two nodes
+ * @src_id: source node id
+ * @dst_id: destination node id
+ *
+ * Return: 0 on success, or an error code otherwise
+ */
+int icc_link_create(struct icc_node *node, const int dst_id)
+{
+ struct icc_node *dst;
+ struct icc_node **new;
+ int ret = 0;
+
+ if (IS_ERR_OR_NULL(node))
+ return PTR_ERR(node);
+
+ mutex_lock(&node->provider->lock);
+
+ dst = node_find(dst_id);
+ if (!dst)
+ dst = icc_node_create(dst_id);
+
+ new = krealloc(node->links,
+ (node->num_links + 1) * sizeof(*node->links),
+ GFP_KERNEL);
+ if (!new) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ node->links = new;
+ node->links[node->num_links++] = dst;
+
+out:
+ mutex_unlock(&node->provider->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(icc_link_create);
+
+/**
+ * icc_add_node() - add an interconnect node to interconnect provider
+ * @node: pointer to the interconnect node
+ * @provider: pointer to the interconnect provider
+ *
+ * Return: 0 on success, or an error code otherwise
+ */
+int icc_node_add(struct icc_node *node, struct icc_provider *provider)
+{
+ if (WARN_ON(!node))
+ return -EINVAL;
+
+ if (WARN_ON(!provider))
+ return -EINVAL;
+
+ node->provider = provider;
+
+ mutex_lock(&provider->lock);
+ list_add_tail(&node->node_list, &provider->nodes);
+ mutex_unlock(&provider->lock);
+
+ return 0;
+}
+
+/**
+ * icc_add_provider() - add a new interconnect provider
+ * @icc_provider: the interconnect provider that will be added into topology
+ *
+ * Return: 0 on success, or an error code otherwise
+ */
+int icc_add_provider(struct icc_provider *provider)
+{
+ if (WARN_ON(!provider))
+ return -EINVAL;
+
+ if (WARN_ON(!provider->set))
+ return -EINVAL;
+
+ mutex_init(&provider->lock);
+ INIT_LIST_HEAD(&provider->nodes);
+
+ mutex_lock(&icc_provider_list_mutex);
+ list_add(&provider->provider_list, &icc_provider_list);
+ mutex_unlock(&icc_provider_list_mutex);
+
+ dev_dbg(provider->dev, "interconnect provider added to topology\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(icc_add_provider);
+
+/**
+ * icc_del_provider() - delete previously added interconnect provider
+ * @icc_provider: the interconnect provider that will be removed from topology
+ *
+ * Return: 0 on success, or an error code otherwise
+ */
+int icc_del_provider(struct icc_provider *provider)
+{
+ mutex_lock(&provider->lock);
+ if (provider->users) {
+ pr_warn("interconnect provider still has %d users\n",
+ provider->users);
+ }
+ mutex_unlock(&provider->lock);
+
+ mutex_lock(&icc_provider_list_mutex);
+ list_del(&provider->provider_list);
+ mutex_unlock(&icc_provider_list_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(icc_del_provider);
+
+MODULE_AUTHOR("Georgi Djakov <[email protected]");
+MODULE_DESCRIPTION("Interconnect Driver Core");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h
new file mode 100644
index 000000000000..779b5b5b1306
--- /dev/null
+++ b/include/linux/interconnect-provider.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018, Linaro Ltd.
+ * Author: Georgi Djakov <[email protected]>
+ */
+
+#ifndef _LINUX_INTERCONNECT_PROVIDER_H
+#define _LINUX_INTERCONNECT_PROVIDER_H
+
+#include <linux/interconnect.h>
+
+struct icc_node;
+
+/**
+ * struct icc_provider - interconnect provider (controller) entity that might
+ * provide multiple interconnect controls
+ *
+ * @provider_list: list of the registered interconnect providers
+ * @nodes: internal list of the interconnect provider nodes
+ * @set: pointer to device specific set operation function
+ * @dev: the device this interconnect provider belongs to
+ * @lock: lock to provide consistency during aggregation/update of constraints
+ * @users: count of active users
+ * @data: pointer to private data
+ */
+struct icc_provider {
+ struct list_head provider_list;
+ struct list_head nodes;
+ int (*set)(struct icc_node *src, struct icc_node *dst,
+ u32 avg_bw, u32 peak_bw);
+ struct device *dev;
+ struct mutex lock;
+ int users;
+ void *data;
+};
+
+/**
+ * struct icc_node - entity that is part of the interconnect topology
+ *
+ * @id: platform specific node id
+ * @name: node name used in debugfs
+ * @links: a list of targets where we can go next when traversing
+ * @num_links: number of links to other interconnect nodes
+ * @provider: points to the interconnect provider of this node
+ * @node_list: list of interconnect nodes associated with @provider
+ * @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
+ * @peak_bw: aggregated value of peak bandwidth
+ * @data: pointer to private data
+ */
+struct icc_node {
+ int id;
+ const char *name;
+ struct icc_node **links;
+ size_t num_links;
+
+ struct icc_provider *provider;
+ struct list_head node_list;
+ struct list_head orphan_list;
+ struct list_head search_list;
+ struct icc_node *reverse;
+ bool is_traversed;
+ struct hlist_head req_list;
+ u32 avg_bw;
+ u32 peak_bw;
+ void *data;
+};
+
+#if IS_ENABLED(CONFIG_INTERCONNECT)
+
+struct icc_node *icc_node_create(int id);
+int icc_node_add(struct icc_node *node, struct icc_provider *provider);
+int icc_link_create(struct icc_node *node, const int dst_id);
+int icc_add_provider(struct icc_provider *provider);
+int icc_del_provider(struct icc_provider *provider);
+
+#else
+
+static inline struct icc_node *icc_node_create(int id)
+{
+ return ERR_PTR(-ENOTSUPP);
+}
+
+int icc_node_add(struct icc_node *node, struct icc_provider *provider)
+{
+ return -ENOTSUPP;
+}
+
+static inline int icc_link_create(struct icc_node *node, const int dst_id)
+{
+ return -ENOTSUPP;
+}
+
+static inline int icc_add_provider(struct icc_provider *provider)
+{
+ return -ENOTSUPP;
+}
+
+static inline int icc_del_provider(struct icc_provider *provider)
+{
+ return -ENOTSUPP;
+}
+
+#endif /* CONFIG_INTERCONNECT */
+
+#endif /* _LINUX_INTERCONNECT_PROVIDER_H */
diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h
new file mode 100644
index 000000000000..5a7cf72b76a5
--- /dev/null
+++ b/include/linux/interconnect.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018, Linaro Ltd.
+ * Author: Georgi Djakov <[email protected]>
+ */
+
+#ifndef _LINUX_INTERCONNECT_H
+#define _LINUX_INTERCONNECT_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+
+struct icc_path;
+struct device;
+
+#if IS_ENABLED(CONFIG_INTERCONNECT)
+
+struct icc_path *icc_get(const int src_id, const int dst_id);
+void icc_put(struct icc_path *path);
+int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw);
+
+#else
+
+static inline struct icc_path *icc_get(const int src_id, const int dst_id)
+{
+ return NULL;
+}
+
+static inline void icc_put(struct icc_path *path)
+{
+}
+
+static inline int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
+{
+ return 0;
+}
+
+#endif /* CONFIG_INTERCONNECT */
+
+#endif /* _LINUX_INTERCONNECT_H */

2018-03-18 22:52:29

by Bjorn Andersson

[permalink] [raw]
Subject: Re: [PATCH v4 2/7] dt-bindings: Introduce interconnect provider bindings

On Fri 09 Mar 13:09 PST 2018, Georgi Djakov wrote:
> diff --git a/Documentation/devicetree/bindings/interconnect/interconnect.txt b/Documentation/devicetree/bindings/interconnect/interconnect.txt
[..]
> +Required properties:
> +- compatible : contains the interconnect provider vendor specific compatible
> + string
> +- reg : register space of the interconnect controller hardware

These properties doesn't relate to the interconnect provider, so there's
no need to describe them here.

> +
> +Examples:
> +
> + snoc: snoc@580000 {

These nodes should be described in a qcom/msm8916 binding document.

> + compatible = "qcom,msm8916-snoc";
> + reg = <0x580000 0x14000>;
> + clock-names = "bus_clk", "bus_a_clk";
> + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, <&rpmcc RPM_SMD_SNOC_A_CLK>;
> + status = "okay";
> + };

Regards,
Bjorn

2018-03-18 23:02:08

by Bjorn Andersson

[permalink] [raw]
Subject: Re: [PATCH v4 6/7] dt-bindings: Introduce interconnect consumers bindings

On Fri 09 Mar 13:09 PST 2018, Georgi Djakov wrote:

> Add documentation for the interconnect consumer bindings, that will allow
> to link a device node (consumer) to its interconnect controller hardware.
>
> Tha aim is to enable drivers to request a framework API to configure an
> interconnect path by providing their struct device pointer and a name.
>
> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> .../bindings/interconnect/interconnect.txt | 23 ++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/interconnect/interconnect.txt b/Documentation/devicetree/bindings/interconnect/interconnect.txt
> index 70612bb201e4..7935abf10c4b 100644
> --- a/Documentation/devicetree/bindings/interconnect/interconnect.txt
> +++ b/Documentation/devicetree/bindings/interconnect/interconnect.txt
> @@ -45,3 +45,26 @@ Examples:
> status = "okay";
> };
>
> += interconnect consumers =
> +
> +The interconnect consumers are device nodes which consume the interconnect
> +path(s) provided by the interconnect provider. There can be multiple
> +interconnect providers on a SoC and the consumer may consume multiple paths
> +from different providers depending on usecase and the components it has to
> +interact with.
> +
> +Required-properties:
> +interconnects: Pairs of phandles and interconnect provider specifier to denote
> + the source and the destination port of the interconnect path.
> +interconnect-names: List of interconnect path name strings sorted in the same
> + order as the interconnects property. Consumers drivers will use
> + interconnect-names to match interconnect paths with interconnect
> + specifiers.

Analog to other subsystems the -names property should be optional, at
least when there's only a single entry in interconnects.

> +
> +Example:
> +
> + sdhci@7864000 {
> + ...
> + interconnects = <&pnoc 78 &bimc 512>
> + interconnect-names = "memory";
> + };

Regards,
Bjorn

2018-03-19 09:35:58

by Georgi Djakov

[permalink] [raw]
Subject: Re: [PATCH v4 2/7] dt-bindings: Introduce interconnect provider bindings

Hi Bjorn,

On 03/19/2018 06:50 AM, Bjorn Andersson wrote:
> On Fri 09 Mar 13:09 PST 2018, Georgi Djakov wrote:
>> diff --git a/Documentation/devicetree/bindings/interconnect/interconnect.txt b/Documentation/devicetree/bindings/interconnect/interconnect.txt
> [..]
>> +Required properties:
>> +- compatible : contains the interconnect provider vendor specific compatible
>> + string
>> +- reg : register space of the interconnect controller hardware
>
> These properties doesn't relate to the interconnect provider, so there's
> no need to describe them here.

Agree.

>
>> +
>> +Examples:
>> +
>> + snoc: snoc@580000 {
>
> These nodes should be described in a qcom/msm8916 binding document.

Ok, will move this and the above to the platform specific binding
documentation.

>
>> + compatible = "qcom,msm8916-snoc";
>> + reg = <0x580000 0x14000>;
>> + clock-names = "bus_clk", "bus_a_clk";
>> + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, <&rpmcc RPM_SMD_SNOC_A_CLK>;
>> + status = "okay";
>> + };

Thanks for the comments!

BR,
Georgi

2018-03-19 09:43:26

by Georgi Djakov

[permalink] [raw]
Subject: Re: [PATCH v4 6/7] dt-bindings: Introduce interconnect consumers bindings

Hi Bjorn,

On 03/19/2018 06:49 AM, Bjorn Andersson wrote:
> On Fri 09 Mar 13:09 PST 2018, Georgi Djakov wrote:
>
>> Add documentation for the interconnect consumer bindings, that will allow
>> to link a device node (consumer) to its interconnect controller hardware.
>>
>> Tha aim is to enable drivers to request a framework API to configure an
>> interconnect path by providing their struct device pointer and a name.
>>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
>> .../bindings/interconnect/interconnect.txt | 23 ++++++++++++++++++++++
>> 1 file changed, 23 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/interconnect/interconnect.txt b/Documentation/devicetree/bindings/interconnect/interconnect.txt
>> index 70612bb201e4..7935abf10c4b 100644
>> --- a/Documentation/devicetree/bindings/interconnect/interconnect.txt
>> +++ b/Documentation/devicetree/bindings/interconnect/interconnect.txt
>> @@ -45,3 +45,26 @@ Examples:
>> status = "okay";
>> };
>>
>> += interconnect consumers =
>> +
>> +The interconnect consumers are device nodes which consume the interconnect
>> +path(s) provided by the interconnect provider. There can be multiple
>> +interconnect providers on a SoC and the consumer may consume multiple paths
>> +from different providers depending on usecase and the components it has to
>> +interact with.
>> +
>> +Required-properties:
>> +interconnects: Pairs of phandles and interconnect provider specifier to denote
>> + the source and the destination port of the interconnect path.
>> +interconnect-names: List of interconnect path name strings sorted in the same
>> + order as the interconnects property. Consumers drivers will use
>> + interconnect-names to match interconnect paths with interconnect
>> + specifiers.
>
> Analog to other subsystems the -names property should be optional, at
> least when there's only a single entry in interconnects.
>

Will do it.

Thanks,
Georgi

2018-04-05 23:00:11

by Matthias Kaehlcke

[permalink] [raw]
Subject: Re: [PATCH v4 5/7] interconnect: qcom: Add msm8916 interconnect provider driver

On Fri, Mar 09, 2018 at 11:09:56PM +0200, Georgi Djakov wrote:
> Add driver for the Qualcomm interconnect buses found in msm8916 based
> platforms.
>
> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> drivers/interconnect/Kconfig | 5 +
> drivers/interconnect/Makefile | 1 +
> drivers/interconnect/qcom/Kconfig | 11 +
> drivers/interconnect/qcom/Makefile | 2 +
> drivers/interconnect/qcom/msm8916.c | 482 ++++++++++++++++++++++++++++++++++++
> include/linux/interconnect/qcom.h | 350 ++++++++++++++++++++++++++
> 6 files changed, 851 insertions(+)
> create mode 100644 drivers/interconnect/qcom/Kconfig
> create mode 100644 drivers/interconnect/qcom/msm8916.c
> create mode 100644 include/linux/interconnect/qcom.h
>
> diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
> index a261c7d41deb..07a8276fa35a 100644
> --- a/drivers/interconnect/Kconfig
> +++ b/drivers/interconnect/Kconfig
> @@ -8,3 +8,8 @@ menuconfig INTERCONNECT
>
> If unsure, say no.
>
> +if INTERCONNECT
> +
> +source "drivers/interconnect/qcom/Kconfig"
> +
> +endif
> diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
> index 5edf0ae80818..5971b811c2d7 100644
> --- a/drivers/interconnect/Makefile
> +++ b/drivers/interconnect/Makefile
> @@ -1 +1,2 @@
> obj-$(CONFIG_INTERCONNECT) += core.o
> +obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/
> diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
> new file mode 100644
> index 000000000000..86465dc37bd4
> --- /dev/null
> +++ b/drivers/interconnect/qcom/Kconfig
> @@ -0,0 +1,11 @@
> +config INTERCONNECT_QCOM
> + bool "Qualcomm Network-on-Chip interconnect drivers"
> + depends on INTERCONNECT
> + depends on ARCH_QCOM || COMPILE_TEST
> + default y
> +
> +config INTERCONNECT_QCOM_MSM8916
> + tristate "Qualcomm MSM8916 interconnect driver"
> + depends on INTERCONNECT_QCOM
> + help
> + This is a driver for the Qualcomm Network-on-Chip on msm8916-based platforms.
> diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
> index 095bdef1ee6e..a0c13a25e8db 100644
> --- a/drivers/interconnect/qcom/Makefile
> +++ b/drivers/interconnect/qcom/Makefile
> @@ -1 +1,3 @@
> obj-y += smd-rpm.o
> +
> +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += msm8916.o
> diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c
> new file mode 100644
> index 000000000000..d5b54f8261c8
> --- /dev/null
> +++ b/drivers/interconnect/qcom/msm8916.c
> @@ -0,0 +1,482 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2018 Linaro Ltd
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/interconnect-provider.h>
> +#include <linux/interconnect/qcom.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include "smd-rpm.h"
> +
> +#define RPM_MASTER_FIELD_BW 0x00007762
> +#define RPM_BUS_MASTER_REQ 0x73616d62
> +#define RPM_BUS_SLAVE_REQ 0x766c7362
> +
> +#define to_qcom_provider(_provider) \
> + container_of(_provider, struct qcom_icc_provider, provider)
> +
> +#define DEFINE_QNODE(_name, _id, _port, _buswidth, _ap_owned, \
> + _mas_rpm_id, _slv_rpm_id, _qos_mode, \
> + _numlinks, ...) \
> + static struct qcom_icc_node _name = { \
> + .id = _id, \
> + .name = #_name, \
> + .port = _port, \
> + .buswidth = _buswidth, \
> + .qos_mode = _qos_mode, \
> + .ap_owned = _ap_owned, \
> + .mas_rpm_id = _mas_rpm_id, \
> + .slv_rpm_id = _slv_rpm_id, \
> + .num_links = _numlinks, \
> + .links = { __VA_ARGS__ }, \
> + }
> +
> +enum qcom_qos_mode {
> + QCOM_QOS_MODE_BYPASS = 0,
> + QCOM_QOS_MODE_FIXED,
> + QCOM_QOS_MODE_MAX,
> +};
> +
> +struct qcom_icc_provider {
> + struct icc_provider provider;
> + void __iomem *base;
> + struct clk *bus_clk;
> + struct clk *bus_a_clk;
> +};
> +
> +#define MSM8916_MAX_LINKS 8
> +
> +/**
> + * struct qcom_icc_node - Qualcomm specific interconnect nodes
> + * @name: the node name used in debugfs
> + * @links: an array of nodes where we can go next while traversing
> + * @id: a unique node identifier
> + * @num_links: the total number of @links
> + * @port: the offset index into the masters QoS register space
> + * @buswidth: width of the interconnect between a node and the bus
> + * @ap_owned: the AP CPU does the writing to QoS registers
> + * @rpm: reference to the RPM SMD driver
> + * @qos_mode: QoS mode for ap_owned resources
> + * @mas_rpm_id: RPM id for devices that are bus masters
> + * @slv_rpm_id: RPM id for devices that are bus slaves
> + * @rate: current bus clock rate in Hz
> + */
> +struct qcom_icc_node {
> + unsigned char *name;
> + u16 links[MSM8916_MAX_LINKS];
> + u16 id;
> + u16 num_links;
> + u16 port;
> + u16 buswidth;
> + bool ap_owned;
> + struct qcom_smd_rpm *rpm;
> + enum qcom_qos_mode qos_mode;
> + int mas_rpm_id;
> + int slv_rpm_id;
> + u64 rate;
> +};
> +
> +struct qcom_icc_desc {
> + struct qcom_icc_node **nodes;
> + size_t num_nodes;
> +};
> +
> +DEFINE_QNODE(mas_video, 63, 8, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);

IIUC this should be something like:

+DEFINE_QNODE(mas_video, MASTER_VIDEO_P0, MASTER_ADM_PORT1, 16, 1, -1,
-1, QCOM_QOS_MODE_BYPASS, 2, SNOC_MM_INT_0, SNOC_MM_INT_2);

and similar for the other nodes.

> +DEFINE_QNODE(mas_jpeg, 62, 6, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
> +DEFINE_QNODE(mas_vfe, 29, 9, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10001, 10002);
> +DEFINE_QNODE(mas_mdp, 22, 7, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
> +DEFINE_QNODE(mas_qdss_bam, 53, 11, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10009);
> +DEFINE_QNODE(mas_snoc_cfg, 54, 0, 16, 0, 20, -1, QCOM_QOS_MODE_BYPASS, 1, 10009);
> +DEFINE_QNODE(mas_qdss_etr, 60, 10, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10009);
> +DEFINE_QNODE(mm_int_0, 10000, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10003);
> +DEFINE_QNODE(mm_int_1, 10001, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10003);
> +DEFINE_QNODE(mm_int_2, 10002, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10004);
> +DEFINE_QNODE(mm_int_bimc, 10003, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10008);
> +DEFINE_QNODE(snoc_int_0, 10004, 0, 8, 0, 99, 130, QCOM_QOS_MODE_FIXED, 3, 588, 519, 10027);
> +DEFINE_QNODE(snoc_int_1, 10005, 0, 8, 0, 100, 131, QCOM_QOS_MODE_FIXED, 3, 517, 663, 664);
> +DEFINE_QNODE(snoc_int_bimc, 10006, 0, 8, 0, 101, 132, QCOM_QOS_MODE_FIXED, 1, 10007);
> +DEFINE_QNODE(snoc_bimc_0_mas, 10007, 0, 8, 0, 3, -1, QCOM_QOS_MODE_FIXED, 1, 10025);
> +DEFINE_QNODE(snoc_bimc_1_mas, 10008, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10026);
> +DEFINE_QNODE(qdss_int, 10009, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 2, 10004, 10006);
> +DEFINE_QNODE(bimc_snoc_slv, 10017, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 2, 10004, 10005);
> +DEFINE_QNODE(snoc_pnoc_mas, 10027, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10028);
> +DEFINE_QNODE(pnoc_snoc_slv, 10011, 0, 8, 0, -1, 45, QCOM_QOS_MODE_FIXED, 3, 10004, 10006, 10005);
> +DEFINE_QNODE(slv_srvc_snoc, 587, 0, 8, 0, -1, 29, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_qdss_stm, 588, 0, 4, 0, -1, 30, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_imem, 519, 0, 8, 0, -1, 26, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_apss, 517, 0, 4, 0, -1, 20, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_cats_0, 663, 0, 16, 0, -1, 106, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_cats_1, 664, 0, 8, 0, -1, 107, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(mas_apss, 1, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
> +DEFINE_QNODE(mas_tcu0, 104, 5, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
> +DEFINE_QNODE(mas_tcu1, 105, 6, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
> +DEFINE_QNODE(mas_gfx, 26, 2, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
> +DEFINE_QNODE(bimc_snoc_mas, 10016, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10017);
> +DEFINE_QNODE(snoc_bimc_0_slv, 10025, 0, 8, 0, -1, 24, QCOM_QOS_MODE_FIXED, 1, 512);
> +DEFINE_QNODE(snoc_bimc_1_slv, 10026, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 512);
> +DEFINE_QNODE(slv_ebi_ch0, 512, 0, 8, 0, -1, 0, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_apps_l2, 514, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(snoc_pnoc_slv, 10028, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10012);
> +DEFINE_QNODE(pnoc_int_0, 10012, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 8, 10010, 10018, 10019, 10020, 10021, 10022, 10023, 10024);
> +DEFINE_QNODE(pnoc_int_1, 10013, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10010);
> +DEFINE_QNODE(pnoc_m_0, 10014, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10012);
> +DEFINE_QNODE(pnoc_m_1, 10015, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10010);
> +DEFINE_QNODE(pnoc_s_0, 10018, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 620, 624, 579, 622, 521);
> +DEFINE_QNODE(pnoc_s_1, 10019, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 627, 625, 535, 577, 618);
> +DEFINE_QNODE(pnoc_s_2, 10020, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 533, 630, 629, 641, 632);
> +DEFINE_QNODE(pnoc_s_3, 10021, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 536, 647, 636, 635, 634);
> +DEFINE_QNODE(pnoc_s_4, 10022, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 596, 589, 590);
> +DEFINE_QNODE(pnoc_s_8, 10023, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 614, 606, 613);
> +DEFINE_QNODE(pnoc_s_9, 10024, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 609, 522, 598);
> +DEFINE_QNODE(slv_imem_cfg, 627, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_crypto_0_cfg, 625, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_msg_ram, 535, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_pdm, 577, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_prng, 618, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_clk_ctl, 620, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_mss, 521, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_tlmm, 624, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_tcsr, 579, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_security, 622, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_spdm, 533, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_pnoc_cfg, 641, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_pmic_arb, 632, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_bimc_cfg, 629, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_boot_rom, 630, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_mpm, 536, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_qdss_cfg, 635, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_rbcpr_cfg, 636, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_snoc_cfg, 647, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_dehr_cfg, 634, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_venus_cfg, 596, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_display_cfg, 590, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_camera_cfg, 589, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_usb_hs, 614, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_sdcc_1, 606, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_blsp_1, 613, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_sdcc_2, 609, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_gfx_cfg, 598, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_audio, 522, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(mas_blsp_1, 86, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10015);
> +DEFINE_QNODE(mas_spdm, 36, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
> +DEFINE_QNODE(mas_dehr, 75, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
> +DEFINE_QNODE(mas_audio, 15, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
> +DEFINE_QNODE(mas_usb_hs, 87, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10015);
> +DEFINE_QNODE(mas_pnoc_crypto_0, 55, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
> +DEFINE_QNODE(mas_pnoc_sdcc_1, 78, 7, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
> +DEFINE_QNODE(mas_pnoc_sdcc_2, 81, 8, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
> +DEFINE_QNODE(pnoc_snoc_mas, 10010, 0, 8, 0, 29, -1, QCOM_QOS_MODE_FIXED, 1, 10011);
> +
> +static struct qcom_icc_node *msm8916_snoc_nodes[] = {
> + &mas_video,
> + &mas_jpeg,
> + &mas_vfe,
> + &mas_mdp,
> + &mas_qdss_bam,
> + &mas_snoc_cfg,
> + &mas_qdss_etr,
> + &mm_int_0,
> + &mm_int_1,
> + &mm_int_2,
> + &mm_int_bimc,
> + &snoc_int_0,
> + &snoc_int_1,
> + &snoc_int_bimc,
> + &snoc_bimc_0_mas,
> + &snoc_bimc_1_mas,
> + &qdss_int,
> + &bimc_snoc_slv,
> + &snoc_pnoc_mas,
> + &pnoc_snoc_slv,
> + &slv_srvc_snoc,
> + &slv_qdss_stm,
> + &slv_imem,
> + &slv_apss,
> + &slv_cats_0,
> + &slv_cats_1,
> +};
> +
> +static struct qcom_icc_desc msm8916_snoc = {
> + .nodes = msm8916_snoc_nodes,
> + .num_nodes = ARRAY_SIZE(msm8916_snoc_nodes),
> +};
> +
> +static struct qcom_icc_node *msm8916_bimc_nodes[] = {
> + &mas_apss,
> + &mas_tcu0,
> + &mas_tcu1,
> + &mas_gfx,
> + &bimc_snoc_mas,
> + &snoc_bimc_0_slv,
> + &snoc_bimc_1_slv,
> + &slv_ebi_ch0,
> + &slv_apps_l2,
> +};
> +
> +static struct qcom_icc_desc msm8916_bimc = {
> + .nodes = msm8916_bimc_nodes,
> + .num_nodes = ARRAY_SIZE(msm8916_bimc_nodes),
> +};
> +
> +static struct qcom_icc_node *msm8916_pnoc_nodes[] = {
> + &snoc_pnoc_slv,
> + &pnoc_int_0,
> + &pnoc_int_1,
> + &pnoc_m_0,
> + &pnoc_m_1,
> + &pnoc_s_0,
> + &pnoc_s_1,
> + &pnoc_s_2,
> + &pnoc_s_3,
> + &pnoc_s_4,
> + &pnoc_s_8,
> + &pnoc_s_9,
> + &slv_imem_cfg,
> + &slv_crypto_0_cfg,
> + &slv_msg_ram,
> + &slv_pdm,
> + &slv_prng,
> + &slv_clk_ctl,
> + &slv_mss,
> + &slv_tlmm,
> + &slv_tcsr,
> + &slv_security,
> + &slv_spdm,
> + &slv_pnoc_cfg,
> + &slv_pmic_arb,
> + &slv_bimc_cfg,
> + &slv_boot_rom,
> + &slv_mpm,
> + &slv_qdss_cfg,
> + &slv_rbcpr_cfg,
> + &slv_snoc_cfg,
> + &slv_dehr_cfg,
> + &slv_venus_cfg,
> + &slv_display_cfg,
> + &slv_camera_cfg,
> + &slv_usb_hs,
> + &slv_sdcc_1,
> + &slv_blsp_1,
> + &slv_sdcc_2,
> + &slv_gfx_cfg,
> + &slv_audio,
> + &mas_blsp_1,
> + &mas_spdm,
> + &mas_dehr,
> + &mas_audio,
> + &mas_usb_hs,
> + &mas_pnoc_crypto_0,
> + &mas_pnoc_sdcc_1,
> + &mas_pnoc_sdcc_2,
> + &pnoc_snoc_mas,
> +};
> +
> +static struct qcom_icc_desc msm8916_pnoc = {
> + .nodes = msm8916_pnoc_nodes,
> + .num_nodes = ARRAY_SIZE(msm8916_pnoc_nodes),
> +};
> +
> +static int qcom_icc_init(struct icc_node *node)
> +{
> + struct qcom_icc_provider *qp = to_qcom_provider(node->provider);
> + /* TODO: init qos and priority */
> +
> + clk_prepare_enable(qp->bus_clk);
> + clk_prepare_enable(qp->bus_a_clk);
> +
> + return 0;
> +}
> +
> +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst,
> + u32 avg, u32 peak)
> +{
> + struct qcom_icc_provider *qp;
> + struct qcom_icc_node *qn;
> + struct icc_node *node;
> + struct icc_provider *provider;
> + u64 avg_bw;
> + u64 peak_bw;
> + u64 rate = 0;
> + int ret = 0;
> +
> + if (!src)
> + node = dst;
> + else
> + node = src;
> +
> + qn = node->data;
> + provider = node->provider;
> + qp = to_qcom_provider(node->provider);
> +
> + /* convert from kbps to bps */
> + avg_bw = avg * 1000ULL;
> + peak_bw = peak * 1000ULL;
> +
> + /* set bandwidth */
> + if (qn->ap_owned) {
> + /* TODO: set QoS */
> + } else {
> + /* send message to the RPM processor */
> + if (qn->mas_rpm_id != -1) {
> + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
> + RPM_BUS_MASTER_REQ,
> + qn->mas_rpm_id,
> + avg_bw);
> + }
> +
> + if (qn->slv_rpm_id != -1) {
> + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
> + RPM_BUS_SLAVE_REQ,
> + qn->slv_rpm_id,
> + avg_bw);
> + }
> + }
> +
> + rate = max(avg_bw, peak_bw);
> +
> + do_div(rate, qn->buswidth);
> +
> + if (qn->rate != rate) {
> + ret = clk_set_rate(qp->bus_clk, rate);
> + if (ret) {
> + pr_err("set clk rate %lld error %d\n", rate, ret);
> + return ret;
> + }
> +
> + ret = clk_set_rate(qp->bus_a_clk, rate);
> + if (ret) {
> + pr_err("set clk rate %lld error %d\n", rate, ret);
> + return ret;
> + }
> +
> + qn->rate = rate;
> + }
> +
> + return ret;
> +}
> +
> +static int qnoc_probe(struct platform_device *pdev)
> +{
> + const struct qcom_icc_desc *desc;
> + struct qcom_icc_node **qnodes;
> + struct qcom_icc_provider *qp;
> + struct resource *res;
> + struct icc_provider *provider;
> + size_t num_nodes, i;
> + int ret;
> +
> + /* wait for RPM */
> + if (!qcom_icc_rpm_smd_available())
> + return -EPROBE_DEFER;
> +
> + desc = of_device_get_match_data(&pdev->dev);
> + if (!desc)
> + return -EINVAL;
> +
> + qnodes = desc->nodes;
> + num_nodes = desc->num_nodes;
> +
> + qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
> + if (!qp)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + qp->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(qp->base))
> + return PTR_ERR(qp->base);
> +
> + qp->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
> + if (IS_ERR(qp->bus_clk))
> + return PTR_ERR(qp->bus_clk);
> +
> + qp->bus_a_clk = devm_clk_get(&pdev->dev, "bus_a_clk");
> + if (IS_ERR(qp->bus_a_clk))
> + return PTR_ERR(qp->bus_a_clk);
> +
> + provider = &qp->provider;
> + provider->dev = &pdev->dev;
> + provider->set = &qcom_icc_set;
> + INIT_LIST_HEAD(&provider->nodes);
> + provider->data = qp;
> +
> + ret = icc_add_provider(provider);
> + if (ret) {
> + dev_err(&pdev->dev, "error adding interconnect provider\n");
> + return ret;
> + }
> +
> + for (i = 0; i < num_nodes; i++) {
> + struct icc_node *node;
> + int ret;
> + size_t j;
> +
> + node = icc_node_create(qnodes[i]->id);
> + if (IS_ERR(node)) {
> + ret = PTR_ERR(node);
> + goto err;
> + }
> +
> + node->name = qnodes[i]->name;
> + node->data = qnodes[i];
> + icc_node_add(node, provider);
> +
> + dev_dbg(&pdev->dev, "registered node %p %s %d\n", node,
> + qnodes[i]->name, node->id);
> +
> + /* populate links */
> + for (j = 0; j < qnodes[i]->num_links; j++)
> + if (qnodes[i]->links[j])
> + icc_link_create(node, qnodes[i]->links[j]);
> +
> + ret = qcom_icc_init(node);
> + if (ret)
> + dev_err(&pdev->dev, "%s init error (%d)\n", node->name,
> + ret);
> + }
> +
> + platform_set_drvdata(pdev, provider);
> +
> + return ret;
> +err:
> + icc_del_provider(provider);
> + return ret;
> +}
> +
> +static int qnoc_remove(struct platform_device *pdev)
> +{
> + struct icc_provider *provider = platform_get_drvdata(pdev);
> +
> + icc_del_provider(provider);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id qnoc_of_match[] = {
> + { .compatible = "qcom,msm8916-pnoc", .data = &msm8916_pnoc },
> + { .compatible = "qcom,msm8916-snoc", .data = &msm8916_snoc },
> + { .compatible = "qcom,msm8916-bimc", .data = &msm8916_bimc },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, qnoc_of_match);
> +
> +static struct platform_driver qnoc_driver = {
> + .probe = qnoc_probe,
> + .remove = qnoc_remove,
> + .driver = {
> + .name = "qnoc-msm8916",
> + .of_match_table = qnoc_of_match,
> + },
> +};
> +module_platform_driver(qnoc_driver);
> +MODULE_AUTHOR("Georgi Djakov <[email protected]>");
> +MODULE_DESCRIPTION("Qualcomm msm8916 NoC driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/interconnect/qcom.h
> b/include/linux/interconnect/qcom.h

Are the values in this file valid for all Qualcomm SoCs or specific to
the MSM8916? In the latter case you might want to include the chip
(family) in the name of the constant.

> new file mode 100644
> index 000000000000..2cd378d2f575
> --- /dev/null
> +++ b/include/linux/interconnect/qcom.h
> @@ -0,0 +1,350 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Qualcomm interconnect IDs
> + *
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef __QCOM_INTERCONNECT_IDS_H
> +#define __QCOM_INTERCONNECT_IDS_H
> +
> +#define FAB_BIMC 0
> +#define FAB_SYS_NOC 1024
> +#define FAB_MMSS_NOC 2048
> +#define FAB_OCMEM_NOC 3072
> +#define FAB_PERIPH_NOC 4096
> +#define FAB_CONFIG_NOC 5120
> +#define FAB_OCMEM_VNOC 6144
> +
> +#define MASTER_AMPSS_M0 1
> +#define MASTER_AMPSS_M1 2
> +#define APPSS_MASTER_FAB_MMSS 3
> +#define APPSS_MASTER_FAB_SYSTEM 4
> +#define SYSTEM_MASTER_FAB_APPSS 5
> +#define MASTER_SPS 6
> +#define MASTER_ADM_PORT0 7
> +#define MASTER_ADM_PORT1 8
> +#define SYSTEM_MASTER_ADM1_PORT0 9
> +#define MASTER_ADM1_PORT1 10
> +#define MASTER_LPASS_PROC 11
> +#define MASTER_MSS_PROCI 12
> +#define MASTER_MSS_PROCD 13
> +#define MASTER_MSS_MDM_PORT0 14
> +#define MASTER_LPASS 15
> +#define SYSTEM_MASTER_CPSS_FPB 16
> +#define SYSTEM_MASTER_SYSTEM_FPB 17
> +#define SYSTEM_MASTER_MMSS_FPB 18
> +#define MASTER_ADM1_CI 19
> +#define MASTER_ADM0_CI 20
> +#define MASTER_MSS_MDM_PORT1 21
> +#define MASTER_MDP_PORT0 22
> +#define MASTER_MDP_PORT1 23
> +#define MMSS_MASTER_ADM1_PORT0 24
> +#define MASTER_ROTATOR 25
> +#define MASTER_GRAPHICS_3D 26
> +#define MASTER_JPEG_DEC 27
> +#define MASTER_GRAPHICS_2D_CORE0 28
> +#define MASTER_VFE 29
> +#define MASTER_VPE 30
> +#define MASTER_JPEG_ENC 31
> +#define MASTER_GRAPHICS_2D_CORE1 32
> +#define MMSS_MASTER_APPS_FAB 33
> +#define MASTER_HD_CODEC_PORT0 34
> +#define MASTER_HD_CODEC_PORT1 35
> +#define MASTER_SPDM 36
> +#define MASTER_RPM 37
> +#define MASTER_MSS 38
> +#define MASTER_RIVA 39
> +#define SYSTEM_MASTER_UNUSED_6 40
> +#define MASTER_MSS_SW_PROC 41
> +#define MASTER_MSS_FW_PROC 42
> +#define MMSS_MASTER_UNUSED_2 43
> +#define MASTER_GSS_NAV 44
> +#define MASTER_PCIE 45
> +#define MASTER_SATA 46
> +#define MASTER_CRYPTO 47
> +#define MASTER_VIDEO_CAP 48
> +#define MASTER_GRAPHICS_3D_PORT1 49
> +#define MASTER_VIDEO_ENC 50
> +#define MASTER_VIDEO_DEC 51
> +#define MASTER_LPASS_AHB 52
> +#define MASTER_QDSS_BAM 53
> +#define MASTER_SNOC_CFG 54
> +#define MASTER_CRYPTO_CORE0 55
> +#define MASTER_CRYPTO_CORE1 56
> +#define MASTER_MSS_NAV 57
> +#define MASTER_OCMEM_DMA 58
> +#define MASTER_WCSS 59
> +#define MASTER_QDSS_ETR 60
> +#define MASTER_USB3 61
> +#define MASTER_JPEG 62
> +#define MASTER_VIDEO_P0 63
> +#define MASTER_VIDEO_P1 64
> +#define MASTER_MSS_PROC 65
> +#define MASTER_JPEG_OCMEM 66
> +#define MASTER_MDP_OCMEM 67
> +#define MASTER_VIDEO_P0_OCMEM 68
> +#define MASTER_VIDEO_P1_OCMEM 69
> +#define MASTER_VFE_OCMEM 70
> +#define MASTER_CNOC_ONOC_CFG 71
> +#define MASTER_RPM_INST 72
> +#define MASTER_RPM_DATA 73
> +#define MASTER_RPM_SYS 74
> +#define MASTER_DEHR 75
> +#define MASTER_QDSS_DAP 76
> +#define MASTER_TIC 77
> +#define MASTER_SDCC_1 78
> +#define MASTER_SDCC_3 79
> +#define MASTER_SDCC_4 80
> +#define MASTER_SDCC_2 81
> +#define MASTER_TSIF 82
> +#define MASTER_BAM_DMA 83
> +#define MASTER_BLSP_2 84
> +#define MASTER_USB_HSIC 85
> +#define MASTER_BLSP_1 86
> +#define MASTER_USB_HS 87
> +#define MASTER_PNOC_CFG 88
> +#define MASTER_V_OCMEM_GFX3D 89
> +#define MASTER_IPA 90
> +#define MASTER_QPIC 91
> +#define MASTER_MDPE 92
> +#define MASTER_USB_HS2 93
> +#define MASTER_VPU 94
> +#define MASTER_UFS 95
> +#define MASTER_BCAST 96
> +#define MASTER_CRYPTO_CORE2 97
> +#define MASTER_EMAC 98
> +#define MASTER_VPU_1 99
> +#define MASTER_PCIE_1 100
> +#define MASTER_USB3_1 101
> +#define MASTER_CNOC_MNOC_MMSS_CFG 102
> +#define MASTER_CNOC_MNOC_CFG 103
> +#define MASTER_TCU_0 104
> +#define MASTER_TCU_1 105
> +#define MASTER_CPP 106
> +#define MASTER_AUDIO 107
> +
> +#define SNOC_MM_INT_0 10000
> +#define SNOC_MM_INT_1 10001
> +#define SNOC_MM_INT_2 10002
> +#define SNOC_MM_INT_BIMC 10003
> +#define SNOC_INT_0 10004
> +#define SNOC_INT_1 10005
> +#define SNOC_INT_BIMC 10006
> +#define SNOC_BIMC_0_MAS 10007
> +#define SNOC_BIMC_1_MAS 10008
> +#define SNOC_QDSS_INT 10009
> +#define PNOC_SNOC_MAS 10010
> +#define PNOC_SNOC_SLV 10011
> +#define PNOC_INT_0 10012
> +#define PNOC_INT_1 10013
> +#define PNOC_M_0 10014
> +#define PNOC_M_1 10015
> +#define BIMC_SNOC_MAS 10016
> +#define BIMC_SNOC_SLV 10017
> +#define PNOC_SLV_0 10018
> +#define PNOC_SLV_1 10019
> +#define PNOC_SLV_2 10020
> +#define PNOC_SLV_3 10021
> +#define PNOC_SLV_4 10022
> +#define PNOC_SLV_8 10023
> +#define PNOC_SLV_9 10024
> +#define SNOC_BIMC_0_SLV 10025
> +#define SNOC_BIMC_1_SLV 10026
> +#define MNOC_BIMC_MAS 10027
> +#define MNOC_BIMC_SLV 10028
> +#define BIMC_MNOC_MAS 10029
> +#define BIMC_MNOC_SLV 10030
> +#define SNOC_BIMC_MAS 10031
> +#define SNOC_BIMC_SLV 10032
> +#define CNOC_SNOC_MAS 10033
> +#define CNOC_SNOC_SLV 10034
> +#define SNOC_CNOC_MAS 10035
> +#define SNOC_CNOC_SLV 10036
> +#define OVNOC_SNOC_MAS 10037
> +#define OVNOC_SNOC_SLV 10038
> +#define SNOC_OVNOC_MAS 10039
> +#define SNOC_OVNOC_SLV 10040
> +#define SNOC_PNOC_MAS 10041
> +#define SNOC_PNOC_SLV 10042
> +#define BIMC_INT_APPS_EBI 10043
> +#define BIMC_INT_APPS_SNOC 10044
> +#define SNOC_BIMC_2_MAS 10045
> +#define SNOC_BIMC_2_SLV 10046
> +#define PNOC_SLV_5 10047
> +#define PNOC_SLV_7 10048
> +#define PNOC_INT_2 10049
> +#define PNOC_INT_3 10050
> +#define PNOC_INT_4 10051
> +#define PNOC_INT_5 10052
> +#define PNOC_INT_6 10053
> +#define PNOC_INT_7 10054
> +
> +#define SLAVE_EBI_CH0 512
> +#define SLAVE_EBI_CH1 513
> +#define SLAVE_AMPSS_L2 514
> +#define APPSS_SLAVE_FAB_MMSS 515
> +#define APPSS_SLAVE_FAB_SYSTEM 516
> +#define SYSTEM_SLAVE_FAB_APPS 517
> +#define SLAVE_SPS 518
> +#define SLAVE_SYSTEM_IMEM 519
> +#define SLAVE_AMPSS 520
> +#define SLAVE_MSS 521
> +#define SLAVE_LPASS 522
> +#define SYSTEM_SLAVE_CPSS_FPB 523
> +#define SYSTEM_SLAVE_SYSTEM_FPB 524
> +#define SYSTEM_SLAVE_MMSS_FPB 525
> +#define SLAVE_CORESIGHT 526
> +#define SLAVE_RIVA 527
> +#define SLAVE_SMI 528
> +#define MMSS_SLAVE_FAB_APPS 529
> +#define MMSS_SLAVE_FAB_APPS_1 530
> +#define SLAVE_MM_IMEM 531
> +#define SLAVE_CRYPTO 532
> +#define SLAVE_SPDM 533
> +#define SLAVE_RPM 534
> +#define SLAVE_RPM_MSG_RAM 535
> +#define SLAVE_MPM 536
> +#define SLAVE_PMIC1_SSBI1_A 537
> +#define SLAVE_PMIC1_SSBI1_B 538
> +#define SLAVE_PMIC1_SSBI1_C 539
> +#define SLAVE_PMIC2_SSBI2_A 540
> +#define SLAVE_PMIC2_SSBI2_B 541
> +#define SLAVE_GSBI1_UART 542
> +#define SLAVE_GSBI2_UART 543
> +#define SLAVE_GSBI3_UART 544
> +#define SLAVE_GSBI4_UART 545
> +#define SLAVE_GSBI5_UART 546
> +#define SLAVE_GSBI6_UART 547
> +#define SLAVE_GSBI7_UART 548
> +#define SLAVE_GSBI8_UART 549
> +#define SLAVE_GSBI9_UART 550
> +#define SLAVE_GSBI10_UART 551
> +#define SLAVE_GSBI11_UART 552
> +#define SLAVE_GSBI12_UART 553
> +#define SLAVE_GSBI1_QUP 554
> +#define SLAVE_GSBI2_QUP 555
> +#define SLAVE_GSBI3_QUP 556
> +#define SLAVE_GSBI4_QUP 557
> +#define SLAVE_GSBI5_QUP 558
> +#define SLAVE_GSBI6_QUP 559
> +#define SLAVE_GSBI7_QUP 560
> +#define SLAVE_GSBI8_QUP 561
> +#define SLAVE_GSBI9_QUP 562
> +#define SLAVE_GSBI10_QUP 563
> +#define SLAVE_GSBI11_QUP 564
> +#define SLAVE_GSBI12_QUP 565
> +#define SLAVE_EBI2_NAND 566
> +#define SLAVE_EBI2_CS0 567
> +#define SLAVE_EBI2_CS1 568
> +#define SLAVE_EBI2_CS2 569
> +#define SLAVE_EBI2_CS3 570
> +#define SLAVE_EBI2_CS4 571
> +#define SLAVE_EBI2_CS5 572
> +#define SLAVE_USB_FS1 573
> +#define SLAVE_USB_FS2 574
> +#define SLAVE_TSIF 575
> +#define SLAVE_MSM_TSSC 576
> +#define SLAVE_MSM_PDM 577
> +#define SLAVE_MSM_DIMEM 578
> +#define SLAVE_MSM_TCSR 579
> +#define SLAVE_MSM_PRNG 580
> +#define SLAVE_GSS 581
> +#define SLAVE_SATA 582
> +#define SLAVE_USB3 583
> +#define SLAVE_WCSS 584
> +#define SLAVE_OCIMEM 585
> +#define SLAVE_SNOC_OCMEM 586
> +#define SLAVE_SERVICE_SNOC 587
> +#define SLAVE_QDSS_STM 588
> +#define SLAVE_CAMERA_CFG 589
> +#define SLAVE_DISPLAY_CFG 590
> +#define SLAVE_OCMEM_CFG 591
> +#define SLAVE_CPR_CFG 592
> +#define SLAVE_CPR_XPU_CFG 593
> +#define SLAVE_MISC_CFG 594
> +#define SLAVE_MISC_XPU_CFG 595
> +#define SLAVE_VENUS_CFG 596
> +#define SLAVE_MISC_VENUS_CFG 597
> +#define SLAVE_GRAPHICS_3D_CFG 598
> +#define SLAVE_MMSS_CLK_CFG 599
> +#define SLAVE_MMSS_CLK_XPU_CFG 600
> +#define SLAVE_MNOC_MPU_CFG 601
> +#define SLAVE_ONOC_MPU_CFG 602
> +#define SLAVE_SERVICE_MNOC 603
> +#define SLAVE_OCMEM 604
> +#define SLAVE_SERVICE_ONOC 605
> +#define SLAVE_SDCC_1 606
> +#define SLAVE_SDCC_3 607
> +#define SLAVE_SDCC_2 608
> +#define SLAVE_SDCC_4 609
> +#define SLAVE_BAM_DMA 610
> +#define SLAVE_BLSP_2 611
> +#define SLAVE_USB_HSIC 612
> +#define SLAVE_BLSP_1 613
> +#define SLAVE_USB_HS 614
> +#define SLAVE_PDM 615
> +#define SLAVE_PERIPH_APU_CFG 616
> +#define SLAVE_PNOC_MPU_CFG 617
> +#define SLAVE_PRNG 618
> +#define SLAVE_SERVICE_PNOC 619
> +#define SLAVE_CLK_CTL 620
> +#define SLAVE_CNOC_MSS 621
> +#define SLAVE_SECURITY 622
> +#define SLAVE_TCSR 623
> +#define SLAVE_TLMM 624
> +#define SLAVE_CRYPTO_0_CFG 625
> +#define SLAVE_CRYPTO_1_CFG 626
> +#define SLAVE_IMEM_CFG 627
> +#define SLAVE_MESSAGE_RAM 628
> +#define SLAVE_BIMC_CFG 629
> +#define SLAVE_BOOT_ROM 630
> +#define SLAVE_CNOC_MNOC_MMSS_CFG 631
> +#define SLAVE_PMIC_ARB 632
> +#define SLAVE_SPDM_WRAPPER 633
> +#define SLAVE_DEHR_CFG 634
> +#define SLAVE_QDSS_CFG 635
> +#define SLAVE_RBCPR_CFG 636
> +#define SLAVE_RBCPR_QDSS_APU_CFG 637
> +#define SLAVE_SNOC_MPU_CFG 638
> +#define SLAVE_CNOC_ONOC_CFG 639
> +#define SLAVE_CNOC_MNOC_CFG 640
> +#define SLAVE_PNOC_CFG 641
> +#define SLAVE_SNOC_CFG 642
> +#define SLAVE_EBI1_DLL_CFG 643
> +#define SLAVE_PHY_APU_CFG 644
> +#define SLAVE_EBI1_PHY_CFG 645
> +#define SLAVE_SERVICE_CNOC 646
> +#define SLAVE_IPS_CFG 647
> +#define SLAVE_QPIC 648
> +#define SLAVE_DSI_CFG 649
> +#define SLAVE_UFS_CFG 650
> +#define SLAVE_RBCPR_CX_CFG 651
> +#define SLAVE_RBCPR_MX_CFG 652
> +#define SLAVE_PCIE_CFG 653
> +#define SLAVE_USB_PHYS_CFG 654
> +#define SLAVE_VIDEO_CAP_CFG 655
> +#define SLAVE_AVSYNC_CFG 656
> +#define SLAVE_CRYPTO_2_CFG 657
> +#define SLAVE_VPU_CFG 658
> +#define SLAVE_BCAST_CFG 659
> +#define SLAVE_KLM_CFG 660
> +#define SLAVE_GENI_IR_CFG 661
> +#define SLAVE_OCMEM_GFX 662
> +#define SLAVE_CATS_128 663
> +#define SLAVE_OCMEM_64 664
> +#define SLAVE_PCIE_0 665
> +#define SLAVE_PCIE_1 666
> +#define SLAVE_PCIE_0_CFG 667
> +#define SLAVE_PCIE_1_CFG 668
> +#define SLAVE_SRVC_MNOC 669
> +#define SLAVE_USB_HS2 670
> +#define SLAVE_AUDIO 671
> +#define SLAVE_TCU 672
> +#define SLAVE_APPSS 673
> +#define SLAVE_PCIE_PARF 674
> +#define SLAVE_USB3_PHY_CFG 675
> +#define SLAVE_IPA_CFG 676
> +
> +#endif

2018-04-06 17:42:38

by Matthias Kaehlcke

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

On Fri, Mar 09, 2018 at 11:09:52PM +0200, Georgi Djakov wrote:
> This patch introduce a new API to get requirements and configure the
> interconnect buses across the entire chipset to fit with the current
> demand.
>
> The API is using a consumer/provider-based model, where the providers are
> the interconnect buses and the consumers could be various drivers.
> The consumers request interconnect resources (path) between endpoints and
> set the desired constraints on this data flow path. The providers receive
> requests from consumers and aggregate these requests for all master-slave
> pairs on that path. Then the providers configure each participating in the
> topology node according to the requested data flow path, physical links and
> constraints. The topology could be complicated and multi-tiered and is SoC
> specific.
>
> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> Documentation/interconnect/interconnect.rst | 96 ++++++
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/interconnect/Kconfig | 10 +
> drivers/interconnect/Makefile | 1 +
> drivers/interconnect/core.c | 489 ++++++++++++++++++++++++++++
> include/linux/interconnect-provider.h | 109 +++++++
> include/linux/interconnect.h | 40 +++
> 8 files changed, 748 insertions(+)
> create mode 100644 Documentation/interconnect/interconnect.rst
> create mode 100644 drivers/interconnect/Kconfig
> create mode 100644 drivers/interconnect/Makefile
> create mode 100644 drivers/interconnect/core.c
> create mode 100644 include/linux/interconnect-provider.h
> create mode 100644 include/linux/interconnect.h
>
> diff --git a/Documentation/interconnect/interconnect.rst b/Documentation/interconnect/interconnect.rst
> new file mode 100644
> index 000000000000..23eba68e8424
> --- /dev/null
> +++ b/Documentation/interconnect/interconnect.rst
> @@ -0,0 +1,96 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +=====================================
> +GENERIC SYSTEM INTERCONNECT SUBSYSTEM
> +=====================================
> +
> +Introduction
> +------------
> +
> +This framework is designed to provide a standard kernel interface to control
> +the settings of the interconnects on a SoC. These settings can be throughput,
> +latency and priority between multiple interconnected devices or functional
> +blocks. This can be controlled dynamically in order to save power or provide
> +maximum performance.
> +
> +The interconnect bus is a hardware with configurable parameters, which can be
> +set on a data path according to the requests received from various drivers.
> +An example of interconnect buses are the interconnects between various
> +components or functional blocks in chipsets. There can be multiple interconnects
> +on a SoC that can be multi-tiered.
> +
> +Below is a simplified diagram of a real-world SoC interconnect bus topology.
> +
> +::
> +
> + +----------------+ +----------------+
> + | HW Accelerator |--->| M NoC |<---------------+
> + +----------------+ +----------------+ |
> + | | +------------+
> + +-----+ +-------------+ V +------+ | |
> + | DDR | | +--------+ | PCIe | | |
> + +-----+ | | Slaves | +------+ | |
> + ^ ^ | +--------+ | | C NoC |
> + | | V V | |
> + +------------------+ +------------------------+ | | +-----+
> + | |-->| |-->| |-->| CPU |
> + | |-->| |<--| | +-----+
> + | Mem NoC | | S NoC | +------------+
> + | |<--| |---------+ |
> + | |<--| |<------+ | | +--------+
> + +------------------+ +------------------------+ | | +-->| Slaves |
> + ^ ^ ^ ^ ^ | | +--------+
> + | | | | | | V
> + +------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
> + | CPUs | | | GPU | | DSP | | Masters |-->| P NoC |-->| Slaves |
> + +------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
> + |
> + +-------+
> + | Modem |
> + +-------+
> +
> +Terminology
> +-----------
> +
> +Interconnect provider is the software definition of the interconnect hardware.
> +The interconnect providers on the above diagram are M NoC, S NoC, C NoC and Mem
> +NoC.

Should P NoC be part of that list?

> +
> +Interconnect node is the software definition of the interconnect hardware
> +port. Each interconnect provider consists of multiple interconnect nodes,
> +which are connected to other SoC components including other interconnect
> +providers. The point on the diagram where the CPUs connects to the memory is
> +called an interconnect node, which belongs to the Mem NoC interconnect provider.
> +
> +Interconnect endpoints are the first or the last element of the path. Every
> +endpoint is a node, but not every node is an endpoint.
> +
> +Interconnect path is everything between two endpoints including all the nodes
> +that have to be traversed to reach from a source to destination node. It may
> +include multiple master-slave pairs across several interconnect providers.
> +
> +Interconnect consumers are the entities which make use of the data paths exposed
> +by the providers. The consumers send requests to providers requesting various
> +throughput, latency and priority. Usually the consumers are device drivers, that
> +send request based on their needs. An example for a consumer is a video decoder
> +that supports various formats and image sizes.
> +
> +Interconnect providers
> +----------------------
> +
> +Interconnect provider is an entity that implements methods to initialize and
> +configure a interconnect bus hardware. The interconnect provider drivers should
> +be registered with the interconnect provider core.
> +
> +The interconnect framework provider API functions are documented in
> +.. kernel-doc:: include/linux/interconnect-provider.h
> +
> +Interconnect consumers
> +----------------------
> +
> +Interconnect consumers are the clients which use the interconnect APIs to
> +get paths between endpoints and set their bandwidth/latency/QoS requirements
> +for these interconnect paths.
> +
> +The interconnect framework consumer API functions are documented in
> +.. kernel-doc:: include/linux/interconnect.h
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 879dc0604cba..96a1db022cee 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -219,4 +219,6 @@ source "drivers/siox/Kconfig"
>
> source "drivers/slimbus/Kconfig"
>
> +source "drivers/interconnect/Kconfig"
> +
> endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 24cd47014657..0cca95740d9b 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -185,3 +185,4 @@ obj-$(CONFIG_TEE) += tee/
> obj-$(CONFIG_MULTIPLEXER) += mux/
> obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/
> obj-$(CONFIG_SIOX) += siox/
> +obj-$(CONFIG_INTERCONNECT) += interconnect/
> diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
> new file mode 100644
> index 000000000000..a261c7d41deb
> --- /dev/null
> +++ b/drivers/interconnect/Kconfig
> @@ -0,0 +1,10 @@
> +menuconfig INTERCONNECT
> + tristate "On-Chip Interconnect management support"
> + help
> + Support for management of the on-chip interconnects.
> +
> + This framework is designed to provide a generic interface for
> + managing the interconnects in a SoC.
> +
> + If unsure, say no.
> +
> diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
> new file mode 100644
> index 000000000000..5edf0ae80818
> --- /dev/null
> +++ b/drivers/interconnect/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_INTERCONNECT) += core.o
> diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
> new file mode 100644
> index 000000000000..6306e258b9b9
> --- /dev/null
> +++ b/drivers/interconnect/core.c
> @@ -0,0 +1,489 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Interconnect framework core driver
> + *
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#include <linux/device.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/interconnect.h>
> +#include <linux/interconnect-provider.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_IDR(icc_idr);
> +static LIST_HEAD(icc_provider_list);
> +static DEFINE_MUTEX(icc_provider_list_mutex);
> +static DEFINE_MUTEX(icc_path_mutex);
> +
> +/**
> + * 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
> + * @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;
> + u32 avg_bw;
> + u32 peak_bw;
> +};
> +
> +/**
> + * struct icc_path - interconnect path structure
> + * @num_nodes: number of hops (nodes)
> + * @reqs: array of the requests applicable to this path of nodes
> + */
> +struct icc_path {
> + size_t num_nodes;
> + struct icc_req reqs[0];
> +};
> +
> +static struct icc_node *node_find(const int id)
> +{
> + struct icc_node *node;
> +
> + node = idr_find(&icc_idr, id);
> +
> + return node;
> +}
> +
> +static struct icc_path *path_allocate(struct icc_node *node, ssize_t num_nodes)
> +{
> + struct icc_path *path;
> + size_t i;
> +
> + path = kzalloc(sizeof(*path) + num_nodes * sizeof(*path->reqs),
> + GFP_KERNEL);
> + if (!path)
> + return ERR_PTR(-ENOMEM);
> +
> + path->num_nodes = num_nodes;
> +
> + for (i = 0; i < num_nodes; i++) {
> + hlist_add_head(&path->reqs[i].req_node, &node->req_list);
> +
> + path->reqs[i].node = node;
> + /* reference to previous node was saved during path traversal */
> + node = node->reverse;
> + }
> +
> + return path;
> +}
> +
> +static struct icc_path *path_find(struct icc_node *src, struct icc_node *dst)
> +{
> + struct icc_node *node = NULL;
> + struct list_head traverse_list;
> + struct list_head edge_list;
> + struct list_head tmp_list;
> + size_t i, number = 0;
> + bool found = false;
> +
> + INIT_LIST_HEAD(&traverse_list);
> + INIT_LIST_HEAD(&edge_list);
> + INIT_LIST_HEAD(&tmp_list);
> +
> + list_add_tail(&src->search_list, &traverse_list);
> +
> + do {
> + list_for_each_entry(node, &traverse_list, search_list) {
> + if (node == dst) {
> + found = true;
> + list_add(&node->search_list, &tmp_list);
> + break;
> + }
> + for (i = 0; i < node->num_links; i++) {
> + struct icc_node *tmp = node->links[i];
> +
> + if (!tmp)
> + return ERR_PTR(-ENOENT);
> +
> + if (tmp->is_traversed)
> + continue;
> +
> + tmp->is_traversed = true;
> + tmp->reverse = node;
> + list_add_tail(&tmp->search_list, &edge_list);
> + }
> + }
> + if (found)
> + break;
> +
> + list_splice_init(&traverse_list, &tmp_list);
> + list_splice_init(&edge_list, &traverse_list);
> +
> + /* count the number of nodes */
> + number++;
> +
> + } while (!list_empty(&traverse_list));
> +
> + /* reset the traversed state */
> + list_for_each_entry(node, &tmp_list, search_list)
> + node->is_traversed = false;
> +
> + if (found)
> + return path_allocate(dst, number);
> +
> + return ERR_PTR(-EPROBE_DEFER);
> +}
> +
> +static int path_init(struct icc_path *path)
> +{
> + struct icc_node *node;
> + size_t i;
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> +
> + mutex_lock(&node->provider->lock);
> + node->provider->users++;
> + mutex_unlock(&node->provider->lock);
> + }
> +
> + return 0;
> +}
> +
> +static void node_aggregate(struct icc_node *node)
> +{
> + struct icc_req *r;
> + u32 agg_avg = 0;

Should this be u64 to avoid overflow in case of a large number of
constraints and high bandwidths?

> + u32 agg_peak = 0;
> +
> + hlist_for_each_entry(r, &node->req_list, req_node) {
> + /* sum(averages) and max(peaks) */
> + agg_avg += r->avg_bw;
> + agg_peak = max(agg_peak, r->peak_bw);
> + }
> +
> + node->avg_bw = agg_avg;

Is it really intended to store the sum of averages here rather than
the overall average?

> + node->peak_bw = agg_peak;
> +}
> +
> +static void provider_aggregate(struct icc_provider *provider, u32 *avg_bw,
> + u32 *peak_bw)
> +{
> + struct icc_node *n;
> + u32 agg_avg = 0;

See above.

> + u32 agg_peak = 0;
> +
> + /* aggregate for the interconnect provider */
> + list_for_each_entry(n, &provider->nodes, node_list) {
> + /* sum the average and max the peak */
> + agg_avg += n->avg_bw;
> + agg_peak = max(agg_peak, n->peak_bw);
> + }
> +
> + *avg_bw = agg_avg;

See above.

> + *peak_bw = agg_peak;
> +}
> +
> +static int constraints_apply(struct icc_path *path)
> +{
> + struct icc_node *next, *prev = NULL;
> + int i;
> +
> + for (i = 0; i < path->num_nodes; i++, prev = next) {
> + struct icc_provider *provider;
> + u32 avg_bw = 0;
> + u32 peak_bw = 0;
> + int ret;
> +
> + next = path->reqs[i].node;
> + /*
> + * Both endpoints should be valid master-slave pairs of the
> + * same interconnect provider that will be configured.
> + */
> + if (!next || !prev)
> + continue;
> +
> + if (next->provider != prev->provider)
> + continue;
> +
> + provider = next->provider;
> + mutex_lock(&provider->lock);
> +
> + /* aggregate requests for the provider */
> + provider_aggregate(provider, &avg_bw, &peak_bw);
> +
> + if (provider->set) {
> + /* set the constraints */
> + ret = provider->set(prev, next, avg_bw, peak_bw);
> + }
> +
> + mutex_unlock(&provider->lock);
> +
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * icc_set() - set constraints on an interconnect path between two endpoints
> + * @path: reference to the path returned by icc_get()
> + * @avg_bw: average bandwidth in kbps
> + * @peak_bw: peak bandwidth in kbps
> + *
> + * This function is used by an interconnect consumer to express its own needs
> + * in term of bandwidth and QoS for a previously requested path between two
> + * endpoints. The requests are aggregated and each node is updated accordingly.
> + *
> + * Returns 0 on success, or an approproate error code otherwise.
> + */
> +int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
> +{
> + struct icc_node *node;
> + size_t i;
> + int ret;
> +
> + if (!path)
> + return 0;
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> +
> + mutex_lock(&icc_path_mutex);
> +
> + /* update the consumer request for this path */
> + path->reqs[i].avg_bw = avg_bw;
> + path->reqs[i].peak_bw = peak_bw;
> +
> + /* aggregate requests for this node */
> + node_aggregate(node);
> +
> + mutex_unlock(&icc_path_mutex);
> + }
> +
> + ret = constraints_apply(path);
> + if (ret)
> + pr_err("interconnect: error applying constraints (%d)", ret);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(icc_set);
> +
> +/**
> + * icc_get() - return a handle for path between two endpoints
> + * @src_id: source device port id
> + * @dst_id: destination device port id
> + *
> + * 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 the they are not needed anymore.
> + *
> + * Return: icc_path pointer on success, or ERR_PTR() on error
> + */
> +struct icc_path *icc_get(const int src_id, const int dst_id)
> +{
> + struct icc_node *src, *dst;
> + struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
> +
> + src = node_find(src_id);
> + if (!src)
> + goto out;
> +
> + dst = node_find(dst_id);
> + if (!dst)
> + goto out;
> +
> + mutex_lock(&icc_path_mutex);
> + path = path_find(src, dst);
> + mutex_unlock(&icc_path_mutex);
> + if (IS_ERR(path))
> + goto out;
> +
> + path_init(path);
> +
> +out:
> + return path;
> +}
> +EXPORT_SYMBOL_GPL(icc_get);
> +
> +/**
> + * icc_put() - release the reference to the icc_path
> + * @path: 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.
> + */
> +void icc_put(struct icc_path *path)
> +{
> + struct icc_node *node;
> + size_t i;
> + int ret;
> +
> + if (!path || WARN_ON_ONCE(IS_ERR(path)))
> + return;
> +
> + ret = icc_set(path, 0, 0);
> + if (ret)
> + pr_err("%s: error (%d)\n", __func__, ret);
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> + hlist_del(&path->reqs[i].req_node);
> +
> + mutex_lock(&node->provider->lock);
> + node->provider->users--;
> + mutex_unlock(&node->provider->lock);
> + }
> +
> + kfree(path);
> +}
> +EXPORT_SYMBOL_GPL(icc_put);
> +
> +/**
> + * icc_node_create() - create a node
> + * @id: node id
> + *
> + * Return: icc_node pointer on success, or ERR_PTR() on error
> + */
> +struct icc_node *icc_node_create(int id)
> +{
> + struct icc_node *node;
> +
> + /* check if node already exists */
> + node = node_find(id);
> + if (node)
> + return node;
> +
> + node = kzalloc(sizeof(*node), GFP_KERNEL);
> + if (!node)
> + return ERR_PTR(-ENOMEM);
> +
> + id = idr_alloc(&icc_idr, node, id, id + 1, GFP_KERNEL);
> + if (WARN(id < 0, "couldn't get idr"))
> + return ERR_PTR(id);
> +
> + node->id = id;
> +
> + return node;
> +}
> +EXPORT_SYMBOL_GPL(icc_node_create);
> +
> +/**
> + * icc_link_create() - create a link between two nodes
> + * @src_id: source node id
> + * @dst_id: destination node id
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_link_create(struct icc_node *node, const int dst_id)
> +{
> + struct icc_node *dst;
> + struct icc_node **new;
> + int ret = 0;
> +
> + if (IS_ERR_OR_NULL(node))
> + return PTR_ERR(node);
> +
> + mutex_lock(&node->provider->lock);
> +
> + dst = node_find(dst_id);
> + if (!dst)
> + dst = icc_node_create(dst_id);
> +
> + new = krealloc(node->links,
> + (node->num_links + 1) * sizeof(*node->links),
> + GFP_KERNEL);
> + if (!new) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + node->links = new;
> + node->links[node->num_links++] = dst;
> +
> +out:
> + mutex_unlock(&node->provider->lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_link_create);
> +
> +/**
> + * icc_add_node() - add an interconnect node to interconnect provider
> + * @node: pointer to the interconnect node
> + * @provider: pointer to the interconnect provider
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider)
> +{
> + if (WARN_ON(!node))
> + return -EINVAL;
> +
> + if (WARN_ON(!provider))
> + return -EINVAL;
> +
> + node->provider = provider;
> +
> + mutex_lock(&provider->lock);
> + list_add_tail(&node->node_list, &provider->nodes);
> + mutex_unlock(&provider->lock);
> +
> + return 0;
> +}
> +
> +/**
> + * icc_add_provider() - add a new interconnect provider
> + * @icc_provider: the interconnect provider that will be added into topology
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_add_provider(struct icc_provider *provider)
> +{
> + if (WARN_ON(!provider))
> + return -EINVAL;
> +
> + if (WARN_ON(!provider->set))
> + return -EINVAL;
> +
> + mutex_init(&provider->lock);
> + INIT_LIST_HEAD(&provider->nodes);
> +
> + mutex_lock(&icc_provider_list_mutex);
> + list_add(&provider->provider_list, &icc_provider_list);
> + mutex_unlock(&icc_provider_list_mutex);
> +
> + dev_dbg(provider->dev, "interconnect provider added to topology\n");
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_add_provider);
> +
> +/**
> + * icc_del_provider() - delete previously added interconnect provider
> + * @icc_provider: the interconnect provider that will be removed from topology
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_del_provider(struct icc_provider *provider)
> +{
> + mutex_lock(&provider->lock);
> + if (provider->users) {
> + pr_warn("interconnect provider still has %d users\n",
> + provider->users);
> + }
> + mutex_unlock(&provider->lock);
> +
> + mutex_lock(&icc_provider_list_mutex);
> + list_del(&provider->provider_list);
> + mutex_unlock(&icc_provider_list_mutex);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_del_provider);
> +
> +MODULE_AUTHOR("Georgi Djakov <[email protected]");
> +MODULE_DESCRIPTION("Interconnect Driver Core");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h
> new file mode 100644
> index 000000000000..779b5b5b1306
> --- /dev/null
> +++ b/include/linux/interconnect-provider.h
> @@ -0,0 +1,109 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef _LINUX_INTERCONNECT_PROVIDER_H
> +#define _LINUX_INTERCONNECT_PROVIDER_H
> +
> +#include <linux/interconnect.h>
> +
> +struct icc_node;
> +
> +/**
> + * struct icc_provider - interconnect provider (controller) entity that might
> + * provide multiple interconnect controls
> + *
> + * @provider_list: list of the registered interconnect providers
> + * @nodes: internal list of the interconnect provider nodes
> + * @set: pointer to device specific set operation function
> + * @dev: the device this interconnect provider belongs to
> + * @lock: lock to provide consistency during aggregation/update of constraints
> + * @users: count of active users
> + * @data: pointer to private data
> + */
> +struct icc_provider {
> + struct list_head provider_list;
> + struct list_head nodes;
> + int (*set)(struct icc_node *src, struct icc_node *dst,
> + u32 avg_bw, u32 peak_bw);
> + struct device *dev;
> + struct mutex lock;
> + int users;
> + void *data;
> +};
> +
> +/**
> + * struct icc_node - entity that is part of the interconnect topology
> + *
> + * @id: platform specific node id
> + * @name: node name used in debugfs
> + * @links: a list of targets where we can go next when traversing
> + * @num_links: number of links to other interconnect nodes
> + * @provider: points to the interconnect provider of this node
> + * @node_list: list of interconnect nodes associated with @provider
> + * @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
> + * @peak_bw: aggregated value of peak bandwidth
> + * @data: pointer to private data
> + */
> +struct icc_node {
> + int id;
> + const char *name;
> + struct icc_node **links;
> + size_t num_links;
> +
> + struct icc_provider *provider;
> + struct list_head node_list;
> + struct list_head orphan_list;

orphan_list is not used (nor documented)

> + struct list_head search_list;
> + struct icc_node *reverse;
> + bool is_traversed;
> + struct hlist_head req_list;
> + u32 avg_bw;
> + u32 peak_bw;
> + void *data;
> +};
> +
> +#if IS_ENABLED(CONFIG_INTERCONNECT)
> +
> +struct icc_node *icc_node_create(int id);
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider);
> +int icc_link_create(struct icc_node *node, const int dst_id);
> +int icc_add_provider(struct icc_provider *provider);
> +int icc_del_provider(struct icc_provider *provider);
> +
> +#else
> +
> +static inline struct icc_node *icc_node_create(int id)
> +{
> + return ERR_PTR(-ENOTSUPP);
> +}
> +
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int icc_link_create(struct icc_node *node, const int dst_id)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int icc_add_provider(struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int icc_del_provider(struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +#endif /* CONFIG_INTERCONNECT */
> +
> +#endif /* _LINUX_INTERCONNECT_PROVIDER_H */
> diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h
> new file mode 100644
> index 000000000000..5a7cf72b76a5
> --- /dev/null
> +++ b/include/linux/interconnect.h
> @@ -0,0 +1,40 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef _LINUX_INTERCONNECT_H
> +#define _LINUX_INTERCONNECT_H
> +
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +
> +struct icc_path;
> +struct device;
> +
> +#if IS_ENABLED(CONFIG_INTERCONNECT)
> +
> +struct icc_path *icc_get(const int src_id, const int dst_id);
> +void icc_put(struct icc_path *path);
> +int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw);
> +
> +#else
> +
> +static inline struct icc_path *icc_get(const int src_id, const int dst_id)
> +{
> + return NULL;
> +}
> +
> +static inline void icc_put(struct icc_path *path)
> +{
> +}
> +
> +static inline int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
> +{
> + return 0;
> +}
> +
> +#endif /* CONFIG_INTERCONNECT */
> +
> +#endif /* _LINUX_INTERCONNECT_H */

2018-04-12 13:10:10

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

Hi Matthias,

On 04/06/2018 08:38 PM, Matthias Kaehlcke wrote:
> On Fri, Mar 09, 2018 at 11:09:52PM +0200, Georgi Djakov wrote:
>> This patch introduce a new API to get requirements and configure the
>> interconnect buses across the entire chipset to fit with the current
>> demand.
>>
>> The API is using a consumer/provider-based model, where the providers are
>> the interconnect buses and the consumers could be various drivers.
>> The consumers request interconnect resources (path) between endpoints and
>> set the desired constraints on this data flow path. The providers receive
>> requests from consumers and aggregate these requests for all master-slave
>> pairs on that path. Then the providers configure each participating in the
>> topology node according to the requested data flow path, physical links and
>> constraints. The topology could be complicated and multi-tiered and is SoC
>> specific.
>>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
>> Documentation/interconnect/interconnect.rst | 96 ++++++
>> drivers/Kconfig | 2 +
>> drivers/Makefile | 1 +
>> drivers/interconnect/Kconfig | 10 +
>> drivers/interconnect/Makefile | 1 +
>> drivers/interconnect/core.c | 489 ++++++++++++++++++++++++++++
>> include/linux/interconnect-provider.h | 109 +++++++
>> include/linux/interconnect.h | 40 +++
>> 8 files changed, 748 insertions(+)
>> create mode 100644 Documentation/interconnect/interconnect.rst
>> create mode 100644 drivers/interconnect/Kconfig
>> create mode 100644 drivers/interconnect/Makefile
>> create mode 100644 drivers/interconnect/core.c
>> create mode 100644 include/linux/interconnect-provider.h
>> create mode 100644 include/linux/interconnect.h
>>
>> diff --git a/Documentation/interconnect/interconnect.rst b/Documentation/interconnect/interconnect.rst
>> new file mode 100644
>> index 000000000000..23eba68e8424
>> --- /dev/null
>> +++ b/Documentation/interconnect/interconnect.rst

[..]

>> +Terminology
>> +-----------
>> +
>> +Interconnect provider is the software definition of the interconnect hardware.
>> +The interconnect providers on the above diagram are M NoC, S NoC, C NoC and Mem
>> +NoC.
>
> Should P NoC be part of that list?
>

Yes, it should be!

>> +
>> +Interconnect node is the software definition of the interconnect hardware
>> +port. Each interconnect provider consists of multiple interconnect nodes,
>> +which are connected to other SoC components including other interconnect
>> +providers. The point on the diagram where the CPUs connects to the memory is
>> +called an interconnect node, which belongs to the Mem NoC interconnect provider.
>> +
>> +Interconnect endpoints are the first or the last element of the path. Every
>> +endpoint is a node, but not every node is an endpoint.
>> +
>> +Interconnect path is everything between two endpoints including all the nodes
>> +that have to be traversed to reach from a source to destination node. It may
>> +include multiple master-slave pairs across several interconnect providers.
>> +
>> +Interconnect consumers are the entities which make use of the data paths exposed
>> +by the providers. The consumers send requests to providers requesting various
>> +throughput, latency and priority. Usually the consumers are device drivers, that
>> +send request based on their needs. An example for a consumer is a video decoder
>> +that supports various formats and image sizes.
>> +
>> +Interconnect providers
>> +----------------------

[..]

>> +static void node_aggregate(struct icc_node *node)
>> +{
>> + struct icc_req *r;
>> + u32 agg_avg = 0;
>
> Should this be u64 to avoid overflow in case of a large number of
> constraints and high bandwidths?

These values are proposed to be in kbps and u32 seems to be enough for
now, but in the future we can switch to u64 if needed.

>
>> + u32 agg_peak = 0;
>> +
>> + hlist_for_each_entry(r, &node->req_list, req_node) {
>> + /* sum(averages) and max(peaks) */
>> + agg_avg += r->avg_bw;
>> + agg_peak = max(agg_peak, r->peak_bw);
>> + }
>> +
>> + node->avg_bw = agg_avg;
>
> Is it really intended to store the sum of averages here rather than
> the overall average?

Yes, the intention is to sum all the averages in total, so that the
hardware is set in a state that would be able to handle the total
bandwidth passing through a node.

Also in the next version of this patch i have changed this part a bit,
so that the aggregation could be customized and made platform specific,
as different platforms could use their own aggregation algorithms other
than the default sum/max.

>
>> + node->peak_bw = agg_peak;
>> +}
>> +
>> +static void provider_aggregate(struct icc_provider *provider, u32 *avg_bw,
>> + u32 *peak_bw)
>> +{
>> + struct icc_node *n;
>> + u32 agg_avg = 0;
>
> See above.
>
>> + u32 agg_peak = 0;
>> +
>> + /* aggregate for the interconnect provider */
>> + list_for_each_entry(n, &provider->nodes, node_list) {
>> + /* sum the average and max the peak */
>> + agg_avg += n->avg_bw;
>> + agg_peak = max(agg_peak, n->peak_bw);
>> + }
>> +
>> + *avg_bw = agg_avg;
>
> See above.
>
>> + *peak_bw = agg_peak;
>> +}
>> +

[..]

>> +/**
>> + * struct icc_node - entity that is part of the interconnect topology
>> + *
>> + * @id: platform specific node id
>> + * @name: node name used in debugfs
>> + * @links: a list of targets where we can go next when traversing
>> + * @num_links: number of links to other interconnect nodes
>> + * @provider: points to the interconnect provider of this node
>> + * @node_list: list of interconnect nodes associated with @provider
>> + * @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
>> + * @peak_bw: aggregated value of peak bandwidth
>> + * @data: pointer to private data
>> + */
>> +struct icc_node {
>> + int id;
>> + const char *name;
>> + struct icc_node **links;
>> + size_t num_links;
>> +
>> + struct icc_provider *provider;
>> + struct list_head node_list;
>> + struct list_head orphan_list;
>
> orphan_list is not used (nor documented)

It's not used anymore. Will remove!

Thanks,
Georgi

2018-04-12 13:13:09

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 5/7] interconnect: qcom: Add msm8916 interconnect provider driver

Hi Matthias,

On 04/06/2018 01:58 AM, Matthias Kaehlcke wrote:
> On Fri, Mar 09, 2018 at 11:09:56PM +0200, Georgi Djakov wrote:
>> Add driver for the Qualcomm interconnect buses found in msm8916 based
>> platforms.
>>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
>> drivers/interconnect/Kconfig | 5 +
>> drivers/interconnect/Makefile | 1 +
>> drivers/interconnect/qcom/Kconfig | 11 +
>> drivers/interconnect/qcom/Makefile | 2 +
>> drivers/interconnect/qcom/msm8916.c | 482 ++++++++++++++++++++++++++++++++++++
>> include/linux/interconnect/qcom.h | 350 ++++++++++++++++++++++++++
>> 6 files changed, 851 insertions(+)
>> create mode 100644 drivers/interconnect/qcom/Kconfig
>> create mode 100644 drivers/interconnect/qcom/msm8916.c
>> create mode 100644 include/linux/interconnect/qcom.h
>>

[..]

>> +
>> +DEFINE_QNODE(mas_video, 63, 8, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
>
> IIUC this should be something like:
>
> +DEFINE_QNODE(mas_video, MASTER_VIDEO_P0, MASTER_ADM_PORT1, 16, 1, -1,
> -1, QCOM_QOS_MODE_BYPASS, 2, SNOC_MM_INT_0, SNOC_MM_INT_2);
>
> and similar for the other nodes.
>

Yes, exactly.

>> +DEFINE_QNODE(mas_jpeg, 62, 6, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
>> +DEFINE_QNODE(mas_vfe, 29, 9, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10001, 10002);
>> +DEFINE_QNODE(mas_mdp, 22, 7, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
>> +DEFINE_QNODE(mas_qdss_bam, 53, 11, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10009);
>> +DEFINE_QNODE(mas_snoc_cfg, 54, 0, 16, 0, 20, -1, QCOM_QOS_MODE_BYPASS, 1, 10009);
>> +DEFINE_QNODE(mas_qdss_etr, 60, 10, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10009);
>> +DEFINE_QNODE(mm_int_0, 10000, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10003);
>> +DEFINE_QNODE(mm_int_1, 10001, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10003);
>> +DEFINE_QNODE(mm_int_2, 10002, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10004);
>> +DEFINE_QNODE(mm_int_bimc, 10003, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10008);
>> +DEFINE_QNODE(snoc_int_0, 10004, 0, 8, 0, 99, 130, QCOM_QOS_MODE_FIXED, 3, 588, 519, 10027);
>> +DEFINE_QNODE(snoc_int_1, 10005, 0, 8, 0, 100, 131, QCOM_QOS_MODE_FIXED, 3, 517, 663, 664);
>> +DEFINE_QNODE(snoc_int_bimc, 10006, 0, 8, 0, 101, 132, QCOM_QOS_MODE_FIXED, 1, 10007);
>> +DEFINE_QNODE(snoc_bimc_0_mas, 10007, 0, 8, 0, 3, -1, QCOM_QOS_MODE_FIXED, 1, 10025);
>> +DEFINE_QNODE(snoc_bimc_1_mas, 10008, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10026);
>> +DEFINE_QNODE(qdss_int, 10009, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 2, 10004, 10006);
>> +DEFINE_QNODE(bimc_snoc_slv, 10017, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 2, 10004, 10005);
>> +DEFINE_QNODE(snoc_pnoc_mas, 10027, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10028);
>> +DEFINE_QNODE(pnoc_snoc_slv, 10011, 0, 8, 0, -1, 45, QCOM_QOS_MODE_FIXED, 3, 10004, 10006, 10005);
>> +DEFINE_QNODE(slv_srvc_snoc, 587, 0, 8, 0, -1, 29, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_qdss_stm, 588, 0, 4, 0, -1, 30, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_imem, 519, 0, 8, 0, -1, 26, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_apss, 517, 0, 4, 0, -1, 20, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_cats_0, 663, 0, 16, 0, -1, 106, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_cats_1, 664, 0, 8, 0, -1, 107, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(mas_apss, 1, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
>> +DEFINE_QNODE(mas_tcu0, 104, 5, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
>> +DEFINE_QNODE(mas_tcu1, 105, 6, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
>> +DEFINE_QNODE(mas_gfx, 26, 2, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
>> +DEFINE_QNODE(bimc_snoc_mas, 10016, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10017);
>> +DEFINE_QNODE(snoc_bimc_0_slv, 10025, 0, 8, 0, -1, 24, QCOM_QOS_MODE_FIXED, 1, 512);
>> +DEFINE_QNODE(snoc_bimc_1_slv, 10026, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 512);
>> +DEFINE_QNODE(slv_ebi_ch0, 512, 0, 8, 0, -1, 0, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_apps_l2, 514, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(snoc_pnoc_slv, 10028, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10012);
>> +DEFINE_QNODE(pnoc_int_0, 10012, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 8, 10010, 10018, 10019, 10020, 10021, 10022, 10023, 10024);
>> +DEFINE_QNODE(pnoc_int_1, 10013, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10010);
>> +DEFINE_QNODE(pnoc_m_0, 10014, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10012);
>> +DEFINE_QNODE(pnoc_m_1, 10015, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10010);
>> +DEFINE_QNODE(pnoc_s_0, 10018, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 620, 624, 579, 622, 521);
>> +DEFINE_QNODE(pnoc_s_1, 10019, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 627, 625, 535, 577, 618);
>> +DEFINE_QNODE(pnoc_s_2, 10020, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 533, 630, 629, 641, 632);
>> +DEFINE_QNODE(pnoc_s_3, 10021, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 536, 647, 636, 635, 634);
>> +DEFINE_QNODE(pnoc_s_4, 10022, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 596, 589, 590);
>> +DEFINE_QNODE(pnoc_s_8, 10023, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 614, 606, 613);
>> +DEFINE_QNODE(pnoc_s_9, 10024, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 609, 522, 598);
>> +DEFINE_QNODE(slv_imem_cfg, 627, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_crypto_0_cfg, 625, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_msg_ram, 535, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_pdm, 577, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_prng, 618, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_clk_ctl, 620, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_mss, 521, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_tlmm, 624, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_tcsr, 579, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_security, 622, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_spdm, 533, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_pnoc_cfg, 641, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_pmic_arb, 632, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_bimc_cfg, 629, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_boot_rom, 630, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_mpm, 536, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_qdss_cfg, 635, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_rbcpr_cfg, 636, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_snoc_cfg, 647, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_dehr_cfg, 634, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_venus_cfg, 596, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_display_cfg, 590, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_camera_cfg, 589, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_usb_hs, 614, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_sdcc_1, 606, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_blsp_1, 613, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_sdcc_2, 609, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_gfx_cfg, 598, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(slv_audio, 522, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
>> +DEFINE_QNODE(mas_blsp_1, 86, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10015);
>> +DEFINE_QNODE(mas_spdm, 36, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
>> +DEFINE_QNODE(mas_dehr, 75, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
>> +DEFINE_QNODE(mas_audio, 15, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
>> +DEFINE_QNODE(mas_usb_hs, 87, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10015);
>> +DEFINE_QNODE(mas_pnoc_crypto_0, 55, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
>> +DEFINE_QNODE(mas_pnoc_sdcc_1, 78, 7, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
>> +DEFINE_QNODE(mas_pnoc_sdcc_2, 81, 8, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
>> +DEFINE_QNODE(pnoc_snoc_mas, 10010, 0, 8, 0, 29, -1, QCOM_QOS_MODE_FIXED, 1, 10011);
>> +

[..]

>> +static const struct of_device_id qnoc_of_match[] = {
>> + { .compatible = "qcom,msm8916-pnoc", .data = &msm8916_pnoc },
>> + { .compatible = "qcom,msm8916-snoc", .data = &msm8916_snoc },
>> + { .compatible = "qcom,msm8916-bimc", .data = &msm8916_bimc },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, qnoc_of_match);
>> +
>> +static struct platform_driver qnoc_driver = {
>> + .probe = qnoc_probe,
>> + .remove = qnoc_remove,
>> + .driver = {
>> + .name = "qnoc-msm8916",
>> + .of_match_table = qnoc_of_match,
>> + },
>> +};
>> +module_platform_driver(qnoc_driver);
>> +MODULE_AUTHOR("Georgi Djakov <[email protected]>");
>> +MODULE_DESCRIPTION("Qualcomm msm8916 NoC driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/interconnect/qcom.h
>> b/include/linux/interconnect/qcom.h
>
> Are the values in this file valid for all Qualcomm SoCs or specific to
> the MSM8916? In the latter case you might want to include the chip
> (family) in the name of the constant.

Currently i am trying to use common ids, which is useful if you have
same drivers for different SoCs and use only platform data. But the goal
here is to fully move to DT and then we could use arbitrary ids.

Thanks,
Georgi


2018-04-12 13:19:49

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 2/7] dt-bindings: Introduce interconnect provider bindings

On 09/03/2018 22:09, Georgi Djakov wrote:
> This binding is intended to represent the interconnect hardware present
> in some of the modern SoCs. Currently it consists only of a binding for
> the interconnect hardware devices (provider).
>
> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> .../bindings/interconnect/interconnect.txt | 47 ++++++++++++++++++++++
> 1 file changed, 47 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/interconnect/interconnect.txt
>
> diff --git a/Documentation/devicetree/bindings/interconnect/interconnect.txt b/Documentation/devicetree/bindings/interconnect/interconnect.txt
> new file mode 100644
> index 000000000000..70612bb201e4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interconnect/interconnect.txt
> @@ -0,0 +1,47 @@
> +Interconnect Provider Device Tree Bindings
> +=========================================
> +
> +The purpose of this document is to define a common set of generic interconnect
> +providers/consumers properties.
> +
> +
> += interconnect providers =
> +
> +The interconnect provider binding is intended to represent the interconnect
> +controllers in the system. Each provider registers a set of interconnect
> +nodes, which expose the interconnect related capabilities of the interconnect
> +to consumer drivers. These capabilities can be throughput, latency, priority
> +etc. The consumer drivers set constraints on interconnect path (or endpoints)
> +depending on the usecase. Interconnect providers can also be interconnect
> +consumers, such as in the case where two network-on-chip fabrics interface
> +directly

Hi,

Can't we specify the number of cells for the phandle ? It should be aligned with other consumer/provider bindings.

Neil

> +
> +Required properties:
> +- compatible : contains the interconnect provider vendor specific compatible
> + string
> +- reg : register space of the interconnect controller hardware
> +
> +Examples:
> +
> + snoc: snoc@580000 {
> + compatible = "qcom,msm8916-snoc";
> + reg = <0x580000 0x14000>;
> + clock-names = "bus_clk", "bus_a_clk";
> + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, <&rpmcc RPM_SMD_SNOC_A_CLK>;
> + status = "okay";
> + };
> + bimc: bimc@400000 {
> + compatible = "qcom,msm8916-bimc";
> + reg = <0x400000 0x62000>;
> + clock-names = "bus_clk", "bus_a_clk";
> + clocks = <&rpmcc RPM_SMD_BIMC_CLK>, <&rpmcc RPM_SMD_BIMC_A_CLK>;
> + status = "okay";
> + };
> + pnoc: pnoc@500000 {
> + compatible = "qcom,msm8916-pnoc";
> + reg = <0x500000 0x11000>;
> + clock-names = "bus_clk", "bus_a_clk";
> + clocks = <&rpmcc RPM_SMD_PCNOC_CLK>, <&rpmcc RPM_SMD_PCNOC_A_CLK>;
> + status = "okay";
> + };
> +
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>


2018-05-11 21:30:39

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v4 7/7] interconnect: Allow endpoints translation via DT

On Fri, Mar 9, 2018 at 1:10 PM Georgi Djakov <[email protected]>
wrote:

> Currently we support only platform data for specifying the interconnect
> endpoints. As now the endpoints are hard-coded into the consumer driver
> this may leed to complications when a single driver is used by multiple

Nit: s/leed/lead/

-Evan

2018-05-11 21:31:57

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v4 4/7] interconnect: qcom: Add RPM communication

On Fri, Mar 9, 2018 at 1:11 PM Georgi Djakov <[email protected]>
wrote:

> On some Qualcomm SoCs, there is a remote processor, which controls some of
> the Network-On-Chip interconnect resources. Other CPUs express their needs
> by communicating with this processor. Add a driver to handle comminication
> with this remote processor.

> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> .../devicetree/bindings/interconnect/qcom-smd.txt | 31 ++++++++
> drivers/interconnect/qcom/Makefile | 1 +
> drivers/interconnect/qcom/smd-rpm.c | 90
++++++++++++++++++++++
> drivers/interconnect/qcom/smd-rpm.h | 15 ++++
> 4 files changed, 137 insertions(+)
> create mode 100644
Documentation/devicetree/bindings/interconnect/qcom-smd.txt
> create mode 100644 drivers/interconnect/qcom/Makefile
> create mode 100644 drivers/interconnect/qcom/smd-rpm.c
> create mode 100644 drivers/interconnect/qcom/smd-rpm.h

> diff --git a/Documentation/devicetree/bindings/interconnect/qcom-smd.txt
b/Documentation/devicetree/bindings/interconnect/qcom-smd.txt
> new file mode 100644
> index 000000000000..14e83ed7019b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interconnect/qcom-smd.txt
> @@ -0,0 +1,31 @@
> +Qualcomm SMD-RPM interconnect driver binding
> +------------------------------------------------
> +The RPM is a dedicated hardware engine for managing the shared
> +SoC resources in order to keep the lowest power profile. It
> +communicates with other hardware subsystems via shared memory
> +and accepts requests for various resources.

You never say what RPM or SMD stands for. RPM is Resource Power Manager,
right? But I'm not in the know about SMD. Can you define these somewhere?

-Evan

2018-05-11 21:32:09

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v4 5/7] interconnect: qcom: Add msm8916 interconnect provider driver

Hi Georgi,

On Fri, Mar 9, 2018 at 1:11 PM Georgi Djakov <[email protected]>
wrote:

> Add driver for the Qualcomm interconnect buses found in msm8916 based
> platforms.

> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> drivers/interconnect/Kconfig | 5 +
> drivers/interconnect/Makefile | 1 +
> drivers/interconnect/qcom/Kconfig | 11 +
> drivers/interconnect/qcom/Makefile | 2 +
> drivers/interconnect/qcom/msm8916.c | 482
++++++++++++++++++++++++++++++++++++
> include/linux/interconnect/qcom.h | 350 ++++++++++++++++++++++++++
> 6 files changed, 851 insertions(+)
> create mode 100644 drivers/interconnect/qcom/Kconfig
> create mode 100644 drivers/interconnect/qcom/msm8916.c
> create mode 100644 include/linux/interconnect/qcom.h
...
> diff --git a/drivers/interconnect/qcom/msm8916.c
b/drivers/interconnect/qcom/msm8916.c
> new file mode 100644
> index 000000000000..d5b54f8261c8
> --- /dev/null
> +++ b/drivers/interconnect/qcom/msm8916.c
...
> +static int qnoc_probe(struct platform_device *pdev)
> +{
> + const struct qcom_icc_desc *desc;
> + struct qcom_icc_node **qnodes;
> + struct qcom_icc_provider *qp;
> + struct resource *res;
> + struct icc_provider *provider;
> + size_t num_nodes, i;
> + int ret;
> +
> + /* wait for RPM */
> + if (!qcom_icc_rpm_smd_available())
> + return -EPROBE_DEFER;
> +
> + desc = of_device_get_match_data(&pdev->dev);
> + if (!desc)
> + return -EINVAL;
> +
> + qnodes = desc->nodes;
> + num_nodes = desc->num_nodes;
> +
> + qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
> + if (!qp)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + qp->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(qp->base))
> + return PTR_ERR(qp->base);
> +
> + qp->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
> + if (IS_ERR(qp->bus_clk))
> + return PTR_ERR(qp->bus_clk);
> +
> + qp->bus_a_clk = devm_clk_get(&pdev->dev, "bus_a_clk");
> + if (IS_ERR(qp->bus_a_clk))
> + return PTR_ERR(qp->bus_a_clk);
> +
> + provider = &qp->provider;
> + provider->dev = &pdev->dev;
> + provider->set = &qcom_icc_set;
> + INIT_LIST_HEAD(&provider->nodes);
> + provider->data = qp;
> +
> + ret = icc_add_provider(provider);
> + if (ret) {
> + dev_err(&pdev->dev, "error adding interconnect
provider\n");
> + return ret;
> + }
> +
> + for (i = 0; i < num_nodes; i++) {
> + struct icc_node *node;
> + int ret;
> + size_t j;
> +
> + node = icc_node_create(qnodes[i]->id);
> + if (IS_ERR(node)) {
> + ret = PTR_ERR(node);
> + goto err;
> + }
> +
> + node->name = qnodes[i]->name;
> + node->data = qnodes[i];
> + icc_node_add(node, provider);
> +
> + dev_dbg(&pdev->dev, "registered node %p %s %d\n", node,
> + qnodes[i]->name, node->id);
> +
> + /* populate links */
> + for (j = 0; j < qnodes[i]->num_links; j++)
> + if (qnodes[i]->links[j])
> + icc_link_create(node,
qnodes[i]->links[j]);
> +
> + ret = qcom_icc_init(node);
> + if (ret)
> + dev_err(&pdev->dev, "%s init error (%d)\n",
node->name,
> + ret);

Don't you want to call qcom_icc_init before icc_link_create? As soon as
icc_link_create is called, the node is connected to the graph, and
qcom_icc_set can be called.

> + }
> +
> + platform_set_drvdata(pdev, provider);
> +
> + return ret;
> +err:
> + icc_del_provider(provider);
> + return ret;
> +}
> +
> +static int qnoc_remove(struct platform_device *pdev)
> +{
> + struct icc_provider *provider = platform_get_drvdata(pdev);
> +
> + icc_del_provider(provider);

Presumably in the framework nodes and links ought to get cleaned up
somewhere too, right? As it is now, the devm code frees provider when this
device is removed, even though provider is still very connected in the
graph via the nodes and links.

> +
> + return 0;
> +}
> +
> +static const struct of_device_id qnoc_of_match[] = {
> + { .compatible = "qcom,msm8916-pnoc", .data = &msm8916_pnoc },
> + { .compatible = "qcom,msm8916-snoc", .data = &msm8916_snoc },
> + { .compatible = "qcom,msm8916-bimc", .data = &msm8916_bimc },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, qnoc_of_match);
> +
> +static struct platform_driver qnoc_driver = {
> + .probe = qnoc_probe,
> + .remove = qnoc_remove,
> + .driver = {
> + .name = "qnoc-msm8916",
> + .of_match_table = qnoc_of_match,
> + },
> +};
> +module_platform_driver(qnoc_driver);
> +MODULE_AUTHOR("Georgi Djakov <[email protected]>");
> +MODULE_DESCRIPTION("Qualcomm msm8916 NoC driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/interconnect/qcom.h
b/include/linux/interconnect/qcom.h
> new file mode 100644
> index 000000000000..2cd378d2f575
> --- /dev/null
> +++ b/include/linux/interconnect/qcom.h
> @@ -0,0 +1,350 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Qualcomm interconnect IDs
> + *
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef __QCOM_INTERCONNECT_IDS_H
> +#define __QCOM_INTERCONNECT_IDS_H
> +
> +#define FAB_BIMC 0
> +#define FAB_SYS_NOC 1024
> +#define FAB_MMSS_NOC 2048
> +#define FAB_OCMEM_NOC 3072
> +#define FAB_PERIPH_NOC 4096
> +#define FAB_CONFIG_NOC 5120
> +#define FAB_OCMEM_VNOC 6144

Looks like you're still following that downstream convention of NoCs being
divisible by 1024, masters in 1-512, and slaves in 512-1023, then I don't
know about the 10000s. This was documented somewhere downstream, can you
add that documentation somewhere in here?

-Evan

2018-05-11 21:32:19

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

Hi Georgi,

On Fri, Mar 9, 2018 at 1:12 PM Georgi Djakov <[email protected]>
wrote:

> This patch introduce a new API to get requirements and configure the
> interconnect buses across the entire chipset to fit with the current
> demand.

> The API is using a consumer/provider-based model, where the providers are
> the interconnect buses and the consumers could be various drivers.
> The consumers request interconnect resources (path) between endpoints and
> set the desired constraints on this data flow path. The providers receive
> requests from consumers and aggregate these requests for all master-slave
> pairs on that path. Then the providers configure each participating in the
> topology node according to the requested data flow path, physical links
and
> constraints. The topology could be complicated and multi-tiered and is SoC
> specific.

> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> Documentation/interconnect/interconnect.rst | 96 ++++++
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/interconnect/Kconfig | 10 +
> drivers/interconnect/Makefile | 1 +
> drivers/interconnect/core.c | 489
++++++++++++++++++++++++++++
> include/linux/interconnect-provider.h | 109 +++++++
> include/linux/interconnect.h | 40 +++
> 8 files changed, 748 insertions(+)
> create mode 100644 Documentation/interconnect/interconnect.rst
> create mode 100644 drivers/interconnect/Kconfig
> create mode 100644 drivers/interconnect/Makefile
> create mode 100644 drivers/interconnect/core.c
> create mode 100644 include/linux/interconnect-provider.h
> create mode 100644 include/linux/interconnect.h

...
> diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
> new file mode 100644
> index 000000000000..6306e258b9b9
> --- /dev/null
> +++ b/drivers/interconnect/core.c
> @@ -0,0 +1,489 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Interconnect framework core driver
> + *
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#include <linux/device.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/interconnect.h>
> +#include <linux/interconnect-provider.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_IDR(icc_idr);
> +static LIST_HEAD(icc_provider_list);
> +static DEFINE_MUTEX(icc_provider_list_mutex);
> +static DEFINE_MUTEX(icc_path_mutex);
> +
> +/**
> + * 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
> + * @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;
> + u32 avg_bw;
> + u32 peak_bw;
> +};
> +
> +/**
> + * struct icc_path - interconnect path structure
> + * @num_nodes: number of hops (nodes)
> + * @reqs: array of the requests applicable to this path of nodes
> + */
> +struct icc_path {
> + size_t num_nodes;
> + struct icc_req reqs[0];
> +};
> +
> +static struct icc_node *node_find(const int id)
> +{
> + struct icc_node *node;
> +
> + node = idr_find(&icc_idr, id);
> +
> + return node;
> +}
> +
> +static struct icc_path *path_allocate(struct icc_node *node, ssize_t
num_nodes)
> +{

So node is really the destination, correct? Then we use ->reverse to walk
backwards num_nodes steps towards the source. It might increase readability
to call the parameter dest, then assign that to a local called node for
traversal.

> + struct icc_path *path;
> + size_t i;
> +
> + path = kzalloc(sizeof(*path) + num_nodes * sizeof(*path->reqs),
> + GFP_KERNEL);
> + if (!path)
> + return ERR_PTR(-ENOMEM);
> +
> + path->num_nodes = num_nodes;
> +
> + for (i = 0; i < num_nodes; i++) {
> + hlist_add_head(&path->reqs[i].req_node, &node->req_list);
> +
> + path->reqs[i].node = node;
> + /* reference to previous node was saved during path
traversal */
> + node = node->reverse;
> + }
> +
> + return path;
> +}
> +
> +static struct icc_path *path_find(struct icc_node *src, struct icc_node
*dst)
> +{
> + struct icc_node *node = NULL;
> + struct list_head traverse_list;
> + struct list_head edge_list;
> + struct list_head tmp_list;
> + size_t i, number = 0;
> + bool found = false;
> +
> + INIT_LIST_HEAD(&traverse_list);
> + INIT_LIST_HEAD(&edge_list);
> + INIT_LIST_HEAD(&tmp_list);

tmp_list is really the list of nodes you've already visited and need to
remember to reset is_traversed for. Maybe calling this done_list or
visited_list would be more descriptive.

> +
> + list_add_tail(&src->search_list, &traverse_list);

For added paranoia, you could set src->reverse to NULL so that somebody
elsewhere who had a bug in their back-traversal would fall off the end,
rather than into some previous scrapped path.

> +
> + do {
> + list_for_each_entry(node, &traverse_list, search_list) {
> + if (node == dst) {
> + found = true;
> + list_add(&node->search_list, &tmp_list);
> + break;
> + }
> + for (i = 0; i < node->num_links; i++) {
> + struct icc_node *tmp = node->links[i];
> +
> + if (!tmp)
> + return ERR_PTR(-ENOENT);

You just bail out here, but never clean up the nodes is_traversed, which
will ruin later searches. Maybe a goto towards the common cleanup path?

> +
> + if (tmp->is_traversed)
> + continue;
> +
> + tmp->is_traversed = true;
> + tmp->reverse = node;
> + list_add_tail(&tmp->search_list,
&edge_list);
> + }
> + }
> + if (found)
> + break;
> +
> + list_splice_init(&traverse_list, &tmp_list);
> + list_splice_init(&edge_list, &traverse_list);
> +
> + /* count the number of nodes */
> + number++;

Depth might be a better name for this, since this really counts the hops
away from the source, rather than the number of nodes you've processed.

> +
> + } while (!list_empty(&traverse_list));
> +
> + /* reset the traversed state */
> + list_for_each_entry(node, &tmp_list, search_list)
> + node->is_traversed = false;
> +
> + if (found)
> + return path_allocate(dst, number);
> +
> + return ERR_PTR(-EPROBE_DEFER);
> +}
> +
> +static int path_init(struct icc_path *path)
> +{
> + struct icc_node *node;
> + size_t i;
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> +
> + mutex_lock(&node->provider->lock);
> + node->provider->users++;
> + mutex_unlock(&node->provider->lock);
> + }
> +
> + return 0;
> +}

This function cannot fail, nor do you check its return value, so you should
change the return type to void.

I'm wondering if the locking here is a little sketchy. I was in the process
of typing a suggestion that you call path_init from within path_find, since
it seemed weird to have this gray zone of a path without its reference
counts, when I noticed the locks.

I can't evaluate fully, since the implementation seems to be missing
icc_node_remove, a critical function in terms of evaluating the locks. You
have an icc_del_provider, but its warning of if (provider->users) is pretty
weak, since without node removal provider->users could easily be
incremented after the provider lock is released. It also leaks all of its
nodes, since there's no way to remove them.

Here's my suggestion as far as the locking goes:
* To add or remove links/nodes from the graph, you're going to need to hold
a global lock to avoid colliding with traversals. You've already got an
icc_path_mutex, so that would work.
* icc_link_create needs to hold the global icc_path_mutex, since it's
messing with arrays and connections used in path traversal, and doesn't
need to hold the provider lock, since it's not changing anything there.
* The presumably upcoming icc_link_destroy, or its parent icc_node_destroy,
also needs to hold the global lock. node_destroy may also need the provider
lock in symmetry with icc_node_add.
* Provider->users will be protected under the global icc_path_mutex, rather
than the provider lock. Then move path_init into path_find, or inline it
into path_allocate.
* Once you do that, provider->lock is now only protecting its node list.
For now, it's probably more efficient to roll the protection of
provider->nodes under the global lock as well, and remove the lock from the
provider altogether. If you anticipate other functions in the future that
will require a lock in the provider, then it might make sense to keep the
lock, or maybe just add it later with that new functionality.

> +
> +static void node_aggregate(struct icc_node *node)
> +{
> + struct icc_req *r;
> + u32 agg_avg = 0;
> + u32 agg_peak = 0;
> +
> + hlist_for_each_entry(r, &node->req_list, req_node) {
> + /* sum(averages) and max(peaks) */
> + agg_avg += r->avg_bw;
> + agg_peak = max(agg_peak, r->peak_bw);
> + }
> +
> + node->avg_bw = agg_avg;
> + node->peak_bw = agg_peak;
> +}
> +
> +static void provider_aggregate(struct icc_provider *provider, u32
*avg_bw,
> + u32 *peak_bw)
> +{
> + struct icc_node *n;
> + u32 agg_avg = 0;
> + u32 agg_peak = 0;
> +
> + /* aggregate for the interconnect provider */
> + list_for_each_entry(n, &provider->nodes, node_list) {
> + /* sum the average and max the peak */
> + agg_avg += n->avg_bw;
> + agg_peak = max(agg_peak, n->peak_bw);
> + }
> +
> + *avg_bw = agg_avg;
> + *peak_bw = agg_peak;
> +}
> +
> +static int constraints_apply(struct icc_path *path)
> +{

Nit: maybe name it apply_constraints, since constraints_apply sounds like a
query (do the constraints apply?).

> + struct icc_node *next, *prev = NULL;
> + int i;
> +
> + for (i = 0; i < path->num_nodes; i++, prev = next) {
> + struct icc_provider *provider;
> + u32 avg_bw = 0;
> + u32 peak_bw = 0;
> + int ret;
> +
> + next = path->reqs[i].node;
> + /*
> + * Both endpoints should be valid master-slave pairs of
the
> + * same interconnect provider that will be configured.
> + */
> + if (!next || !prev)
> + continue;
> +
> + if (next->provider != prev->provider)
> + continue;

next should never be null, right? So you could shorten this to if (!prev ||
(next->provider != prev->provider))

> +
> + provider = next->provider;
> + mutex_lock(&provider->lock);
> +
> + /* aggregate requests for the provider */
> + provider_aggregate(provider, &avg_bw, &peak_bw);
> +
> + if (provider->set) {
> + /* set the constraints */
> + ret = provider->set(prev, next, avg_bw, peak_bw);
> + }
> +
> + mutex_unlock(&provider->lock);
> +
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * icc_set() - set constraints on an interconnect path between two
endpoints
> + * @path: reference to the path returned by icc_get()
> + * @avg_bw: average bandwidth in kbps
> + * @peak_bw: peak bandwidth in kbps
> + *
> + * This function is used by an interconnect consumer to express its own
needs
> + * in term of bandwidth and QoS for a previously requested path between
two

"in terms of" rather than "in term of", and not really QoS yet, right?

> + * endpoints. The requests are aggregated and each node is updated
accordingly.
> + *
> + * Returns 0 on success, or an approproate error code otherwise.

appropriate

> + */
> +int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
> +{
> + struct icc_node *node;
> + size_t i;
> + int ret;
> +
> + if (!path)
> + return 0;

Can we ditch this null check? My understanding is it's generally preferred
to skip this if it's only there to avoid developer errors.

> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> +
> + mutex_lock(&icc_path_mutex);
> +
> + /* update the consumer request for this path */
> + path->reqs[i].avg_bw = avg_bw;
> + path->reqs[i].peak_bw = peak_bw;
> +
> + /* aggregate requests for this node */
> + node_aggregate(node);
> +
> + mutex_unlock(&icc_path_mutex);
> + }
> +
> + ret = constraints_apply(path);
> + if (ret)
> + pr_err("interconnect: error applying constraints (%d)",
ret);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(icc_set);
> +
> +/**
> + * icc_get() - return a handle for path between two endpoints
> + * @src_id: source device port id
> + * @dst_id: destination device port id
> + *
> + * 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 the they are not needed anymore.
> + *
> + * Return: icc_path pointer on success, or ERR_PTR() on error
> + */
> +struct icc_path *icc_get(const int src_id, const int dst_id)
> +{
> + struct icc_node *src, *dst;
> + struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
> +
> + src = node_find(src_id);
> + if (!src)
> + goto out;
> +
> + dst = node_find(dst_id);
> + if (!dst)
> + goto out;
> +
> + mutex_lock(&icc_path_mutex);
> + path = path_find(src, dst);
> + mutex_unlock(&icc_path_mutex);
> + if (IS_ERR(path))
> + goto out;
> +
> + path_init(path);
> +
> +out:
> + return path;
> +}
> +EXPORT_SYMBOL_GPL(icc_get);
> +
> +/**
> + * icc_put() - release the reference to the icc_path
> + * @path: 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.
> + */
> +void icc_put(struct icc_path *path)
> +{
> + struct icc_node *node;
> + size_t i;
> + int ret;
> +
> + if (!path || WARN_ON_ONCE(IS_ERR(path)))
> + return;
> +
> + ret = icc_set(path, 0, 0);
> + if (ret)
> + pr_err("%s: error (%d)\n", __func__, ret);
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> + hlist_del(&path->reqs[i].req_node);
> +
> + mutex_lock(&node->provider->lock);
> + node->provider->users--;
> + mutex_unlock(&node->provider->lock);
> + }
> +
> + kfree(path);
> +}
> +EXPORT_SYMBOL_GPL(icc_put);
> +
> +/**
> + * icc_node_create() - create a node
> + * @id: node id
> + *
> + * Return: icc_node pointer on success, or ERR_PTR() on error
> + */
> +struct icc_node *icc_node_create(int id)
> +{
> + struct icc_node *node;
> +
> + /* check if node already exists */
> + node = node_find(id);
> + if (node)
> + return node;

This is probably going to do more harm than good once icc_node_delete comes
in, since it almost certainly indicates a programmer error or ID collision,
and will likely result in a double free. We should probably fail with
EEXIST instead.

> +
> + node = kzalloc(sizeof(*node), GFP_KERNEL);
> + if (!node)
> + return ERR_PTR(-ENOMEM);
> +
> + id = idr_alloc(&icc_idr, node, id, id + 1, GFP_KERNEL);
> + if (WARN(id < 0, "couldn't get idr"))
> + return ERR_PTR(id);
> +
> + node->id = id;
> +
> + return node;
> +}
> +EXPORT_SYMBOL_GPL(icc_node_create);
> +
> +/**
> + * icc_link_create() - create a link between two nodes
> + * @src_id: source node id
> + * @dst_id: destination node id
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_link_create(struct icc_node *node, const int dst_id)
> +{
> + struct icc_node *dst;
> + struct icc_node **new;
> + int ret = 0;
> +
> + if (IS_ERR_OR_NULL(node))
> + return PTR_ERR(node);

Remove this.

> +
> + mutex_lock(&node->provider->lock);
> +
> + dst = node_find(dst_id);
> + if (!dst)
> + dst = icc_node_create(dst_id);

icc_node_create can fail, you should fail here if it does.

> +
> + new = krealloc(node->links,
> + (node->num_links + 1) * sizeof(*node->links),
> + GFP_KERNEL);
> + if (!new) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + node->links = new;
> + node->links[node->num_links++] = dst;
> +
> +out:
> + mutex_unlock(&node->provider->lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_link_create);
> +
> +/**
> + * icc_add_node() - add an interconnect node to interconnect provider
> + * @node: pointer to the interconnect node
> + * @provider: pointer to the interconnect provider
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider)
> +{
> + if (WARN_ON(!node))
> + return -EINVAL;
> +
> + if (WARN_ON(!provider))
> + return -EINVAL;

Remove these.

> +
> + node->provider = provider;
> +
> + mutex_lock(&provider->lock);
> + list_add_tail(&node->node_list, &provider->nodes);
> + mutex_unlock(&provider->lock);
> +
> + return 0;
> +}

icc_node_add should be exported, right? I see it being used in msm8916.c.
You should make sure that "make allmodconfig" still builds with your
changes.

I think you should add a safety check in icc_link_create to ensure that the
node has a provider before adding any links. If some consumer made a
mistake and added links before adding the node to the provider, path
traversal would use the uninitialized or NULL provider pointer. I was
thinking about this while noticing that you assign node->provider before
acquiring the lock.

> +
> +/**
> + * icc_add_provider() - add a new interconnect provider
> + * @icc_provider: the interconnect provider that will be added into
topology
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_add_provider(struct icc_provider *provider)
> +{
> + if (WARN_ON(!provider))
> + return -EINVAL;
> +

Remove this one. The one below is okay.

> + if (WARN_ON(!provider->set))
> + return -EINVAL;
> +
> + mutex_init(&provider->lock);
> + INIT_LIST_HEAD(&provider->nodes);
> +
> + mutex_lock(&icc_provider_list_mutex);
> + list_add(&provider->provider_list, &icc_provider_list);
> + mutex_unlock(&icc_provider_list_mutex);
> +
> + dev_dbg(provider->dev, "interconnect provider added to
topology\n");
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_add_provider);
> +
> +/**
> + * icc_del_provider() - delete previously added interconnect provider
> + * @icc_provider: the interconnect provider that will be removed from
topology
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_del_provider(struct icc_provider *provider)
> +{
> + mutex_lock(&provider->lock);
> + if (provider->users) {
> + pr_warn("interconnect provider still has %d users\n",
> + provider->users);
> + }
> + mutex_unlock(&provider->lock);
> +
> + mutex_lock(&icc_provider_list_mutex);
> + list_del(&provider->provider_list);
> + mutex_unlock(&icc_provider_list_mutex);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_del_provider);
> +
> +MODULE_AUTHOR("Georgi Djakov <[email protected]");
> +MODULE_DESCRIPTION("Interconnect Driver Core");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/interconnect-provider.h
b/include/linux/interconnect-provider.h
> new file mode 100644
> index 000000000000..779b5b5b1306
> --- /dev/null
> +++ b/include/linux/interconnect-provider.h
> @@ -0,0 +1,109 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef _LINUX_INTERCONNECT_PROVIDER_H
> +#define _LINUX_INTERCONNECT_PROVIDER_H
> +
> +#include <linux/interconnect.h>
> +
> +struct icc_node;
> +
> +/**
> + * struct icc_provider - interconnect provider (controller) entity that
might
> + * provide multiple interconnect controls
> + *
> + * @provider_list: list of the registered interconnect providers
> + * @nodes: internal list of the interconnect provider nodes
> + * @set: pointer to device specific set operation function
> + * @dev: the device this interconnect provider belongs to
> + * @lock: lock to provide consistency during aggregation/update of
constraints
> + * @users: count of active users
> + * @data: pointer to private data
> + */
> +struct icc_provider {
> + struct list_head provider_list;
> + struct list_head nodes;
> + int (*set)(struct icc_node *src, struct icc_node *dst,
> + u32 avg_bw, u32 peak_bw);
> + struct device *dev;
> + struct mutex lock;
> + int users;
> + void *data;
> +};
> +
> +/**
> + * struct icc_node - entity that is part of the interconnect topology
> + *
> + * @id: platform specific node id
> + * @name: node name used in debugfs
> + * @links: a list of targets where we can go next when traversing
> + * @num_links: number of links to other interconnect nodes
> + * @provider: points to the interconnect provider of this node
> + * @node_list: list of interconnect nodes associated with @provider
> + * @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
> + * @peak_bw: aggregated value of peak bandwidth
> + * @data: pointer to private data
> + */
> +struct icc_node {
> + int id;

Why int here? Are you expecting negative numbers? Maybe u32 instead? Or
even better, maybe a typedef u32 icc_id? Ooh yeah, that way we know when
parameters and such are passed around that they refer to this.

> + const char *name;
> + struct icc_node **links;
> + size_t num_links;
> +
> + struct icc_provider *provider;
> + struct list_head node_list;

So the difference between node_list and links is that node_list nodes live
inside this node, whereas links point at other peers?

Oh no, I get it now after reading the .c file: node_list is the list entry
in the parent provider's "nodes" list. The comment description could be
clearer about that.

-Evan

2018-05-25 08:27:16

by Amit Kucheria

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

On Fri, Mar 9, 2018 at 11:09 PM, Georgi Djakov <[email protected]> wrote:
> This patch introduce a new API to get requirements and configure the
> interconnect buses across the entire chipset to fit with the current
> demand.
>
> The API is using a consumer/provider-based model, where the providers are
> the interconnect buses and the consumers could be various drivers.
> The consumers request interconnect resources (path) between endpoints and
> set the desired constraints on this data flow path. The providers receive
> requests from consumers and aggregate these requests for all master-slave
> pairs on that path. Then the providers configure each participating in the
> topology node according to the requested data flow path, physical links and
> constraints. The topology could be complicated and multi-tiered and is SoC
> specific.
>
> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> Documentation/interconnect/interconnect.rst | 96 ++++++
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/interconnect/Kconfig | 10 +
> drivers/interconnect/Makefile | 1 +
> drivers/interconnect/core.c | 489 ++++++++++++++++++++++++++++
> include/linux/interconnect-provider.h | 109 +++++++
> include/linux/interconnect.h | 40 +++
> 8 files changed, 748 insertions(+)
> create mode 100644 Documentation/interconnect/interconnect.rst
> create mode 100644 drivers/interconnect/Kconfig
> create mode 100644 drivers/interconnect/Makefile
> create mode 100644 drivers/interconnect/core.c
> create mode 100644 include/linux/interconnect-provider.h
> create mode 100644 include/linux/interconnect.h
>
> diff --git a/Documentation/interconnect/interconnect.rst b/Documentation/interconnect/interconnect.rst
> new file mode 100644
> index 000000000000..23eba68e8424
> --- /dev/null
> +++ b/Documentation/interconnect/interconnect.rst
> @@ -0,0 +1,96 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +=====================================
> +GENERIC SYSTEM INTERCONNECT SUBSYSTEM
> +=====================================
> +
> +Introduction
> +------------
> +
> +This framework is designed to provide a standard kernel interface to control
> +the settings of the interconnects on a SoC. These settings can be throughput,
> +latency and priority between multiple interconnected devices or functional
> +blocks. This can be controlled dynamically in order to save power or provide
> +maximum performance.
> +
> +The interconnect bus is a hardware with configurable parameters, which can be
> +set on a data path according to the requests received from various drivers.
> +An example of interconnect buses are the interconnects between various
> +components or functional blocks in chipsets. There can be multiple interconnects
> +on a SoC that can be multi-tiered.
> +
> +Below is a simplified diagram of a real-world SoC interconnect bus topology.
> +
> +::
> +
> + +----------------+ +----------------+
> + | HW Accelerator |--->| M NoC |<---------------+
> + +----------------+ +----------------+ |
> + | | +------------+
> + +-----+ +-------------+ V +------+ | |
> + | DDR | | +--------+ | PCIe | | |
> + +-----+ | | Slaves | +------+ | |
> + ^ ^ | +--------+ | | C NoC |
> + | | V V | |
> + +------------------+ +------------------------+ | | +-----+
> + | |-->| |-->| |-->| CPU |
> + | |-->| |<--| | +-----+
> + | Mem NoC | | S NoC | +------------+
> + | |<--| |---------+ |
> + | |<--| |<------+ | | +--------+
> + +------------------+ +------------------------+ | | +-->| Slaves |
> + ^ ^ ^ ^ ^ | | +--------+
> + | | | | | | V
> + +------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
> + | CPUs | | | GPU | | DSP | | Masters |-->| P NoC |-->| Slaves |
> + +------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
> + |
> + +-------+
> + | Modem |
> + +-------+
> +
> +Terminology
> +-----------
> +
> +Interconnect provider is the software definition of the interconnect hardware.
> +The interconnect providers on the above diagram are M NoC, S NoC, C NoC and Mem
> +NoC.
> +
> +Interconnect node is the software definition of the interconnect hardware
> +port. Each interconnect provider consists of multiple interconnect nodes,
> +which are connected to other SoC components including other interconnect
> +providers. The point on the diagram where the CPUs connects to the memory is
> +called an interconnect node, which belongs to the Mem NoC interconnect provider.
> +
> +Interconnect endpoints are the first or the last element of the path. Every
> +endpoint is a node, but not every node is an endpoint.
> +
> +Interconnect path is everything between two endpoints including all the nodes
> +that have to be traversed to reach from a source to destination node. It may
> +include multiple master-slave pairs across several interconnect providers.
> +
> +Interconnect consumers are the entities which make use of the data paths exposed
> +by the providers. The consumers send requests to providers requesting various
> +throughput, latency and priority. Usually the consumers are device drivers, that
> +send request based on their needs. An example for a consumer is a video decoder
> +that supports various formats and image sizes.
> +
> +Interconnect providers
> +----------------------
> +
> +Interconnect provider is an entity that implements methods to initialize and
> +configure a interconnect bus hardware. The interconnect provider drivers should
> +be registered with the interconnect provider core.
> +
> +The interconnect framework provider API functions are documented in
> +.. kernel-doc:: include/linux/interconnect-provider.h
> +
> +Interconnect consumers
> +----------------------
> +
> +Interconnect consumers are the clients which use the interconnect APIs to
> +get paths between endpoints and set their bandwidth/latency/QoS requirements
> +for these interconnect paths.
> +

This document is missing a section on the locking semantics of the
framework. Does the core ensure that the entire path is locked for
set() to propagate?

> +The interconnect framework consumer API functions are documented in
> +.. kernel-doc:: include/linux/interconnect.h
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 879dc0604cba..96a1db022cee 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -219,4 +219,6 @@ source "drivers/siox/Kconfig"
>
> source "drivers/slimbus/Kconfig"
>
> +source "drivers/interconnect/Kconfig"
> +
> endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 24cd47014657..0cca95740d9b 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -185,3 +185,4 @@ obj-$(CONFIG_TEE) += tee/
> obj-$(CONFIG_MULTIPLEXER) += mux/
> obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/
> obj-$(CONFIG_SIOX) += siox/
> +obj-$(CONFIG_INTERCONNECT) += interconnect/
> diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
> new file mode 100644
> index 000000000000..a261c7d41deb
> --- /dev/null
> +++ b/drivers/interconnect/Kconfig
> @@ -0,0 +1,10 @@
> +menuconfig INTERCONNECT
> + tristate "On-Chip Interconnect management support"
> + help
> + Support for management of the on-chip interconnects.
> +
> + This framework is designed to provide a generic interface for
> + managing the interconnects in a SoC.
> +
> + If unsure, say no.
> +
> diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
> new file mode 100644
> index 000000000000..5edf0ae80818
> --- /dev/null
> +++ b/drivers/interconnect/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_INTERCONNECT) += core.o
> diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
> new file mode 100644
> index 000000000000..6306e258b9b9
> --- /dev/null
> +++ b/drivers/interconnect/core.c
> @@ -0,0 +1,489 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Interconnect framework core driver
> + *
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#include <linux/device.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/interconnect.h>
> +#include <linux/interconnect-provider.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_IDR(icc_idr);
> +static LIST_HEAD(icc_provider_list);
> +static DEFINE_MUTEX(icc_provider_list_mutex);
> +static DEFINE_MUTEX(icc_path_mutex);
> +
> +/**
> + * 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
> + * @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;
> + u32 avg_bw;
> + u32 peak_bw;
> +};
> +
> +/**
> + * struct icc_path - interconnect path structure
> + * @num_nodes: number of hops (nodes)
> + * @reqs: array of the requests applicable to this path of nodes
> + */
> +struct icc_path {
> + size_t num_nodes;
> + struct icc_req reqs[0];
> +};
> +
> +static struct icc_node *node_find(const int id)
> +{
> + struct icc_node *node;
> +
> + node = idr_find(&icc_idr, id);
> +
> + return node;
> +}
> +
> +static struct icc_path *path_allocate(struct icc_node *node, ssize_t num_nodes)
> +{
> + struct icc_path *path;
> + size_t i;
> +
> + path = kzalloc(sizeof(*path) + num_nodes * sizeof(*path->reqs),
> + GFP_KERNEL);
> + if (!path)
> + return ERR_PTR(-ENOMEM);
> +
> + path->num_nodes = num_nodes;
> +
> + for (i = 0; i < num_nodes; i++) {
> + hlist_add_head(&path->reqs[i].req_node, &node->req_list);
> +
> + path->reqs[i].node = node;
> + /* reference to previous node was saved during path traversal */
> + node = node->reverse;
> + }
> +
> + return path;
> +}
> +
> +static struct icc_path *path_find(struct icc_node *src, struct icc_node *dst)
> +{
> + struct icc_node *node = NULL;
> + struct list_head traverse_list;
> + struct list_head edge_list;
> + struct list_head tmp_list;
> + size_t i, number = 0;
> + bool found = false;
> +
> + INIT_LIST_HEAD(&traverse_list);
> + INIT_LIST_HEAD(&edge_list);
> + INIT_LIST_HEAD(&tmp_list);
> +
> + list_add_tail(&src->search_list, &traverse_list);
> +
> + do {
> + list_for_each_entry(node, &traverse_list, search_list) {
> + if (node == dst) {
> + found = true;
> + list_add(&node->search_list, &tmp_list);
> + break;
> + }
> + for (i = 0; i < node->num_links; i++) {
> + struct icc_node *tmp = node->links[i];
> +
> + if (!tmp)
> + return ERR_PTR(-ENOENT);
> +
> + if (tmp->is_traversed)
> + continue;
> +
> + tmp->is_traversed = true;
> + tmp->reverse = node;
> + list_add_tail(&tmp->search_list, &edge_list);
> + }
> + }
> + if (found)
> + break;
> +
> + list_splice_init(&traverse_list, &tmp_list);
> + list_splice_init(&edge_list, &traverse_list);
> +
> + /* count the number of nodes */
> + number++;
> +
> + } while (!list_empty(&traverse_list));
> +
> + /* reset the traversed state */
> + list_for_each_entry(node, &tmp_list, search_list)
> + node->is_traversed = false;
> +
> + if (found)
> + return path_allocate(dst, number);
> +
> + return ERR_PTR(-EPROBE_DEFER);
> +}
> +
> +static int path_init(struct icc_path *path)
> +{
> + struct icc_node *node;
> + size_t i;
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> +
> + mutex_lock(&node->provider->lock);
> + node->provider->users++;
> + mutex_unlock(&node->provider->lock);
> + }
> +
> + return 0;
> +}
> +

Consider adding some comments for node_aggregate and
provider_aggregate's aggregation algorithm

"We want the path to honor all bandwidth requests, so the average
bandwidth requirements from each consumer are aggregated at each node
and provider level. The peak bandwidth requirements will then be the
highest of all the peak bw requests"

or something to the effect that.

> +static void node_aggregate(struct icc_node *node)
> +{
> + struct icc_req *r;
> + u32 agg_avg = 0;
> + u32 agg_peak = 0;
> +
> + hlist_for_each_entry(r, &node->req_list, req_node) {
> + /* sum(averages) and max(peaks) */
> + agg_avg += r->avg_bw;
> + agg_peak = max(agg_peak, r->peak_bw);
> + }
> +
> + node->avg_bw = agg_avg;
> + node->peak_bw = agg_peak;
> +}
> +
> +static void provider_aggregate(struct icc_provider *provider, u32 *avg_bw,
> + u32 *peak_bw)
> +{
> + struct icc_node *n;
> + u32 agg_avg = 0;
> + u32 agg_peak = 0;
> +
> + /* aggregate for the interconnect provider */

You could get rid of this, the function name says as much.

> + list_for_each_entry(n, &provider->nodes, node_list) {
> + /* sum the average and max the peak */
> + agg_avg += n->avg_bw;
> + agg_peak = max(agg_peak, n->peak_bw);
> + }
> +
> + *avg_bw = agg_avg;
> + *peak_bw = agg_peak;
> +}
> +
> +static int constraints_apply(struct icc_path *path)
> +{
> + struct icc_node *next, *prev = NULL;
> + int i;
> +
> + for (i = 0; i < path->num_nodes; i++, prev = next) {
> + struct icc_provider *provider;
> + u32 avg_bw = 0;
> + u32 peak_bw = 0;
> + int ret;
> +
> + next = path->reqs[i].node;
> + /*
> + * Both endpoints should be valid master-slave pairs of the
> + * same interconnect provider that will be configured.
> + */
> + if (!next || !prev)
> + continue;
> +
> + if (next->provider != prev->provider)
> + continue;
> +
> + provider = next->provider;
> + mutex_lock(&provider->lock);
> +
> + /* aggregate requests for the provider */

Get rid of comment.

> + provider_aggregate(provider, &avg_bw, &peak_bw);
> +
> + if (provider->set) {
> + /* set the constraints */
> + ret = provider->set(prev, next, avg_bw, peak_bw);
> + }
> +
> + mutex_unlock(&provider->lock);
> +
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * icc_set() - set constraints on an interconnect path between two endpoints
> + * @path: reference to the path returned by icc_get()
> + * @avg_bw: average bandwidth in kbps
> + * @peak_bw: peak bandwidth in kbps
> + *
> + * This function is used by an interconnect consumer to express its own needs
> + * in term of bandwidth and QoS for a previously requested path between two
> + * endpoints. The requests are aggregated and each node is updated accordingly.
> + *
> + * Returns 0 on success, or an approproate error code otherwise.

*appropriate*

> + */
> +int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
> +{
> + struct icc_node *node;
> + size_t i;
> + int ret;
> +
> + if (!path)
> + return 0;
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> +
> + mutex_lock(&icc_path_mutex);
> +
> + /* update the consumer request for this path */
> + path->reqs[i].avg_bw = avg_bw;
> + path->reqs[i].peak_bw = peak_bw;
> +
> + /* aggregate requests for this node */
> + node_aggregate(node);
> +
> + mutex_unlock(&icc_path_mutex);
> + }
> +
> + ret = constraints_apply(path);
> + if (ret)
> + pr_err("interconnect: error applying constraints (%d)", ret);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(icc_set);
> +
> +/**
> + * icc_get() - return a handle for path between two endpoints
> + * @src_id: source device port id
> + * @dst_id: destination device port id
> + *
> + * 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 the they are not needed anymore.
> + *
> + * Return: icc_path pointer on success, or ERR_PTR() on error
> + */
> +struct icc_path *icc_get(const int src_id, const int dst_id)
> +{
> + struct icc_node *src, *dst;
> + struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
> +
> + src = node_find(src_id);
> + if (!src)
> + goto out;
> +
> + dst = node_find(dst_id);
> + if (!dst)
> + goto out;
> +
> + mutex_lock(&icc_path_mutex);
> + path = path_find(src, dst);
> + mutex_unlock(&icc_path_mutex);
> + if (IS_ERR(path))
> + goto out;
> +
> + path_init(path);
> +
> +out:
> + return path;
> +}
> +EXPORT_SYMBOL_GPL(icc_get);
> +
> +/**
> + * icc_put() - release the reference to the icc_path
> + * @path: 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.
> + */
> +void icc_put(struct icc_path *path)
> +{
> + struct icc_node *node;
> + size_t i;
> + int ret;
> +
> + if (!path || WARN_ON_ONCE(IS_ERR(path)))
> + return;
> +
> + ret = icc_set(path, 0, 0);
> + if (ret)
> + pr_err("%s: error (%d)\n", __func__, ret);
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> + hlist_del(&path->reqs[i].req_node);
> +
> + mutex_lock(&node->provider->lock);
> + node->provider->users--;
> + mutex_unlock(&node->provider->lock);
> + }
> +
> + kfree(path);
> +}
> +EXPORT_SYMBOL_GPL(icc_put);
> +
> +/**
> + * icc_node_create() - create a node
> + * @id: node id
> + *
> + * Return: icc_node pointer on success, or ERR_PTR() on error
> + */
> +struct icc_node *icc_node_create(int id)
> +{
> + struct icc_node *node;
> +
> + /* check if node already exists */
> + node = node_find(id);
> + if (node)
> + return node;
> +
> + node = kzalloc(sizeof(*node), GFP_KERNEL);
> + if (!node)
> + return ERR_PTR(-ENOMEM);
> +
> + id = idr_alloc(&icc_idr, node, id, id + 1, GFP_KERNEL);
> + if (WARN(id < 0, "couldn't get idr"))
> + return ERR_PTR(id);
> +
> + node->id = id;
> +
> + return node;
> +}
> +EXPORT_SYMBOL_GPL(icc_node_create);
> +
> +/**
> + * icc_link_create() - create a link between two nodes
> + * @src_id: source node id
> + * @dst_id: destination node id
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_link_create(struct icc_node *node, const int dst_id)
> +{
> + struct icc_node *dst;
> + struct icc_node **new;
> + int ret = 0;
> +
> + if (IS_ERR_OR_NULL(node))
> + return PTR_ERR(node);
> +
> + mutex_lock(&node->provider->lock);
> +
> + dst = node_find(dst_id);
> + if (!dst)
> + dst = icc_node_create(dst_id);
> +
> + new = krealloc(node->links,
> + (node->num_links + 1) * sizeof(*node->links),
> + GFP_KERNEL);
> + if (!new) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + node->links = new;
> + node->links[node->num_links++] = dst;
> +
> +out:
> + mutex_unlock(&node->provider->lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_link_create);
> +
> +/**
> + * icc_add_node() - add an interconnect node to interconnect provider
> + * @node: pointer to the interconnect node
> + * @provider: pointer to the interconnect provider
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider)
> +{
> + if (WARN_ON(!node))
> + return -EINVAL;
> +
> + if (WARN_ON(!provider))
> + return -EINVAL;
> +
> + node->provider = provider;
> +
> + mutex_lock(&provider->lock);
> + list_add_tail(&node->node_list, &provider->nodes);
> + mutex_unlock(&provider->lock);
> +
> + return 0;
> +}
> +
> +/**
> + * icc_add_provider() - add a new interconnect provider
> + * @icc_provider: the interconnect provider that will be added into topology
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_add_provider(struct icc_provider *provider)
> +{
> + if (WARN_ON(!provider))
> + return -EINVAL;
> +
> + if (WARN_ON(!provider->set))
> + return -EINVAL;
> +
> + mutex_init(&provider->lock);
> + INIT_LIST_HEAD(&provider->nodes);
> +
> + mutex_lock(&icc_provider_list_mutex);
> + list_add(&provider->provider_list, &icc_provider_list);
> + mutex_unlock(&icc_provider_list_mutex);
> +
> + dev_dbg(provider->dev, "interconnect provider added to topology\n");
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_add_provider);
> +
> +/**
> + * icc_del_provider() - delete previously added interconnect provider
> + * @icc_provider: the interconnect provider that will be removed from topology
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_del_provider(struct icc_provider *provider)
> +{
> + mutex_lock(&provider->lock);
> + if (provider->users) {
> + pr_warn("interconnect provider still has %d users\n",
> + provider->users);
> + }
> + mutex_unlock(&provider->lock);
> +
> + mutex_lock(&icc_provider_list_mutex);
> + list_del(&provider->provider_list);
> + mutex_unlock(&icc_provider_list_mutex);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_del_provider);
> +
> +MODULE_AUTHOR("Georgi Djakov <[email protected]");
> +MODULE_DESCRIPTION("Interconnect Driver Core");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h
> new file mode 100644
> index 000000000000..779b5b5b1306
> --- /dev/null
> +++ b/include/linux/interconnect-provider.h
> @@ -0,0 +1,109 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef _LINUX_INTERCONNECT_PROVIDER_H
> +#define _LINUX_INTERCONNECT_PROVIDER_H
> +
> +#include <linux/interconnect.h>
> +
> +struct icc_node;
> +
> +/**
> + * struct icc_provider - interconnect provider (controller) entity that might
> + * provide multiple interconnect controls
> + *
> + * @provider_list: list of the registered interconnect providers
> + * @nodes: internal list of the interconnect provider nodes
> + * @set: pointer to device specific set operation function
> + * @dev: the device this interconnect provider belongs to
> + * @lock: lock to provide consistency during aggregation/update of constraints
> + * @users: count of active users
> + * @data: pointer to private data
> + */
> +struct icc_provider {
> + struct list_head provider_list;
> + struct list_head nodes;
> + int (*set)(struct icc_node *src, struct icc_node *dst,
> + u32 avg_bw, u32 peak_bw);
> + struct device *dev;
> + struct mutex lock;
> + int users;
> + void *data;
> +};
> +
> +/**
> + * struct icc_node - entity that is part of the interconnect topology
> + *
> + * @id: platform specific node id
> + * @name: node name used in debugfs
> + * @links: a list of targets where we can go next when traversing
> + * @num_links: number of links to other interconnect nodes
> + * @provider: points to the interconnect provider of this node
> + * @node_list: list of interconnect nodes associated with @provider
> + * @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
> + * @peak_bw: aggregated value of peak bandwidth

Consider changing to "aggregated value of {average|peak} bandwidth
requests from all consumers"

> + * @data: pointer to private data
> + */
> +struct icc_node {
> + int id;
> + const char *name;
> + struct icc_node **links;
> + size_t num_links;
> +
> + struct icc_provider *provider;
> + struct list_head node_list;
> + struct list_head orphan_list;
> + struct list_head search_list;
> + struct icc_node *reverse;
> + bool is_traversed;
> + struct hlist_head req_list;
> + u32 avg_bw;
> + u32 peak_bw;
> + void *data;
> +};
> +
> +#if IS_ENABLED(CONFIG_INTERCONNECT)
> +
> +struct icc_node *icc_node_create(int id);
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider);
> +int icc_link_create(struct icc_node *node, const int dst_id);
> +int icc_add_provider(struct icc_provider *provider);
> +int icc_del_provider(struct icc_provider *provider);
> +
> +#else
> +
> +static inline struct icc_node *icc_node_create(int id)
> +{
> + return ERR_PTR(-ENOTSUPP);
> +}
> +
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int icc_link_create(struct icc_node *node, const int dst_id)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int icc_add_provider(struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int icc_del_provider(struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +#endif /* CONFIG_INTERCONNECT */
> +
> +#endif /* _LINUX_INTERCONNECT_PROVIDER_H */
> diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h
> new file mode 100644
> index 000000000000..5a7cf72b76a5
> --- /dev/null
> +++ b/include/linux/interconnect.h
> @@ -0,0 +1,40 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef _LINUX_INTERCONNECT_H
> +#define _LINUX_INTERCONNECT_H
> +
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +
> +struct icc_path;
> +struct device;
> +
> +#if IS_ENABLED(CONFIG_INTERCONNECT)
> +
> +struct icc_path *icc_get(const int src_id, const int dst_id);
> +void icc_put(struct icc_path *path);
> +int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw);
> +
> +#else
> +
> +static inline struct icc_path *icc_get(const int src_id, const int dst_id)
> +{
> + return NULL;
> +}
> +
> +static inline void icc_put(struct icc_path *path)
> +{
> +}
> +
> +static inline int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
> +{
> + return 0;
> +}
> +
> +#endif /* CONFIG_INTERCONNECT */
> +
> +#endif /* _LINUX_INTERCONNECT_H */
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2018-05-25 08:28:25

by Amit Kucheria

[permalink] [raw]
Subject: Re: [PATCH v4 5/7] interconnect: qcom: Add msm8916 interconnect provider driver

On Fri, Mar 9, 2018 at 11:09 PM, Georgi Djakov <[email protected]> wrote:
> Add driver for the Qualcomm interconnect buses found in msm8916 based
> platforms.
>
> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> drivers/interconnect/Kconfig | 5 +
> drivers/interconnect/Makefile | 1 +
> drivers/interconnect/qcom/Kconfig | 11 +
> drivers/interconnect/qcom/Makefile | 2 +
> drivers/interconnect/qcom/msm8916.c | 482 ++++++++++++++++++++++++++++++++++++
> include/linux/interconnect/qcom.h | 350 ++++++++++++++++++++++++++
> 6 files changed, 851 insertions(+)
> create mode 100644 drivers/interconnect/qcom/Kconfig
> create mode 100644 drivers/interconnect/qcom/msm8916.c
> create mode 100644 include/linux/interconnect/qcom.h
>
> diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
> index a261c7d41deb..07a8276fa35a 100644
> --- a/drivers/interconnect/Kconfig
> +++ b/drivers/interconnect/Kconfig
> @@ -8,3 +8,8 @@ menuconfig INTERCONNECT
>
> If unsure, say no.
>
> +if INTERCONNECT
> +
> +source "drivers/interconnect/qcom/Kconfig"
> +
> +endif
> diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
> index 5edf0ae80818..5971b811c2d7 100644
> --- a/drivers/interconnect/Makefile
> +++ b/drivers/interconnect/Makefile
> @@ -1 +1,2 @@
> obj-$(CONFIG_INTERCONNECT) += core.o
> +obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/
> diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
> new file mode 100644
> index 000000000000..86465dc37bd4
> --- /dev/null
> +++ b/drivers/interconnect/qcom/Kconfig
> @@ -0,0 +1,11 @@
> +config INTERCONNECT_QCOM
> + bool "Qualcomm Network-on-Chip interconnect drivers"
> + depends on INTERCONNECT
> + depends on ARCH_QCOM || COMPILE_TEST
> + default y
> +
> +config INTERCONNECT_QCOM_MSM8916
> + tristate "Qualcomm MSM8916 interconnect driver"
> + depends on INTERCONNECT_QCOM
> + help
> + This is a driver for the Qualcomm Network-on-Chip on msm8916-based platforms.
> diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
> index 095bdef1ee6e..a0c13a25e8db 100644
> --- a/drivers/interconnect/qcom/Makefile
> +++ b/drivers/interconnect/qcom/Makefile
> @@ -1 +1,3 @@
> obj-y += smd-rpm.o
> +
> +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += msm8916.o
> diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c
> new file mode 100644
> index 000000000000..d5b54f8261c8
> --- /dev/null
> +++ b/drivers/interconnect/qcom/msm8916.c
> @@ -0,0 +1,482 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2018 Linaro Ltd
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/interconnect-provider.h>
> +#include <linux/interconnect/qcom.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include "smd-rpm.h"
> +
> +#define RPM_MASTER_FIELD_BW 0x00007762
> +#define RPM_BUS_MASTER_REQ 0x73616d62
> +#define RPM_BUS_SLAVE_REQ 0x766c7362
> +
> +#define to_qcom_provider(_provider) \
> + container_of(_provider, struct qcom_icc_provider, provider)
> +
> +#define DEFINE_QNODE(_name, _id, _port, _buswidth, _ap_owned, \
> + _mas_rpm_id, _slv_rpm_id, _qos_mode, \
> + _numlinks, ...) \
> + static struct qcom_icc_node _name = { \
> + .id = _id, \
> + .name = #_name, \
> + .port = _port, \
> + .buswidth = _buswidth, \
> + .qos_mode = _qos_mode, \
> + .ap_owned = _ap_owned, \
> + .mas_rpm_id = _mas_rpm_id, \
> + .slv_rpm_id = _slv_rpm_id, \
> + .num_links = _numlinks, \
> + .links = { __VA_ARGS__ }, \
> + }

Move this macro definition just above its use below.

> +enum qcom_qos_mode {
> + QCOM_QOS_MODE_BYPASS = 0,
> + QCOM_QOS_MODE_FIXED,
> + QCOM_QOS_MODE_MAX,
> +};
> +
> +struct qcom_icc_provider {
> + struct icc_provider provider;
> + void __iomem *base;
> + struct clk *bus_clk;
> + struct clk *bus_a_clk;
> +};
> +
> +#define MSM8916_MAX_LINKS 8
> +
> +/**
> + * struct qcom_icc_node - Qualcomm specific interconnect nodes
> + * @name: the node name used in debugfs
> + * @links: an array of nodes where we can go next while traversing
> + * @id: a unique node identifier
> + * @num_links: the total number of @links
> + * @port: the offset index into the masters QoS register space
> + * @buswidth: width of the interconnect between a node and the bus

units?

> + * @ap_owned: the AP CPU does the writing to QoS registers
> + * @rpm: reference to the RPM SMD driver
> + * @qos_mode: QoS mode for ap_owned resources
> + * @mas_rpm_id: RPM id for devices that are bus masters
> + * @slv_rpm_id: RPM id for devices that are bus slaves
> + * @rate: current bus clock rate in Hz
> + */
> +struct qcom_icc_node {
> + unsigned char *name;
> + u16 links[MSM8916_MAX_LINKS];
> + u16 id;
> + u16 num_links;
> + u16 port;
> + u16 buswidth;
> + bool ap_owned;
> + struct qcom_smd_rpm *rpm;
> + enum qcom_qos_mode qos_mode;
> + int mas_rpm_id;
> + int slv_rpm_id;
> + u64 rate;
> +};
> +
> +struct qcom_icc_desc {
> + struct qcom_icc_node **nodes;
> + size_t num_nodes;
> +};
> +
> +DEFINE_QNODE(mas_video, 63, 8, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
> +DEFINE_QNODE(mas_jpeg, 62, 6, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
> +DEFINE_QNODE(mas_vfe, 29, 9, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10001, 10002);
> +DEFINE_QNODE(mas_mdp, 22, 7, 16, 1, -1, -1, QCOM_QOS_MODE_BYPASS, 2, 10000, 10002);
> +DEFINE_QNODE(mas_qdss_bam, 53, 11, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10009);
> +DEFINE_QNODE(mas_snoc_cfg, 54, 0, 16, 0, 20, -1, QCOM_QOS_MODE_BYPASS, 1, 10009);
> +DEFINE_QNODE(mas_qdss_etr, 60, 10, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10009);
> +DEFINE_QNODE(mm_int_0, 10000, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10003);
> +DEFINE_QNODE(mm_int_1, 10001, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10003);
> +DEFINE_QNODE(mm_int_2, 10002, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10004);
> +DEFINE_QNODE(mm_int_bimc, 10003, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10008);
> +DEFINE_QNODE(snoc_int_0, 10004, 0, 8, 0, 99, 130, QCOM_QOS_MODE_FIXED, 3, 588, 519, 10027);
> +DEFINE_QNODE(snoc_int_1, 10005, 0, 8, 0, 100, 131, QCOM_QOS_MODE_FIXED, 3, 517, 663, 664);
> +DEFINE_QNODE(snoc_int_bimc, 10006, 0, 8, 0, 101, 132, QCOM_QOS_MODE_FIXED, 1, 10007);
> +DEFINE_QNODE(snoc_bimc_0_mas, 10007, 0, 8, 0, 3, -1, QCOM_QOS_MODE_FIXED, 1, 10025);
> +DEFINE_QNODE(snoc_bimc_1_mas, 10008, 0, 16, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10026);
> +DEFINE_QNODE(qdss_int, 10009, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 2, 10004, 10006);
> +DEFINE_QNODE(bimc_snoc_slv, 10017, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 2, 10004, 10005);
> +DEFINE_QNODE(snoc_pnoc_mas, 10027, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10028);
> +DEFINE_QNODE(pnoc_snoc_slv, 10011, 0, 8, 0, -1, 45, QCOM_QOS_MODE_FIXED, 3, 10004, 10006, 10005);
> +DEFINE_QNODE(slv_srvc_snoc, 587, 0, 8, 0, -1, 29, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_qdss_stm, 588, 0, 4, 0, -1, 30, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_imem, 519, 0, 8, 0, -1, 26, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_apss, 517, 0, 4, 0, -1, 20, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_cats_0, 663, 0, 16, 0, -1, 106, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_cats_1, 664, 0, 8, 0, -1, 107, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(mas_apss, 1, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
> +DEFINE_QNODE(mas_tcu0, 104, 5, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
> +DEFINE_QNODE(mas_tcu1, 105, 6, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
> +DEFINE_QNODE(mas_gfx, 26, 2, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 3, 512, 10016, 514);
> +DEFINE_QNODE(bimc_snoc_mas, 10016, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10017);
> +DEFINE_QNODE(snoc_bimc_0_slv, 10025, 0, 8, 0, -1, 24, QCOM_QOS_MODE_FIXED, 1, 512);
> +DEFINE_QNODE(snoc_bimc_1_slv, 10026, 0, 8, 1, -1, -1, QCOM_QOS_MODE_FIXED, 1, 512);
> +DEFINE_QNODE(slv_ebi_ch0, 512, 0, 8, 0, -1, 0, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_apps_l2, 514, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(snoc_pnoc_slv, 10028, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10012);
> +DEFINE_QNODE(pnoc_int_0, 10012, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 8, 10010, 10018, 10019, 10020, 10021, 10022, 10023, 10024);
> +DEFINE_QNODE(pnoc_int_1, 10013, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10010);
> +DEFINE_QNODE(pnoc_m_0, 10014, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10012);
> +DEFINE_QNODE(pnoc_m_1, 10015, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10010);
> +DEFINE_QNODE(pnoc_s_0, 10018, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 620, 624, 579, 622, 521);
> +DEFINE_QNODE(pnoc_s_1, 10019, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 627, 625, 535, 577, 618);
> +DEFINE_QNODE(pnoc_s_2, 10020, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 533, 630, 629, 641, 632);
> +DEFINE_QNODE(pnoc_s_3, 10021, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 5, 536, 647, 636, 635, 634);
> +DEFINE_QNODE(pnoc_s_4, 10022, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 596, 589, 590);
> +DEFINE_QNODE(pnoc_s_8, 10023, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 614, 606, 613);
> +DEFINE_QNODE(pnoc_s_9, 10024, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 3, 609, 522, 598);
> +DEFINE_QNODE(slv_imem_cfg, 627, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_crypto_0_cfg, 625, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_msg_ram, 535, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_pdm, 577, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_prng, 618, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_clk_ctl, 620, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_mss, 521, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_tlmm, 624, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_tcsr, 579, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_security, 622, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_spdm, 533, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_pnoc_cfg, 641, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_pmic_arb, 632, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_bimc_cfg, 629, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_boot_rom, 630, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_mpm, 536, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_qdss_cfg, 635, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_rbcpr_cfg, 636, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_snoc_cfg, 647, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_dehr_cfg, 634, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_venus_cfg, 596, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_display_cfg, 590, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_camera_cfg, 589, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_usb_hs, 614, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_sdcc_1, 606, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_blsp_1, 613, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_sdcc_2, 609, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_gfx_cfg, 598, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(slv_audio, 522, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 0, 0);
> +DEFINE_QNODE(mas_blsp_1, 86, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10015);
> +DEFINE_QNODE(mas_spdm, 36, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
> +DEFINE_QNODE(mas_dehr, 75, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
> +DEFINE_QNODE(mas_audio, 15, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10014);
> +DEFINE_QNODE(mas_usb_hs, 87, 0, 4, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10015);
> +DEFINE_QNODE(mas_pnoc_crypto_0, 55, 0, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
> +DEFINE_QNODE(mas_pnoc_sdcc_1, 78, 7, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
> +DEFINE_QNODE(mas_pnoc_sdcc_2, 81, 8, 8, 0, -1, -1, QCOM_QOS_MODE_FIXED, 1, 10013);
> +DEFINE_QNODE(pnoc_snoc_mas, 10010, 0, 8, 0, 29, -1, QCOM_QOS_MODE_FIXED, 1, 10011);
> +
> +static struct qcom_icc_node *msm8916_snoc_nodes[] = {
> + &mas_video,
> + &mas_jpeg,
> + &mas_vfe,
> + &mas_mdp,
> + &mas_qdss_bam,
> + &mas_snoc_cfg,
> + &mas_qdss_etr,
> + &mm_int_0,
> + &mm_int_1,
> + &mm_int_2,
> + &mm_int_bimc,
> + &snoc_int_0,
> + &snoc_int_1,
> + &snoc_int_bimc,
> + &snoc_bimc_0_mas,
> + &snoc_bimc_1_mas,
> + &qdss_int,
> + &bimc_snoc_slv,
> + &snoc_pnoc_mas,
> + &pnoc_snoc_slv,
> + &slv_srvc_snoc,
> + &slv_qdss_stm,
> + &slv_imem,
> + &slv_apss,
> + &slv_cats_0,
> + &slv_cats_1,
> +};
> +
> +static struct qcom_icc_desc msm8916_snoc = {
> + .nodes = msm8916_snoc_nodes,
> + .num_nodes = ARRAY_SIZE(msm8916_snoc_nodes),
> +};
> +
> +static struct qcom_icc_node *msm8916_bimc_nodes[] = {
> + &mas_apss,
> + &mas_tcu0,
> + &mas_tcu1,
> + &mas_gfx,
> + &bimc_snoc_mas,
> + &snoc_bimc_0_slv,
> + &snoc_bimc_1_slv,
> + &slv_ebi_ch0,
> + &slv_apps_l2,
> +};
> +
> +static struct qcom_icc_desc msm8916_bimc = {
> + .nodes = msm8916_bimc_nodes,
> + .num_nodes = ARRAY_SIZE(msm8916_bimc_nodes),
> +};
> +
> +static struct qcom_icc_node *msm8916_pnoc_nodes[] = {
> + &snoc_pnoc_slv,
> + &pnoc_int_0,
> + &pnoc_int_1,
> + &pnoc_m_0,
> + &pnoc_m_1,
> + &pnoc_s_0,
> + &pnoc_s_1,
> + &pnoc_s_2,
> + &pnoc_s_3,
> + &pnoc_s_4,
> + &pnoc_s_8,
> + &pnoc_s_9,
> + &slv_imem_cfg,
> + &slv_crypto_0_cfg,
> + &slv_msg_ram,
> + &slv_pdm,
> + &slv_prng,
> + &slv_clk_ctl,
> + &slv_mss,
> + &slv_tlmm,
> + &slv_tcsr,
> + &slv_security,
> + &slv_spdm,
> + &slv_pnoc_cfg,
> + &slv_pmic_arb,
> + &slv_bimc_cfg,
> + &slv_boot_rom,
> + &slv_mpm,
> + &slv_qdss_cfg,
> + &slv_rbcpr_cfg,
> + &slv_snoc_cfg,
> + &slv_dehr_cfg,
> + &slv_venus_cfg,
> + &slv_display_cfg,
> + &slv_camera_cfg,
> + &slv_usb_hs,
> + &slv_sdcc_1,
> + &slv_blsp_1,
> + &slv_sdcc_2,
> + &slv_gfx_cfg,
> + &slv_audio,
> + &mas_blsp_1,
> + &mas_spdm,
> + &mas_dehr,
> + &mas_audio,
> + &mas_usb_hs,
> + &mas_pnoc_crypto_0,
> + &mas_pnoc_sdcc_1,
> + &mas_pnoc_sdcc_2,
> + &pnoc_snoc_mas,
> +};
> +
> +static struct qcom_icc_desc msm8916_pnoc = {
> + .nodes = msm8916_pnoc_nodes,
> + .num_nodes = ARRAY_SIZE(msm8916_pnoc_nodes),
> +};
> +
> +static int qcom_icc_init(struct icc_node *node)
> +{
> + struct qcom_icc_provider *qp = to_qcom_provider(node->provider);
> + /* TODO: init qos and priority */
> +

No need to set_rate here before enabling the clock?

> + clk_prepare_enable(qp->bus_clk);
> + clk_prepare_enable(qp->bus_a_clk);
> +
> + return 0;
> +}
> +
> +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst,
> + u32 avg, u32 peak)
> +{
> + struct qcom_icc_provider *qp;
> + struct qcom_icc_node *qn;
> + struct icc_node *node;
> + struct icc_provider *provider;
> + u64 avg_bw;
> + u64 peak_bw;
> + u64 rate = 0;
> + int ret = 0;
> +
> + if (!src)
> + node = dst;
> + else
> + node = src;
> +
> + qn = node->data;
> + provider = node->provider;
> + qp = to_qcom_provider(node->provider);
> +
> + /* convert from kbps to bps */
> + avg_bw = avg * 1000ULL;
> + peak_bw = peak * 1000ULL;
> +

Since the core uses kbps and various SoC HW might use bps (or other
units), perhaps consider providing a macro in the core header such as:

#define icc_units_to_bps(bw) (bw * 1000ULL)

and then move this conversion to the top of the function where you
define the variable

u64 avg_bw = icc_units_to_bps(avg);
u64 peak_bw = icc_units_to_bps(peak);

Since other drivers will end up copying this driver, it might help
prevent silly errors in the drivers and has a side-effect of allowing
the core to change internal units w/o any driver changes down the
line, if needed.

> + /* set bandwidth */
> + if (qn->ap_owned) {
> + /* TODO: set QoS */
> + } else {
> + /* send message to the RPM processor */
> + if (qn->mas_rpm_id != -1) {
> + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
> + RPM_BUS_MASTER_REQ,
> + qn->mas_rpm_id,
> + avg_bw);
> + }
> +
> + if (qn->slv_rpm_id != -1) {
> + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
> + RPM_BUS_SLAVE_REQ,
> + qn->slv_rpm_id,
> + avg_bw);
> + }
> + }
> +
> + rate = max(avg_bw, peak_bw);
> +
> + do_div(rate, qn->buswidth);
> +
> + if (qn->rate != rate) {
> + ret = clk_set_rate(qp->bus_clk, rate);
> + if (ret) {
> + pr_err("set clk rate %lld error %d\n", rate, ret);
> + return ret;
> + }
> +
> + ret = clk_set_rate(qp->bus_a_clk, rate);
> + if (ret) {
> + pr_err("set clk rate %lld error %d\n", rate, ret);
> + return ret;
> + }
> +
> + qn->rate = rate;
> + }
> +
> + return ret;
> +}
> +
> +static int qnoc_probe(struct platform_device *pdev)
> +{
> + const struct qcom_icc_desc *desc;
> + struct qcom_icc_node **qnodes;
> + struct qcom_icc_provider *qp;
> + struct resource *res;
> + struct icc_provider *provider;
> + size_t num_nodes, i;
> + int ret;
> +
> + /* wait for RPM */
> + if (!qcom_icc_rpm_smd_available())
> + return -EPROBE_DEFER;
> +
> + desc = of_device_get_match_data(&pdev->dev);
> + if (!desc)
> + return -EINVAL;
> +
> + qnodes = desc->nodes;
> + num_nodes = desc->num_nodes;
> +
> + qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
> + if (!qp)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + qp->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(qp->base))
> + return PTR_ERR(qp->base);
> +
> + qp->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
> + if (IS_ERR(qp->bus_clk))
> + return PTR_ERR(qp->bus_clk);
> +
> + qp->bus_a_clk = devm_clk_get(&pdev->dev, "bus_a_clk");
> + if (IS_ERR(qp->bus_a_clk))
> + return PTR_ERR(qp->bus_a_clk);
> +
> + provider = &qp->provider;
> + provider->dev = &pdev->dev;
> + provider->set = &qcom_icc_set;
> + INIT_LIST_HEAD(&provider->nodes);
> + provider->data = qp;
> +
> + ret = icc_add_provider(provider);
> + if (ret) {
> + dev_err(&pdev->dev, "error adding interconnect provider\n");
> + return ret;
> + }
> +
> + for (i = 0; i < num_nodes; i++) {
> + struct icc_node *node;
> + int ret;
> + size_t j;
> +
> + node = icc_node_create(qnodes[i]->id);
> + if (IS_ERR(node)) {
> + ret = PTR_ERR(node);
> + goto err;
> + }
> +
> + node->name = qnodes[i]->name;
> + node->data = qnodes[i];
> + icc_node_add(node, provider);
> +
> + dev_dbg(&pdev->dev, "registered node %p %s %d\n", node,
> + qnodes[i]->name, node->id);
> +
> + /* populate links */
> + for (j = 0; j < qnodes[i]->num_links; j++)
> + if (qnodes[i]->links[j])
> + icc_link_create(node, qnodes[i]->links[j]);
> +
> + ret = qcom_icc_init(node);
> + if (ret)
> + dev_err(&pdev->dev, "%s init error (%d)\n", node->name,
> + ret);
> + }
> +
> + platform_set_drvdata(pdev, provider);
> +
> + return ret;
> +err:
> + icc_del_provider(provider);
> + return ret;
> +}
> +
> +static int qnoc_remove(struct platform_device *pdev)
> +{
> + struct icc_provider *provider = platform_get_drvdata(pdev);
> +
> + icc_del_provider(provider);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id qnoc_of_match[] = {
> + { .compatible = "qcom,msm8916-pnoc", .data = &msm8916_pnoc },
> + { .compatible = "qcom,msm8916-snoc", .data = &msm8916_snoc },
> + { .compatible = "qcom,msm8916-bimc", .data = &msm8916_bimc },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, qnoc_of_match);
> +
> +static struct platform_driver qnoc_driver = {
> + .probe = qnoc_probe,
> + .remove = qnoc_remove,
> + .driver = {
> + .name = "qnoc-msm8916",
> + .of_match_table = qnoc_of_match,
> + },
> +};
> +module_platform_driver(qnoc_driver);
> +MODULE_AUTHOR("Georgi Djakov <[email protected]>");
> +MODULE_DESCRIPTION("Qualcomm msm8916 NoC driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/interconnect/qcom.h b/include/linux/interconnect/qcom.h
> new file mode 100644
> index 000000000000..2cd378d2f575
> --- /dev/null
> +++ b/include/linux/interconnect/qcom.h
> @@ -0,0 +1,350 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Qualcomm interconnect IDs
> + *
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef __QCOM_INTERCONNECT_IDS_H
> +#define __QCOM_INTERCONNECT_IDS_H
> +
> +#define FAB_BIMC 0
> +#define FAB_SYS_NOC 1024
> +#define FAB_MMSS_NOC 2048
> +#define FAB_OCMEM_NOC 3072
> +#define FAB_PERIPH_NOC 4096
> +#define FAB_CONFIG_NOC 5120
> +#define FAB_OCMEM_VNOC 6144
> +
> +#define MASTER_AMPSS_M0 1
> +#define MASTER_AMPSS_M1 2
> +#define APPSS_MASTER_FAB_MMSS 3
> +#define APPSS_MASTER_FAB_SYSTEM 4
> +#define SYSTEM_MASTER_FAB_APPSS 5
> +#define MASTER_SPS 6
> +#define MASTER_ADM_PORT0 7
> +#define MASTER_ADM_PORT1 8
> +#define SYSTEM_MASTER_ADM1_PORT0 9
> +#define MASTER_ADM1_PORT1 10
> +#define MASTER_LPASS_PROC 11
> +#define MASTER_MSS_PROCI 12
> +#define MASTER_MSS_PROCD 13
> +#define MASTER_MSS_MDM_PORT0 14
> +#define MASTER_LPASS 15
> +#define SYSTEM_MASTER_CPSS_FPB 16
> +#define SYSTEM_MASTER_SYSTEM_FPB 17
> +#define SYSTEM_MASTER_MMSS_FPB 18
> +#define MASTER_ADM1_CI 19
> +#define MASTER_ADM0_CI 20
> +#define MASTER_MSS_MDM_PORT1 21
> +#define MASTER_MDP_PORT0 22
> +#define MASTER_MDP_PORT1 23
> +#define MMSS_MASTER_ADM1_PORT0 24
> +#define MASTER_ROTATOR 25
> +#define MASTER_GRAPHICS_3D 26
> +#define MASTER_JPEG_DEC 27
> +#define MASTER_GRAPHICS_2D_CORE0 28
> +#define MASTER_VFE 29
> +#define MASTER_VPE 30
> +#define MASTER_JPEG_ENC 31
> +#define MASTER_GRAPHICS_2D_CORE1 32
> +#define MMSS_MASTER_APPS_FAB 33
> +#define MASTER_HD_CODEC_PORT0 34
> +#define MASTER_HD_CODEC_PORT1 35
> +#define MASTER_SPDM 36
> +#define MASTER_RPM 37
> +#define MASTER_MSS 38
> +#define MASTER_RIVA 39
> +#define SYSTEM_MASTER_UNUSED_6 40
> +#define MASTER_MSS_SW_PROC 41
> +#define MASTER_MSS_FW_PROC 42
> +#define MMSS_MASTER_UNUSED_2 43
> +#define MASTER_GSS_NAV 44
> +#define MASTER_PCIE 45
> +#define MASTER_SATA 46
> +#define MASTER_CRYPTO 47
> +#define MASTER_VIDEO_CAP 48
> +#define MASTER_GRAPHICS_3D_PORT1 49
> +#define MASTER_VIDEO_ENC 50
> +#define MASTER_VIDEO_DEC 51
> +#define MASTER_LPASS_AHB 52
> +#define MASTER_QDSS_BAM 53
> +#define MASTER_SNOC_CFG 54
> +#define MASTER_CRYPTO_CORE0 55
> +#define MASTER_CRYPTO_CORE1 56
> +#define MASTER_MSS_NAV 57
> +#define MASTER_OCMEM_DMA 58
> +#define MASTER_WCSS 59
> +#define MASTER_QDSS_ETR 60
> +#define MASTER_USB3 61
> +#define MASTER_JPEG 62
> +#define MASTER_VIDEO_P0 63
> +#define MASTER_VIDEO_P1 64
> +#define MASTER_MSS_PROC 65
> +#define MASTER_JPEG_OCMEM 66
> +#define MASTER_MDP_OCMEM 67
> +#define MASTER_VIDEO_P0_OCMEM 68
> +#define MASTER_VIDEO_P1_OCMEM 69
> +#define MASTER_VFE_OCMEM 70
> +#define MASTER_CNOC_ONOC_CFG 71
> +#define MASTER_RPM_INST 72
> +#define MASTER_RPM_DATA 73
> +#define MASTER_RPM_SYS 74
> +#define MASTER_DEHR 75
> +#define MASTER_QDSS_DAP 76
> +#define MASTER_TIC 77
> +#define MASTER_SDCC_1 78
> +#define MASTER_SDCC_3 79
> +#define MASTER_SDCC_4 80
> +#define MASTER_SDCC_2 81
> +#define MASTER_TSIF 82
> +#define MASTER_BAM_DMA 83
> +#define MASTER_BLSP_2 84
> +#define MASTER_USB_HSIC 85
> +#define MASTER_BLSP_1 86
> +#define MASTER_USB_HS 87
> +#define MASTER_PNOC_CFG 88
> +#define MASTER_V_OCMEM_GFX3D 89
> +#define MASTER_IPA 90
> +#define MASTER_QPIC 91
> +#define MASTER_MDPE 92
> +#define MASTER_USB_HS2 93
> +#define MASTER_VPU 94
> +#define MASTER_UFS 95
> +#define MASTER_BCAST 96
> +#define MASTER_CRYPTO_CORE2 97
> +#define MASTER_EMAC 98
> +#define MASTER_VPU_1 99
> +#define MASTER_PCIE_1 100
> +#define MASTER_USB3_1 101
> +#define MASTER_CNOC_MNOC_MMSS_CFG 102
> +#define MASTER_CNOC_MNOC_CFG 103
> +#define MASTER_TCU_0 104
> +#define MASTER_TCU_1 105
> +#define MASTER_CPP 106
> +#define MASTER_AUDIO 107
> +
> +#define SNOC_MM_INT_0 10000
> +#define SNOC_MM_INT_1 10001
> +#define SNOC_MM_INT_2 10002
> +#define SNOC_MM_INT_BIMC 10003
> +#define SNOC_INT_0 10004
> +#define SNOC_INT_1 10005
> +#define SNOC_INT_BIMC 10006
> +#define SNOC_BIMC_0_MAS 10007
> +#define SNOC_BIMC_1_MAS 10008
> +#define SNOC_QDSS_INT 10009
> +#define PNOC_SNOC_MAS 10010
> +#define PNOC_SNOC_SLV 10011
> +#define PNOC_INT_0 10012
> +#define PNOC_INT_1 10013
> +#define PNOC_M_0 10014
> +#define PNOC_M_1 10015
> +#define BIMC_SNOC_MAS 10016
> +#define BIMC_SNOC_SLV 10017
> +#define PNOC_SLV_0 10018
> +#define PNOC_SLV_1 10019
> +#define PNOC_SLV_2 10020
> +#define PNOC_SLV_3 10021
> +#define PNOC_SLV_4 10022
> +#define PNOC_SLV_8 10023
> +#define PNOC_SLV_9 10024
> +#define SNOC_BIMC_0_SLV 10025
> +#define SNOC_BIMC_1_SLV 10026
> +#define MNOC_BIMC_MAS 10027
> +#define MNOC_BIMC_SLV 10028
> +#define BIMC_MNOC_MAS 10029
> +#define BIMC_MNOC_SLV 10030
> +#define SNOC_BIMC_MAS 10031
> +#define SNOC_BIMC_SLV 10032
> +#define CNOC_SNOC_MAS 10033
> +#define CNOC_SNOC_SLV 10034
> +#define SNOC_CNOC_MAS 10035
> +#define SNOC_CNOC_SLV 10036
> +#define OVNOC_SNOC_MAS 10037
> +#define OVNOC_SNOC_SLV 10038
> +#define SNOC_OVNOC_MAS 10039
> +#define SNOC_OVNOC_SLV 10040
> +#define SNOC_PNOC_MAS 10041
> +#define SNOC_PNOC_SLV 10042
> +#define BIMC_INT_APPS_EBI 10043
> +#define BIMC_INT_APPS_SNOC 10044
> +#define SNOC_BIMC_2_MAS 10045
> +#define SNOC_BIMC_2_SLV 10046
> +#define PNOC_SLV_5 10047
> +#define PNOC_SLV_7 10048
> +#define PNOC_INT_2 10049
> +#define PNOC_INT_3 10050
> +#define PNOC_INT_4 10051
> +#define PNOC_INT_5 10052
> +#define PNOC_INT_6 10053
> +#define PNOC_INT_7 10054
> +
> +#define SLAVE_EBI_CH0 512
> +#define SLAVE_EBI_CH1 513
> +#define SLAVE_AMPSS_L2 514
> +#define APPSS_SLAVE_FAB_MMSS 515
> +#define APPSS_SLAVE_FAB_SYSTEM 516
> +#define SYSTEM_SLAVE_FAB_APPS 517
> +#define SLAVE_SPS 518
> +#define SLAVE_SYSTEM_IMEM 519
> +#define SLAVE_AMPSS 520
> +#define SLAVE_MSS 521
> +#define SLAVE_LPASS 522
> +#define SYSTEM_SLAVE_CPSS_FPB 523
> +#define SYSTEM_SLAVE_SYSTEM_FPB 524
> +#define SYSTEM_SLAVE_MMSS_FPB 525
> +#define SLAVE_CORESIGHT 526
> +#define SLAVE_RIVA 527
> +#define SLAVE_SMI 528
> +#define MMSS_SLAVE_FAB_APPS 529
> +#define MMSS_SLAVE_FAB_APPS_1 530
> +#define SLAVE_MM_IMEM 531
> +#define SLAVE_CRYPTO 532
> +#define SLAVE_SPDM 533
> +#define SLAVE_RPM 534
> +#define SLAVE_RPM_MSG_RAM 535
> +#define SLAVE_MPM 536
> +#define SLAVE_PMIC1_SSBI1_A 537
> +#define SLAVE_PMIC1_SSBI1_B 538
> +#define SLAVE_PMIC1_SSBI1_C 539
> +#define SLAVE_PMIC2_SSBI2_A 540
> +#define SLAVE_PMIC2_SSBI2_B 541
> +#define SLAVE_GSBI1_UART 542
> +#define SLAVE_GSBI2_UART 543
> +#define SLAVE_GSBI3_UART 544
> +#define SLAVE_GSBI4_UART 545
> +#define SLAVE_GSBI5_UART 546
> +#define SLAVE_GSBI6_UART 547
> +#define SLAVE_GSBI7_UART 548
> +#define SLAVE_GSBI8_UART 549
> +#define SLAVE_GSBI9_UART 550
> +#define SLAVE_GSBI10_UART 551
> +#define SLAVE_GSBI11_UART 552
> +#define SLAVE_GSBI12_UART 553
> +#define SLAVE_GSBI1_QUP 554
> +#define SLAVE_GSBI2_QUP 555
> +#define SLAVE_GSBI3_QUP 556
> +#define SLAVE_GSBI4_QUP 557
> +#define SLAVE_GSBI5_QUP 558
> +#define SLAVE_GSBI6_QUP 559
> +#define SLAVE_GSBI7_QUP 560
> +#define SLAVE_GSBI8_QUP 561
> +#define SLAVE_GSBI9_QUP 562
> +#define SLAVE_GSBI10_QUP 563
> +#define SLAVE_GSBI11_QUP 564
> +#define SLAVE_GSBI12_QUP 565
> +#define SLAVE_EBI2_NAND 566
> +#define SLAVE_EBI2_CS0 567
> +#define SLAVE_EBI2_CS1 568
> +#define SLAVE_EBI2_CS2 569
> +#define SLAVE_EBI2_CS3 570
> +#define SLAVE_EBI2_CS4 571
> +#define SLAVE_EBI2_CS5 572
> +#define SLAVE_USB_FS1 573
> +#define SLAVE_USB_FS2 574
> +#define SLAVE_TSIF 575
> +#define SLAVE_MSM_TSSC 576
> +#define SLAVE_MSM_PDM 577
> +#define SLAVE_MSM_DIMEM 578
> +#define SLAVE_MSM_TCSR 579
> +#define SLAVE_MSM_PRNG 580
> +#define SLAVE_GSS 581
> +#define SLAVE_SATA 582
> +#define SLAVE_USB3 583
> +#define SLAVE_WCSS 584
> +#define SLAVE_OCIMEM 585
> +#define SLAVE_SNOC_OCMEM 586
> +#define SLAVE_SERVICE_SNOC 587
> +#define SLAVE_QDSS_STM 588
> +#define SLAVE_CAMERA_CFG 589
> +#define SLAVE_DISPLAY_CFG 590
> +#define SLAVE_OCMEM_CFG 591
> +#define SLAVE_CPR_CFG 592
> +#define SLAVE_CPR_XPU_CFG 593
> +#define SLAVE_MISC_CFG 594
> +#define SLAVE_MISC_XPU_CFG 595
> +#define SLAVE_VENUS_CFG 596
> +#define SLAVE_MISC_VENUS_CFG 597
> +#define SLAVE_GRAPHICS_3D_CFG 598
> +#define SLAVE_MMSS_CLK_CFG 599
> +#define SLAVE_MMSS_CLK_XPU_CFG 600
> +#define SLAVE_MNOC_MPU_CFG 601
> +#define SLAVE_ONOC_MPU_CFG 602
> +#define SLAVE_SERVICE_MNOC 603
> +#define SLAVE_OCMEM 604
> +#define SLAVE_SERVICE_ONOC 605
> +#define SLAVE_SDCC_1 606
> +#define SLAVE_SDCC_3 607
> +#define SLAVE_SDCC_2 608
> +#define SLAVE_SDCC_4 609
> +#define SLAVE_BAM_DMA 610
> +#define SLAVE_BLSP_2 611
> +#define SLAVE_USB_HSIC 612
> +#define SLAVE_BLSP_1 613
> +#define SLAVE_USB_HS 614
> +#define SLAVE_PDM 615
> +#define SLAVE_PERIPH_APU_CFG 616
> +#define SLAVE_PNOC_MPU_CFG 617
> +#define SLAVE_PRNG 618
> +#define SLAVE_SERVICE_PNOC 619
> +#define SLAVE_CLK_CTL 620
> +#define SLAVE_CNOC_MSS 621
> +#define SLAVE_SECURITY 622
> +#define SLAVE_TCSR 623
> +#define SLAVE_TLMM 624
> +#define SLAVE_CRYPTO_0_CFG 625
> +#define SLAVE_CRYPTO_1_CFG 626
> +#define SLAVE_IMEM_CFG 627
> +#define SLAVE_MESSAGE_RAM 628
> +#define SLAVE_BIMC_CFG 629
> +#define SLAVE_BOOT_ROM 630
> +#define SLAVE_CNOC_MNOC_MMSS_CFG 631
> +#define SLAVE_PMIC_ARB 632
> +#define SLAVE_SPDM_WRAPPER 633
> +#define SLAVE_DEHR_CFG 634
> +#define SLAVE_QDSS_CFG 635
> +#define SLAVE_RBCPR_CFG 636
> +#define SLAVE_RBCPR_QDSS_APU_CFG 637
> +#define SLAVE_SNOC_MPU_CFG 638
> +#define SLAVE_CNOC_ONOC_CFG 639
> +#define SLAVE_CNOC_MNOC_CFG 640
> +#define SLAVE_PNOC_CFG 641
> +#define SLAVE_SNOC_CFG 642
> +#define SLAVE_EBI1_DLL_CFG 643
> +#define SLAVE_PHY_APU_CFG 644
> +#define SLAVE_EBI1_PHY_CFG 645
> +#define SLAVE_SERVICE_CNOC 646
> +#define SLAVE_IPS_CFG 647
> +#define SLAVE_QPIC 648
> +#define SLAVE_DSI_CFG 649
> +#define SLAVE_UFS_CFG 650
> +#define SLAVE_RBCPR_CX_CFG 651
> +#define SLAVE_RBCPR_MX_CFG 652
> +#define SLAVE_PCIE_CFG 653
> +#define SLAVE_USB_PHYS_CFG 654
> +#define SLAVE_VIDEO_CAP_CFG 655
> +#define SLAVE_AVSYNC_CFG 656
> +#define SLAVE_CRYPTO_2_CFG 657
> +#define SLAVE_VPU_CFG 658
> +#define SLAVE_BCAST_CFG 659
> +#define SLAVE_KLM_CFG 660
> +#define SLAVE_GENI_IR_CFG 661
> +#define SLAVE_OCMEM_GFX 662
> +#define SLAVE_CATS_128 663
> +#define SLAVE_OCMEM_64 664
> +#define SLAVE_PCIE_0 665
> +#define SLAVE_PCIE_1 666
> +#define SLAVE_PCIE_0_CFG 667
> +#define SLAVE_PCIE_1_CFG 668
> +#define SLAVE_SRVC_MNOC 669
> +#define SLAVE_USB_HS2 670
> +#define SLAVE_AUDIO 671
> +#define SLAVE_TCU 672
> +#define SLAVE_APPSS 673
> +#define SLAVE_PCIE_PARF 674
> +#define SLAVE_USB3_PHY_CFG 675
> +#define SLAVE_IPA_CFG 676
> +
> +#endif

2018-06-06 15:01:09

by Georgi Djakov

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

Hi Evan,

Thanks for the detailed review!

On 12.05.18 г. 0:30, Evan Green wrote:
> Hi Georgi,
>
> On Fri, Mar 9, 2018 at 1:12 PM Georgi Djakov <[email protected]>
> wrote:
>
>> This patch introduce a new API to get requirements and configure the
>> interconnect buses across the entire chipset to fit with the current
>> demand.
>
>> The API is using a consumer/provider-based model, where the providers are
>> the interconnect buses and the consumers could be various drivers.
>> The consumers request interconnect resources (path) between endpoints and
>> set the desired constraints on this data flow path. The providers receive
>> requests from consumers and aggregate these requests for all master-slave
>> pairs on that path. Then the providers configure each participating in the
>> topology node according to the requested data flow path, physical links
> and
>> constraints. The topology could be complicated and multi-tiered and is SoC
>> specific.
>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
>> Documentation/interconnect/interconnect.rst | 96 ++++++
>> drivers/Kconfig | 2 +
>> drivers/Makefile | 1 +
>> drivers/interconnect/Kconfig | 10 +
>> drivers/interconnect/Makefile | 1 +
>> drivers/interconnect/core.c | 489
> ++++++++++++++++++++++++++++
>> include/linux/interconnect-provider.h | 109 +++++++
>> include/linux/interconnect.h | 40 +++
>> 8 files changed, 748 insertions(+)
>> create mode 100644 Documentation/interconnect/interconnect.rst
>> create mode 100644 drivers/interconnect/Kconfig
>> create mode 100644 drivers/interconnect/Makefile
>> create mode 100644 drivers/interconnect/core.c
>> create mode 100644 include/linux/interconnect-provider.h
>> create mode 100644 include/linux/interconnect.h
>
> ...
>> diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c

[..]

>> +static struct icc_path *path_allocate(struct icc_node *node, ssize_t
> num_nodes)
>> +{
>
> So node is really the destination, correct? Then we use ->reverse to walk
> backwards num_nodes steps towards the source. It might increase readability
> to call the parameter dest, then assign that to a local called node for
> traversal.

Yes, exactly. Will change the name of the parameter to dst.

>> + struct icc_path *path;
>> + size_t i;
>> +
>> + path = kzalloc(sizeof(*path) + num_nodes * sizeof(*path->reqs),
>> + GFP_KERNEL);
>> + if (!path)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + path->num_nodes = num_nodes;
>> +
>> + for (i = 0; i < num_nodes; i++) {
>> + hlist_add_head(&path->reqs[i].req_node, &node->req_list);
>> +
>> + path->reqs[i].node = node;
>> + /* reference to previous node was saved during path
> traversal */
>> + node = node->reverse;
>> + }
>> +
>> + return path;
>> +}
>> +
>> +static struct icc_path *path_find(struct icc_node *src, struct icc_node
> *dst)
>> +{
>> + struct icc_node *node = NULL;
>> + struct list_head traverse_list;
>> + struct list_head edge_list;
>> + struct list_head tmp_list;
>> + size_t i, number = 0;
>> + bool found = false;
>> +
>> + INIT_LIST_HEAD(&traverse_list);
>> + INIT_LIST_HEAD(&edge_list);
>> + INIT_LIST_HEAD(&tmp_list);
>
> tmp_list is really the list of nodes you've already visited and need to
> remember to reset is_traversed for. Maybe calling this done_list or
> visited_list would be more descriptive.

Yes, visited_list sounds better. Will change it.

>> +
>> + list_add_tail(&src->search_list, &traverse_list);
>
> For added paranoia, you could set src->reverse to NULL so that somebody
> elsewhere who had a bug in their back-traversal would fall off the end,
> rather than into some previous scrapped path.

Ok.

>> +
>> + do {
>> + list_for_each_entry(node, &traverse_list, search_list) {
>> + if (node == dst) {
>> + found = true;
>> + list_add(&node->search_list, &tmp_list);
>> + break;
>> + }
>> + for (i = 0; i < node->num_links; i++) {
>> + struct icc_node *tmp = node->links[i];
>> +
>> + if (!tmp)
>> + return ERR_PTR(-ENOENT);
>
> You just bail out here, but never clean up the nodes is_traversed, which
> will ruin later searches. Maybe a goto towards the common cleanup path?

Agree. Good catch.

>> +
>> + if (tmp->is_traversed)
>> + continue;
>> +
>> + tmp->is_traversed = true;
>> + tmp->reverse = node;
>> + list_add_tail(&tmp->search_list,
> &edge_list);
>> + }
>> + }
>> + if (found)
>> + break;
>> +
>> + list_splice_init(&traverse_list, &tmp_list);
>> + list_splice_init(&edge_list, &traverse_list);
>> +
>> + /* count the number of nodes */
>> + number++;
>
> Depth might be a better name for this, since this really counts the hops
> away from the source, rather than the number of nodes you've processed.

Ok, will change it to depth.

>> +
>> + } while (!list_empty(&traverse_list));
>> +
>> + /* reset the traversed state */
>> + list_for_each_entry(node, &tmp_list, search_list)
>> + node->is_traversed = false;
>> +
>> + if (found)
>> + return path_allocate(dst, number);
>> +
>> + return ERR_PTR(-EPROBE_DEFER);
>> +}
>> +
>> +static int path_init(struct icc_path *path)
>> +{
>> + struct icc_node *node;
>> + size_t i;
>> +
>> + for (i = 0; i < path->num_nodes; i++) {
>> + node = path->reqs[i].node;
>> +
>> + mutex_lock(&node->provider->lock);
>> + node->provider->users++;
>> + mutex_unlock(&node->provider->lock);
>> + }
>> +
>> + return 0;
>> +}
>
> This function cannot fail, nor do you check its return value, so you should
> change the return type to void.

Ok.

>
> I'm wondering if the locking here is a little sketchy. I was in the process
> of typing a suggestion that you call path_init from within path_find, since
> it seemed weird to have this gray zone of a path without its reference
> counts, when I noticed the locks.
>
> I can't evaluate fully, since the implementation seems to be missing
> icc_node_remove, a critical function in terms of evaluating the locks. You
> have an icc_del_provider, but its warning of if (provider->users) is pretty
> weak, since without node removal provider->users could easily be
> incremented after the provider lock is released. It also leaks all of its
> nodes, since there's no way to remove them.
>
> Here's my suggestion as far as the locking goes:
> * To add or remove links/nodes from the graph, you're going to need to hold
> a global lock to avoid colliding with traversals. You've already got an
> icc_path_mutex, so that would work.
> * icc_link_create needs to hold the global icc_path_mutex, since it's
> messing with arrays and connections used in path traversal, and doesn't
> need to hold the provider lock, since it's not changing anything there.
> * The presumably upcoming icc_link_destroy, or its parent icc_node_destroy,
> also needs to hold the global lock. node_destroy may also need the provider
> lock in symmetry with icc_node_add.
> * Provider->users will be protected under the global icc_path_mutex, rather
> than the provider lock. Then move path_init into path_find, or inline it
> into path_allocate.
> * Once you do that, provider->lock is now only protecting its node list.
> For now, it's probably more efficient to roll the protection of
> provider->nodes under the global lock as well, and remove the lock from the
> provider altogether. If you anticipate other functions in the future that
> will require a lock in the provider, then it might make sense to keep the
> lock, or maybe just add it later with that new functionality.

Ok, i will try to simplify this. Using one global lock would be ok for
now. Will implement the complete set for functions for remove links and
nodes from the topology.

>> +
>> +static void node_aggregate(struct icc_node *node)
>> +{
>> + struct icc_req *r;
>> + u32 agg_avg = 0;
>> + u32 agg_peak = 0;
>> +
>> + hlist_for_each_entry(r, &node->req_list, req_node) {
>> + /* sum(averages) and max(peaks) */
>> + agg_avg += r->avg_bw;
>> + agg_peak = max(agg_peak, r->peak_bw);
>> + }
>> +
>> + node->avg_bw = agg_avg;
>> + node->peak_bw = agg_peak;
>> +}
>> +
>> +static void provider_aggregate(struct icc_provider *provider, u32
> *avg_bw,
>> + u32 *peak_bw)
>> +{
>> + struct icc_node *n;
>> + u32 agg_avg = 0;
>> + u32 agg_peak = 0;
>> +
>> + /* aggregate for the interconnect provider */
>> + list_for_each_entry(n, &provider->nodes, node_list) {
>> + /* sum the average and max the peak */
>> + agg_avg += n->avg_bw;
>> + agg_peak = max(agg_peak, n->peak_bw);
>> + }
>> +
>> + *avg_bw = agg_avg;
>> + *peak_bw = agg_peak;
>> +}
>> +
>> +static int constraints_apply(struct icc_path *path)
>> +{
>
> Nit: maybe name it apply_constraints, since constraints_apply sounds like a
> query (do the constraints apply?).

Ok.

>> + struct icc_node *next, *prev = NULL;
>> + int i;
>> +
>> + for (i = 0; i < path->num_nodes; i++, prev = next) {
>> + struct icc_provider *provider;
>> + u32 avg_bw = 0;
>> + u32 peak_bw = 0;
>> + int ret;
>> +
>> + next = path->reqs[i].node;
>> + /*
>> + * Both endpoints should be valid master-slave pairs of
> the
>> + * same interconnect provider that will be configured.
>> + */
>> + if (!next || !prev)
>> + continue;
>> +
>> + if (next->provider != prev->provider)
>> + continue;
>
> next should never be null, right? So you could shorten this to if (!prev ||
> (next->provider != prev->provider))

Yes, right. Will do so.

>> +
>> + provider = next->provider;
>> + mutex_lock(&provider->lock);
>> +
>> + /* aggregate requests for the provider */
>> + provider_aggregate(provider, &avg_bw, &peak_bw);
>> +
>> + if (provider->set) {
>> + /* set the constraints */
>> + ret = provider->set(prev, next, avg_bw, peak_bw);
>> + }
>> +
>> + mutex_unlock(&provider->lock);
>> +
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * icc_set() - set constraints on an interconnect path between two
> endpoints
>> + * @path: reference to the path returned by icc_get()
>> + * @avg_bw: average bandwidth in kbps
>> + * @peak_bw: peak bandwidth in kbps
>> + *
>> + * This function is used by an interconnect consumer to express its own
> needs
>> + * in term of bandwidth and QoS for a previously requested path between
> two
>
> "in terms of" rather than "in term of", and not really QoS yet, right?
>
>> + * endpoints. The requests are aggregated and each node is updated
> accordingly.
>> + *
>> + * Returns 0 on success, or an approproate error code otherwise.
>
> appropriate
>

Ok, thanks!

>> + */
>> +int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
>> +{
>> + struct icc_node *node;
>> + size_t i;
>> + int ret;
>> +
>> + if (!path)
>> + return 0;
>
> Can we ditch this null check? My understanding is it's generally preferred
> to skip this if it's only there to avoid developer errors.

Ok.

>> +
>> + for (i = 0; i < path->num_nodes; i++) {
>> + node = path->reqs[i].node;
>> +
>> + mutex_lock(&icc_path_mutex);
>> +
>> + /* update the consumer request for this path */
>> + path->reqs[i].avg_bw = avg_bw;
>> + path->reqs[i].peak_bw = peak_bw;
>> +
>> + /* aggregate requests for this node */
>> + node_aggregate(node);
>> +
>> + mutex_unlock(&icc_path_mutex);
>> + }
>> +
>> + ret = constraints_apply(path);
>> + if (ret)
>> + pr_err("interconnect: error applying constraints (%d)",
> ret);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(icc_set);
>> +
>> +/**
>> + * icc_get() - return a handle for path between two endpoints
>> + * @src_id: source device port id
>> + * @dst_id: destination device port id
>> + *
>> + * 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 the they are not needed anymore.
>> + *
>> + * Return: icc_path pointer on success, or ERR_PTR() on error
>> + */
>> +struct icc_path *icc_get(const int src_id, const int dst_id)
>> +{
>> + struct icc_node *src, *dst;
>> + struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
>> +
>> + src = node_find(src_id);
>> + if (!src)
>> + goto out;
>> +
>> + dst = node_find(dst_id);
>> + if (!dst)
>> + goto out;
>> +
>> + mutex_lock(&icc_path_mutex);
>> + path = path_find(src, dst);
>> + mutex_unlock(&icc_path_mutex);
>> + if (IS_ERR(path))
>> + goto out;
>> +
>> + path_init(path);
>> +
>> +out:
>> + return path;
>> +}
>> +EXPORT_SYMBOL_GPL(icc_get);
>> +
>> +/**
>> + * icc_put() - release the reference to the icc_path
>> + * @path: 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.
>> + */
>> +void icc_put(struct icc_path *path)
>> +{
>> + struct icc_node *node;
>> + size_t i;
>> + int ret;
>> +
>> + if (!path || WARN_ON_ONCE(IS_ERR(path)))
>> + return;
>> +
>> + ret = icc_set(path, 0, 0);
>> + if (ret)
>> + pr_err("%s: error (%d)\n", __func__, ret);
>> +
>> + for (i = 0; i < path->num_nodes; i++) {
>> + node = path->reqs[i].node;
>> + hlist_del(&path->reqs[i].req_node);
>> +
>> + mutex_lock(&node->provider->lock);
>> + node->provider->users--;
>> + mutex_unlock(&node->provider->lock);
>> + }
>> +
>> + kfree(path);
>> +}
>> +EXPORT_SYMBOL_GPL(icc_put);
>> +
>> +/**
>> + * icc_node_create() - create a node
>> + * @id: node id
>> + *
>> + * Return: icc_node pointer on success, or ERR_PTR() on error
>> + */
>> +struct icc_node *icc_node_create(int id)
>> +{
>> + struct icc_node *node;
>> +
>> + /* check if node already exists */
>> + node = node_find(id);
>> + if (node)
>> + return node;
>
> This is probably going to do more harm than good once icc_node_delete comes
> in, since it almost certainly indicates a programmer error or ID collision,
> and will likely result in a double free. We should probably fail with
> EEXIST instead.

In the current approach we create the nodes one by one, and the linked
nodes are created when they are referenced. The other way around would
be to create first all the nodes and then populate the links to avoid
the "chicken and egg" problem.

>> +
>> + node = kzalloc(sizeof(*node), GFP_KERNEL);
>> + if (!node)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + id = idr_alloc(&icc_idr, node, id, id + 1, GFP_KERNEL);
>> + if (WARN(id < 0, "couldn't get idr"))
>> + return ERR_PTR(id);
>> +
>> + node->id = id;
>> +
>> + return node;
>> +}
>> +EXPORT_SYMBOL_GPL(icc_node_create);
>> +
>> +/**
>> + * icc_link_create() - create a link between two nodes
>> + * @src_id: source node id
>> + * @dst_id: destination node id
>> + *
>> + * Return: 0 on success, or an error code otherwise
>> + */
>> +int icc_link_create(struct icc_node *node, const int dst_id)
>> +{
>> + struct icc_node *dst;
>> + struct icc_node **new;
>> + int ret = 0;
>> +
>> + if (IS_ERR_OR_NULL(node))
>> + return PTR_ERR(node);
>
> Remove this.

Ok.

>> +
>> + mutex_lock(&node->provider->lock);
>> +
>> + dst = node_find(dst_id);
>> + if (!dst)
>> + dst = icc_node_create(dst_id);
>
> icc_node_create can fail, you should fail here if it does.

Ok.

>> +
>> + new = krealloc(node->links,
>> + (node->num_links + 1) * sizeof(*node->links),
>> + GFP_KERNEL);
>> + if (!new) {
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> +
>> + node->links = new;
>> + node->links[node->num_links++] = dst;
>> +
>> +out:
>> + mutex_unlock(&node->provider->lock);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(icc_link_create);
>> +
>> +/**
>> + * icc_add_node() - add an interconnect node to interconnect provider
>> + * @node: pointer to the interconnect node
>> + * @provider: pointer to the interconnect provider
>> + *
>> + * Return: 0 on success, or an error code otherwise
>> + */
>> +int icc_node_add(struct icc_node *node, struct icc_provider *provider)
>> +{
>> + if (WARN_ON(!node))
>> + return -EINVAL;
>> +
>> + if (WARN_ON(!provider))
>> + return -EINVAL;
>
> Remove these.

Ok.

>> +
>> + node->provider = provider;
>> +
>> + mutex_lock(&provider->lock);
>> + list_add_tail(&node->node_list, &provider->nodes);
>> + mutex_unlock(&provider->lock);
>> +
>> + return 0;
>> +}
>
> icc_node_add should be exported, right? I see it being used in msm8916.c.
> You should make sure that "make allmodconfig" still builds with your
> changes.

Yes. Agree.

>
> I think you should add a safety check in icc_link_create to ensure that the
> node has a provider before adding any links. If some consumer made a
> mistake and added links before adding the node to the provider, path
> traversal would use the uninitialized or NULL provider pointer. I was
> thinking about this while noticing that you assign node->provider before
> acquiring the lock.

Ok.

>> +
>> +/**
>> + * icc_add_provider() - add a new interconnect provider
>> + * @icc_provider: the interconnect provider that will be added into
> topology
>> + *
>> + * Return: 0 on success, or an error code otherwise
>> + */
>> +int icc_add_provider(struct icc_provider *provider)
>> +{
>> + if (WARN_ON(!provider))
>> + return -EINVAL;
>> +
>
> Remove this one. The one below is okay.

Ok.

>> + if (WARN_ON(!provider->set))
>> + return -EINVAL;
>> +
>> + mutex_init(&provider->lock);
>> + INIT_LIST_HEAD(&provider->nodes);
>> +
>> + mutex_lock(&icc_provider_list_mutex);
>> + list_add(&provider->provider_list, &icc_provider_list);
>> + mutex_unlock(&icc_provider_list_mutex);
>> +
>> + dev_dbg(provider->dev, "interconnect provider added to
> topology\n");
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(icc_add_provider);
>> +
>> +/**
>> + * icc_del_provider() - delete previously added interconnect provider
>> + * @icc_provider: the interconnect provider that will be removed from
> topology
>> + *
>> + * Return: 0 on success, or an error code otherwise
>> + */
>> +int icc_del_provider(struct icc_provider *provider)
>> +{
>> + mutex_lock(&provider->lock);
>> + if (provider->users) {
>> + pr_warn("interconnect provider still has %d users\n",
>> + provider->users);
>> + }
>> + mutex_unlock(&provider->lock);
>> +
>> + mutex_lock(&icc_provider_list_mutex);
>> + list_del(&provider->provider_list);
>> + mutex_unlock(&icc_provider_list_mutex);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(icc_del_provider);
>> +
>> +MODULE_AUTHOR("Georgi Djakov <[email protected]");
>> +MODULE_DESCRIPTION("Interconnect Driver Core");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/interconnect-provider.h
> b/include/linux/interconnect-provider.h
>> new file mode 100644
>> index 000000000000..779b5b5b1306
>> --- /dev/null
>> +++ b/include/linux/interconnect-provider.h
>> @@ -0,0 +1,109 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2018, Linaro Ltd.
>> + * Author: Georgi Djakov <[email protected]>
>> + */
>> +
>> +#ifndef _LINUX_INTERCONNECT_PROVIDER_H
>> +#define _LINUX_INTERCONNECT_PROVIDER_H
>> +
>> +#include <linux/interconnect.h>
>> +
>> +struct icc_node;
>> +
>> +/**
>> + * struct icc_provider - interconnect provider (controller) entity that
> might
>> + * provide multiple interconnect controls
>> + *
>> + * @provider_list: list of the registered interconnect providers
>> + * @nodes: internal list of the interconnect provider nodes
>> + * @set: pointer to device specific set operation function
>> + * @dev: the device this interconnect provider belongs to
>> + * @lock: lock to provide consistency during aggregation/update of
> constraints
>> + * @users: count of active users
>> + * @data: pointer to private data
>> + */
>> +struct icc_provider {
>> + struct list_head provider_list;
>> + struct list_head nodes;
>> + int (*set)(struct icc_node *src, struct icc_node *dst,
>> + u32 avg_bw, u32 peak_bw);
>> + struct device *dev;
>> + struct mutex lock;
>> + int users;
>> + void *data;
>> +};
>> +
>> +/**
>> + * struct icc_node - entity that is part of the interconnect topology
>> + *
>> + * @id: platform specific node id
>> + * @name: node name used in debugfs
>> + * @links: a list of targets where we can go next when traversing
>> + * @num_links: number of links to other interconnect nodes
>> + * @provider: points to the interconnect provider of this node
>> + * @node_list: list of interconnect nodes associated with @provider
>> + * @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
>> + * @peak_bw: aggregated value of peak bandwidth
>> + * @data: pointer to private data
>> + */
>> +struct icc_node {
>> + int id;
>
> Why int here? Are you expecting negative numbers? Maybe u32 instead? Or
> even better, maybe a typedef u32 icc_id? Ooh yeah, that way we know when
> parameters and such are passed around that they refer to this.

It's an int, just to be aligned with the idr API. u32 would also work.

>> + const char *name;
>> + struct icc_node **links;
>> + size_t num_links;
>> +
>> + struct icc_provider *provider;
>> + struct list_head node_list;
>
> So the difference between node_list and links is that node_list nodes live
> inside this node, whereas links point at other peers?
>
> Oh no, I get it now after reading the .c file: node_list is the list entry
> in the parent provider's "nodes" list. The comment description could be
> clearer about that.

Ok. Will improve the wording.

Thanks,
Georgi

2018-06-06 15:03:39

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 4/7] interconnect: qcom: Add RPM communication

Hi Evan,

On 12.05.18 г. 0:30, Evan Green wrote:
> On Fri, Mar 9, 2018 at 1:11 PM Georgi Djakov <[email protected]>
> wrote:
>
>> On some Qualcomm SoCs, there is a remote processor, which controls some of
>> the Network-On-Chip interconnect resources. Other CPUs express their needs
>> by communicating with this processor. Add a driver to handle comminication
>> with this remote processor.
>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
>> .../devicetree/bindings/interconnect/qcom-smd.txt | 31 ++++++++
>> drivers/interconnect/qcom/Makefile | 1 +
>> drivers/interconnect/qcom/smd-rpm.c | 90
> ++++++++++++++++++++++
>> drivers/interconnect/qcom/smd-rpm.h | 15 ++++
>> 4 files changed, 137 insertions(+)
>> create mode 100644
> Documentation/devicetree/bindings/interconnect/qcom-smd.txt
>> create mode 100644 drivers/interconnect/qcom/Makefile
>> create mode 100644 drivers/interconnect/qcom/smd-rpm.c
>> create mode 100644 drivers/interconnect/qcom/smd-rpm.h
>
>> diff --git a/Documentation/devicetree/bindings/interconnect/qcom-smd.txt
> b/Documentation/devicetree/bindings/interconnect/qcom-smd.txt
>> new file mode 100644
>> index 000000000000..14e83ed7019b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/interconnect/qcom-smd.txt
>> @@ -0,0 +1,31 @@
>> +Qualcomm SMD-RPM interconnect driver binding
>> +------------------------------------------------
>> +The RPM is a dedicated hardware engine for managing the shared
>> +SoC resources in order to keep the lowest power profile. It
>> +communicates with other hardware subsystems via shared memory
>> +and accepts requests for various resources.
>
> You never say what RPM or SMD stands for. RPM is Resource Power Manager,
> right? But I'm not in the know about SMD. Can you define these somewhere?
>

Sure, it's the Shared Memory Driver back-end used for communicating with
RPM. Now added this explanation into the docs.

Thanks,
Georgi

2018-06-06 15:04:55

by Georgi Djakov

[permalink] [raw]
Subject: Re: [PATCH v4 5/7] interconnect: qcom: Add msm8916 interconnect provider driver

Hi Evan,

On 12.05.18 г. 0:29, Evan Green wrote:
> Hi Georgi,
>
> On Fri, Mar 9, 2018 at 1:11 PM Georgi Djakov <[email protected]>
> wrote:
>
>> Add driver for the Qualcomm interconnect buses found in msm8916 based
>> platforms.
>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
>> drivers/interconnect/Kconfig | 5 +
>> drivers/interconnect/Makefile | 1 +
>> drivers/interconnect/qcom/Kconfig | 11 +
>> drivers/interconnect/qcom/Makefile | 2 +
>> drivers/interconnect/qcom/msm8916.c | 482
> ++++++++++++++++++++++++++++++++++++
>> include/linux/interconnect/qcom.h | 350 ++++++++++++++++++++++++++
>> 6 files changed, 851 insertions(+)
>> create mode 100644 drivers/interconnect/qcom/Kconfig
>> create mode 100644 drivers/interconnect/qcom/msm8916.c
>> create mode 100644 include/linux/interconnect/qcom.h
> ...
>> diff --git a/drivers/interconnect/qcom/msm8916.c
> b/drivers/interconnect/qcom/msm8916.c
>> new file mode 100644
>> index 000000000000..d5b54f8261c8
>> --- /dev/null
>> +++ b/drivers/interconnect/qcom/msm8916.c
> ...
>> +static int qnoc_probe(struct platform_device *pdev)
>> +{
>> + const struct qcom_icc_desc *desc;
>> + struct qcom_icc_node **qnodes;
>> + struct qcom_icc_provider *qp;
>> + struct resource *res;
>> + struct icc_provider *provider;
>> + size_t num_nodes, i;
>> + int ret;
>> +
>> + /* wait for RPM */
>> + if (!qcom_icc_rpm_smd_available())
>> + return -EPROBE_DEFER;
>> +
>> + desc = of_device_get_match_data(&pdev->dev);
>> + if (!desc)
>> + return -EINVAL;
>> +
>> + qnodes = desc->nodes;
>> + num_nodes = desc->num_nodes;
>> +
>> + qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
>> + if (!qp)
>> + return -ENOMEM;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + qp->base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(qp->base))
>> + return PTR_ERR(qp->base);
>> +
>> + qp->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
>> + if (IS_ERR(qp->bus_clk))
>> + return PTR_ERR(qp->bus_clk);
>> +
>> + qp->bus_a_clk = devm_clk_get(&pdev->dev, "bus_a_clk");
>> + if (IS_ERR(qp->bus_a_clk))
>> + return PTR_ERR(qp->bus_a_clk);
>> +
>> + provider = &qp->provider;
>> + provider->dev = &pdev->dev;
>> + provider->set = &qcom_icc_set;
>> + INIT_LIST_HEAD(&provider->nodes);
>> + provider->data = qp;
>> +
>> + ret = icc_add_provider(provider);
>> + if (ret) {
>> + dev_err(&pdev->dev, "error adding interconnect
> provider\n");
>> + return ret;
>> + }
>> +
>> + for (i = 0; i < num_nodes; i++) {
>> + struct icc_node *node;
>> + int ret;
>> + size_t j;
>> +
>> + node = icc_node_create(qnodes[i]->id);
>> + if (IS_ERR(node)) {
>> + ret = PTR_ERR(node);
>> + goto err;
>> + }
>> +
>> + node->name = qnodes[i]->name;
>> + node->data = qnodes[i];
>> + icc_node_add(node, provider);
>> +
>> + dev_dbg(&pdev->dev, "registered node %p %s %d\n", node,
>> + qnodes[i]->name, node->id);
>> +
>> + /* populate links */
>> + for (j = 0; j < qnodes[i]->num_links; j++)
>> + if (qnodes[i]->links[j])
>> + icc_link_create(node,
> qnodes[i]->links[j]);
>> +
>> + ret = qcom_icc_init(node);
>> + if (ret)
>> + dev_err(&pdev->dev, "%s init error (%d)\n",
> node->name,
>> + ret);
>
> Don't you want to call qcom_icc_init before icc_link_create? As soon as
> icc_link_create is called, the node is connected to the graph, and
> qcom_icc_set can be called.
>

I agree.

>> + }
>> +
>> + platform_set_drvdata(pdev, provider);
>> +
>> + return ret;
>> +err:
>> + icc_del_provider(provider);
>> + return ret;
>> +}
>> +
>> +static int qnoc_remove(struct platform_device *pdev)
>> +{
>> + struct icc_provider *provider = platform_get_drvdata(pdev);
>> +
>> + icc_del_provider(provider);
>
> Presumably in the framework nodes and links ought to get cleaned up
> somewhere too, right? As it is now, the devm code frees provider when this
> device is removed, even though provider is still very connected in the
> graph via the nodes and links.

Sorry, this part is incomplete. Will implement the rest of the cleanup code.

>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id qnoc_of_match[] = {
>> + { .compatible = "qcom,msm8916-pnoc", .data = &msm8916_pnoc },
>> + { .compatible = "qcom,msm8916-snoc", .data = &msm8916_snoc },
>> + { .compatible = "qcom,msm8916-bimc", .data = &msm8916_bimc },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, qnoc_of_match);
>> +
>> +static struct platform_driver qnoc_driver = {
>> + .probe = qnoc_probe,
>> + .remove = qnoc_remove,
>> + .driver = {
>> + .name = "qnoc-msm8916",
>> + .of_match_table = qnoc_of_match,
>> + },
>> +};
>> +module_platform_driver(qnoc_driver);
>> +MODULE_AUTHOR("Georgi Djakov <[email protected]>");
>> +MODULE_DESCRIPTION("Qualcomm msm8916 NoC driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/interconnect/qcom.h
> b/include/linux/interconnect/qcom.h
>> new file mode 100644
>> index 000000000000..2cd378d2f575
>> --- /dev/null
>> +++ b/include/linux/interconnect/qcom.h
>> @@ -0,0 +1,350 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Qualcomm interconnect IDs
>> + *
>> + * Copyright (c) 2018, Linaro Ltd.
>> + * Author: Georgi Djakov <[email protected]>
>> + */
>> +
>> +#ifndef __QCOM_INTERCONNECT_IDS_H
>> +#define __QCOM_INTERCONNECT_IDS_H
>> +
>> +#define FAB_BIMC 0
>> +#define FAB_SYS_NOC 1024
>> +#define FAB_MMSS_NOC 2048
>> +#define FAB_OCMEM_NOC 3072
>> +#define FAB_PERIPH_NOC 4096
>> +#define FAB_CONFIG_NOC 5120
>> +#define FAB_OCMEM_VNOC 6144
>
> Looks like you're still following that downstream convention of NoCs being
> divisible by 1024, masters in 1-512, and slaves in 512-1023, then I don't
> know about the 10000s. This was documented somewhere downstream, can you
> add that documentation somewhere in here?

Yes, i am currently following the existing IDs. The 10000s are the nodes
used for the interconnects between the NoCs. I don't think it's worth
documenting this, as the plan is to switch to arbitrary numbers.

Thanks,
Georgi

2018-06-06 15:09:38

by Georgi Djakov

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

Hi Amit,

On 25.05.18 г. 11:26, Amit Kucheria wrote:
> On Fri, Mar 9, 2018 at 11:09 PM, Georgi Djakov <[email protected]> wrote:
>> This patch introduce a new API to get requirements and configure the
>> interconnect buses across the entire chipset to fit with the current
>> demand.
>>
>> The API is using a consumer/provider-based model, where the providers are
>> the interconnect buses and the consumers could be various drivers.
>> The consumers request interconnect resources (path) between endpoints and
>> set the desired constraints on this data flow path. The providers receive
>> requests from consumers and aggregate these requests for all master-slave
>> pairs on that path. Then the providers configure each participating in the
>> topology node according to the requested data flow path, physical links and
>> constraints. The topology could be complicated and multi-tiered and is SoC
>> specific.
>>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
[..]

>> +Interconnect consumers
>> +----------------------
>> +
>> +Interconnect consumers are the clients which use the interconnect APIs to
>> +get paths between endpoints and set their bandwidth/latency/QoS requirements
>> +for these interconnect paths.
>> +
>
> This document is missing a section on the locking semantics of the
> framework. Does the core ensure that the entire path is locked for
> set() to propagate?

Yes, the core will ensure that the path is locked. Will add this to the
function description.

[..]

>> +
>> +static int path_init(struct icc_path *path)
>> +{
>> + struct icc_node *node;
>> + size_t i;
>> +
>> + for (i = 0; i < path->num_nodes; i++) {
>> + node = path->reqs[i].node;
>> +
>> + mutex_lock(&node->provider->lock);
>> + node->provider->users++;
>> + mutex_unlock(&node->provider->lock);
>> + }
>> +
>> + return 0;
>> +}
>> +
>
> Consider adding some comments for node_aggregate and
> provider_aggregate's aggregation algorithm
>
> "We want the path to honor all bandwidth requests, so the average
> bandwidth requirements from each consumer are aggregated at each node
> and provider level. The peak bandwidth requirements will then be the
> highest of all the peak bw requests"
>
> or something to the effect that.

Ok, thanks.

>> +static void node_aggregate(struct icc_node *node)
>> +{
>> + struct icc_req *r;
>> + u32 agg_avg = 0;
>> + u32 agg_peak = 0;
>> +
>> + hlist_for_each_entry(r, &node->req_list, req_node) {
>> + /* sum(averages) and max(peaks) */
>> + agg_avg += r->avg_bw;
>> + agg_peak = max(agg_peak, r->peak_bw);
>> + }
>> +
>> + node->avg_bw = agg_avg;
>> + node->peak_bw = agg_peak;
>> +}
>> +
>> +static void provider_aggregate(struct icc_provider *provider, u32 *avg_bw,
>> + u32 *peak_bw)
>> +{
>> + struct icc_node *n;
>> + u32 agg_avg = 0;
>> + u32 agg_peak = 0;
>> +
>> + /* aggregate for the interconnect provider */
>
> You could get rid of this, the function name says as much.

Ok.

>> + list_for_each_entry(n, &provider->nodes, node_list) {
>> + /* sum the average and max the peak */
>> + agg_avg += n->avg_bw;
>> + agg_peak = max(agg_peak, n->peak_bw);
>> + }
>> +
>> + *avg_bw = agg_avg;
>> + *peak_bw = agg_peak;
>> +}
>> +
>> +static int constraints_apply(struct icc_path *path)
>> +{
>> + struct icc_node *next, *prev = NULL;
>> + int i;
>> +
>> + for (i = 0; i < path->num_nodes; i++, prev = next) {
>> + struct icc_provider *provider;
>> + u32 avg_bw = 0;
>> + u32 peak_bw = 0;
>> + int ret;
>> +
>> + next = path->reqs[i].node;
>> + /*
>> + * Both endpoints should be valid master-slave pairs of the
>> + * same interconnect provider that will be configured.
>> + */
>> + if (!next || !prev)
>> + continue;
>> +
>> + if (next->provider != prev->provider)
>> + continue;
>> +
>> + provider = next->provider;
>> + mutex_lock(&provider->lock);
>> +
>> + /* aggregate requests for the provider */
>
> Get rid of comment.

Ok.

>> + provider_aggregate(provider, &avg_bw, &peak_bw);
>> +
>> + if (provider->set) {
>> + /* set the constraints */
>> + ret = provider->set(prev, next, avg_bw, peak_bw);
>> + }
>> +
>> + mutex_unlock(&provider->lock);
>> +
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * icc_set() - set constraints on an interconnect path between two endpoints
>> + * @path: reference to the path returned by icc_get()
>> + * @avg_bw: average bandwidth in kbps
>> + * @peak_bw: peak bandwidth in kbps
>> + *
>> + * This function is used by an interconnect consumer to express its own needs
>> + * in term of bandwidth and QoS for a previously requested path between two
>> + * endpoints. The requests are aggregated and each node is updated accordingly.
>> + *
>> + * Returns 0 on success, or an approproate error code otherwise.
>
> *appropriate*

Ok.

[..]
>> +/**
>> + * struct icc_node - entity that is part of the interconnect topology
>> + *
>> + * @id: platform specific node id
>> + * @name: node name used in debugfs
>> + * @links: a list of targets where we can go next when traversing
>> + * @num_links: number of links to other interconnect nodes
>> + * @provider: points to the interconnect provider of this node
>> + * @node_list: list of interconnect nodes associated with @provider
>> + * @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
>> + * @peak_bw: aggregated value of peak bandwidth
>
> Consider changing to "aggregated value of {average|peak} bandwidth
> requests from all consumers"

Ok, will clarify.

Thanks,
Georgi

2018-06-06 15:15:11

by Georgi Djakov

[permalink] [raw]
Subject: Re: [PATCH v4 5/7] interconnect: qcom: Add msm8916 interconnect provider driver

Hi Amit,

On 25.05.18 г. 11:27, Amit Kucheria wrote:
> On Fri, Mar 9, 2018 at 11:09 PM, Georgi Djakov <[email protected]> wrote:
>> Add driver for the Qualcomm interconnect buses found in msm8916 based
>> platforms.
>>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
>> drivers/interconnect/Kconfig | 5 +
>> drivers/interconnect/Makefile | 1 +
>> drivers/interconnect/qcom/Kconfig | 11 +
>> drivers/interconnect/qcom/Makefile | 2 +
>> drivers/interconnect/qcom/msm8916.c | 482 ++++++++++++++++++++++++++++++++++++
>> include/linux/interconnect/qcom.h | 350 ++++++++++++++++++++++++++
>> 6 files changed, 851 insertions(+)
>> create mode 100644 drivers/interconnect/qcom/Kconfig
>> create mode 100644 drivers/interconnect/qcom/msm8916.c
>> create mode 100644 include/linux/interconnect/qcom.h

[..]

>> +#define DEFINE_QNODE(_name, _id, _port, _buswidth, _ap_owned, \
>> + _mas_rpm_id, _slv_rpm_id, _qos_mode, \
>> + _numlinks, ...) \
>> + static struct qcom_icc_node _name = { \
>> + .id = _id, \
>> + .name = #_name, \
>> + .port = _port, \
>> + .buswidth = _buswidth, \
>> + .qos_mode = _qos_mode, \
>> + .ap_owned = _ap_owned, \
>> + .mas_rpm_id = _mas_rpm_id, \
>> + .slv_rpm_id = _slv_rpm_id, \
>> + .num_links = _numlinks, \
>> + .links = { __VA_ARGS__ }, \
>> + }
>
> Move this macro definition just above its use below.
>

Ok.

>> +enum qcom_qos_mode {
>> + QCOM_QOS_MODE_BYPASS = 0,
>> + QCOM_QOS_MODE_FIXED,
>> + QCOM_QOS_MODE_MAX,
>> +};
>> +
>> +struct qcom_icc_provider {
>> + struct icc_provider provider;
>> + void __iomem *base;
>> + struct clk *bus_clk;
>> + struct clk *bus_a_clk;
>> +};
>> +
>> +#define MSM8916_MAX_LINKS 8
>> +
>> +/**
>> + * struct qcom_icc_node - Qualcomm specific interconnect nodes
>> + * @name: the node name used in debugfs
>> + * @links: an array of nodes where we can go next while traversing
>> + * @id: a unique node identifier
>> + * @num_links: the total number of @links
>> + * @port: the offset index into the masters QoS register space
>> + * @buswidth: width of the interconnect between a node and the bus
>
> units?

Will add.

[..]

>> +static int qcom_icc_init(struct icc_node *node)
>> +{
>> + struct qcom_icc_provider *qp = to_qcom_provider(node->provider);
>> + /* TODO: init qos and priority */
>> +
>
> No need to set_rate here before enabling the clock?

Yes, i am planing to initially set the bus clocks at max rate. The rate
would be adjusted as soon as the consumers start placing requests.

>
>> + clk_prepare_enable(qp->bus_clk);
>> + clk_prepare_enable(qp->bus_a_clk);
>> +
>> + return 0;
>> +}
>> +
>> +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst,
>> + u32 avg, u32 peak)
>> +{
>> + struct qcom_icc_provider *qp;
>> + struct qcom_icc_node *qn;
>> + struct icc_node *node;
>> + struct icc_provider *provider;
>> + u64 avg_bw;
>> + u64 peak_bw;
>> + u64 rate = 0;
>> + int ret = 0;
>> +
>> + if (!src)
>> + node = dst;
>> + else
>> + node = src;
>> +
>> + qn = node->data;
>> + provider = node->provider;
>> + qp = to_qcom_provider(node->provider);
>> +
>> + /* convert from kbps to bps */
>> + avg_bw = avg * 1000ULL;
>> + peak_bw = peak * 1000ULL;
>> +
>
> Since the core uses kbps and various SoC HW might use bps (or other
> units), perhaps consider providing a macro in the core header such as:
>
> #define icc_units_to_bps(bw) (bw * 1000ULL)
>
> and then move this conversion to the top of the function where you
> define the variable
>
> u64 avg_bw = icc_units_to_bps(avg);
> u64 peak_bw = icc_units_to_bps(peak);
>
> Since other drivers will end up copying this driver, it might help
> prevent silly errors in the drivers and has a side-effect of allowing
> the core to change internal units w/o any driver changes down the
> line, if needed.

Ok, i am fine with this.

Thanks,
Georgi


2018-06-06 15:23:58

by Georgi Djakov

[permalink] [raw]
Subject: Re: [PATCH v4 2/7] dt-bindings: Introduce interconnect provider bindings

Hi Neil,

Apologies for the delayed response.

On 12.04.18 г. 16:15, Neil Armstrong wrote:
> On 09/03/2018 22:09, Georgi Djakov wrote:
>> This binding is intended to represent the interconnect hardware present
>> in some of the modern SoCs. Currently it consists only of a binding for
>> the interconnect hardware devices (provider).
>>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
>> .../bindings/interconnect/interconnect.txt | 47 ++++++++++++++++++++++
>> 1 file changed, 47 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/interconnect/interconnect.txt
>>
>> diff --git a/Documentation/devicetree/bindings/interconnect/interconnect.txt b/Documentation/devicetree/bindings/interconnect/interconnect.txt
>> new file mode 100644
>> index 000000000000..70612bb201e4
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/interconnect/interconnect.txt
>> @@ -0,0 +1,47 @@
>> +Interconnect Provider Device Tree Bindings
>> +=========================================
>> +
>> +The purpose of this document is to define a common set of generic interconnect
>> +providers/consumers properties.
>> +
>> +
>> += interconnect providers =
>> +
>> +The interconnect provider binding is intended to represent the interconnect
>> +controllers in the system. Each provider registers a set of interconnect
>> +nodes, which expose the interconnect related capabilities of the interconnect
>> +to consumer drivers. These capabilities can be throughput, latency, priority
>> +etc. The consumer drivers set constraints on interconnect path (or endpoints)
>> +depending on the usecase. Interconnect providers can also be interconnect
>> +consumers, such as in the case where two network-on-chip fabrics interface
>> +directly
>
> Hi,
>
> Can't we specify the number of cells for the phandle ? It should be aligned with other consumer/provider bindings.

Yes, that's the plan. Will align it!

Thanks,
Georgi

2018-06-06 20:08:02

by Georgi Djakov

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

Hi Evan,

On 06/06/2018 05:59 PM, Georgi Djakov wrote:
>>> +
>>> +/**
>>> + * icc_node_create() - create a node
>>> + * @id: node id
>>> + *
>>> + * Return: icc_node pointer on success, or ERR_PTR() on error
>>> + */
>>> +struct icc_node *icc_node_create(int id)
>>> +{
>>> + struct icc_node *node;
>>> +
>>> + /* check if node already exists */
>>> + node = node_find(id);
>>> + if (node)
>>> + return node;
>>
>> This is probably going to do more harm than good once icc_node_delete comes
>> in, since it almost certainly indicates a programmer error or ID collision,
>> and will likely result in a double free. We should probably fail with
>> EEXIST instead.
>
> In the current approach we create the nodes one by one, and the linked
> nodes are created when they are referenced. The other way around would
> be to create first all the nodes and then populate the links to avoid
> the "chicken and egg" problem.
>

Just to elaborate a bit more on that: We can't actually register all the
nodes in advance, as we might have multiple interconnect providers
probing in different order. Each provider may have nodes linking to
nodes belonging to other providers (not probed yet). That's why we
create the nodes on the first reference and then, when the actual
provider driver is probed, the rest of the node data is filled.

Thanks,
Georgi

2018-06-07 01:14:24

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

On Wed, Jun 6, 2018 at 11:09 AM Georgi Djakov <[email protected]> wrote:
>
> Hi Evan,
>
> On 06/06/2018 05:59 PM, Georgi Djakov wrote:
> >>> +
> >>> +/**
> >>> + * icc_node_create() - create a node
> >>> + * @id: node id
> >>> + *
> >>> + * Return: icc_node pointer on success, or ERR_PTR() on error
> >>> + */
> >>> +struct icc_node *icc_node_create(int id)
> >>> +{
> >>> + struct icc_node *node;
> >>> +
> >>> + /* check if node already exists */
> >>> + node = node_find(id);
> >>> + if (node)
> >>> + return node;
> >>
> >> This is probably going to do more harm than good once icc_node_delete comes
> >> in, since it almost certainly indicates a programmer error or ID collision,
> >> and will likely result in a double free. We should probably fail with
> >> EEXIST instead.
> >
> > In the current approach we create the nodes one by one, and the linked
> > nodes are created when they are referenced. The other way around would
> > be to create first all the nodes and then populate the links to avoid
> > the "chicken and egg" problem.
> >
>
> Just to elaborate a bit more on that: We can't actually register all the
> nodes in advance, as we might have multiple interconnect providers
> probing in different order. Each provider may have nodes linking to
> nodes belonging to other providers (not probed yet). That's why we
> create the nodes on the first reference and then, when the actual
> provider driver is probed, the rest of the node data is filled.
>

Ah ok, the extra explanation helped a lot. This makes sense to me. Thanks.
-Evan

2018-06-08 15:59:49

by Alexandre Bailon

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

On 03/09/2018 10:09 PM, Georgi Djakov wrote:
> This patch introduce a new API to get requirements and configure the
> interconnect buses across the entire chipset to fit with the current
> demand.
>
> The API is using a consumer/provider-based model, where the providers are
> the interconnect buses and the consumers could be various drivers.
> The consumers request interconnect resources (path) between endpoints and
> set the desired constraints on this data flow path. The providers receive
> requests from consumers and aggregate these requests for all master-slave
> pairs on that path. Then the providers configure each participating in the
> topology node according to the requested data flow path, physical links and
> constraints. The topology could be complicated and multi-tiered and is SoC
> specific.
>
> Signed-off-by: Georgi Djakov <[email protected]>
> ---
> Documentation/interconnect/interconnect.rst | 96 ++++++
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/interconnect/Kconfig | 10 +
> drivers/interconnect/Makefile | 1 +
> drivers/interconnect/core.c | 489 ++++++++++++++++++++++++++++
> include/linux/interconnect-provider.h | 109 +++++++
> include/linux/interconnect.h | 40 +++
> 8 files changed, 748 insertions(+)
> create mode 100644 Documentation/interconnect/interconnect.rst
> create mode 100644 drivers/interconnect/Kconfig
> create mode 100644 drivers/interconnect/Makefile
> create mode 100644 drivers/interconnect/core.c
> create mode 100644 include/linux/interconnect-provider.h
> create mode 100644 include/linux/interconnect.h
>
> diff --git a/Documentation/interconnect/interconnect.rst b/Documentation/interconnect/interconnect.rst
> new file mode 100644
> index 000000000000..23eba68e8424
> --- /dev/null
> +++ b/Documentation/interconnect/interconnect.rst
> @@ -0,0 +1,96 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +=====================================
> +GENERIC SYSTEM INTERCONNECT SUBSYSTEM
> +=====================================
> +
> +Introduction
> +------------
> +
> +This framework is designed to provide a standard kernel interface to control
> +the settings of the interconnects on a SoC. These settings can be throughput,
> +latency and priority between multiple interconnected devices or functional
> +blocks. This can be controlled dynamically in order to save power or provide
> +maximum performance.
> +
> +The interconnect bus is a hardware with configurable parameters, which can be
> +set on a data path according to the requests received from various drivers.
> +An example of interconnect buses are the interconnects between various
> +components or functional blocks in chipsets. There can be multiple interconnects
> +on a SoC that can be multi-tiered.
> +
> +Below is a simplified diagram of a real-world SoC interconnect bus topology.
> +
> +::
> +
> + +----------------+ +----------------+
> + | HW Accelerator |--->| M NoC |<---------------+
> + +----------------+ +----------------+ |
> + | | +------------+
> + +-----+ +-------------+ V +------+ | |
> + | DDR | | +--------+ | PCIe | | |
> + +-----+ | | Slaves | +------+ | |
> + ^ ^ | +--------+ | | C NoC |
> + | | V V | |
> + +------------------+ +------------------------+ | | +-----+
> + | |-->| |-->| |-->| CPU |
> + | |-->| |<--| | +-----+
> + | Mem NoC | | S NoC | +------------+
> + | |<--| |---------+ |
> + | |<--| |<------+ | | +--------+
> + +------------------+ +------------------------+ | | +-->| Slaves |
> + ^ ^ ^ ^ ^ | | +--------+
> + | | | | | | V
> + +------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
> + | CPUs | | | GPU | | DSP | | Masters |-->| P NoC |-->| Slaves |
> + +------+ | +-----+ +-----+ +---------+ +----------------+ +--------+
> + |
> + +-------+
> + | Modem |
> + +-------+
> +
> +Terminology
> +-----------
> +
> +Interconnect provider is the software definition of the interconnect hardware.
> +The interconnect providers on the above diagram are M NoC, S NoC, C NoC and Mem
> +NoC.
> +
> +Interconnect node is the software definition of the interconnect hardware
> +port. Each interconnect provider consists of multiple interconnect nodes,
> +which are connected to other SoC components including other interconnect
> +providers. The point on the diagram where the CPUs connects to the memory is
> +called an interconnect node, which belongs to the Mem NoC interconnect provider.
> +
> +Interconnect endpoints are the first or the last element of the path. Every
> +endpoint is a node, but not every node is an endpoint.
> +
> +Interconnect path is everything between two endpoints including all the nodes
> +that have to be traversed to reach from a source to destination node. It may
> +include multiple master-slave pairs across several interconnect providers.
> +
> +Interconnect consumers are the entities which make use of the data paths exposed
> +by the providers. The consumers send requests to providers requesting various
> +throughput, latency and priority. Usually the consumers are device drivers, that
> +send request based on their needs. An example for a consumer is a video decoder
> +that supports various formats and image sizes.
> +
> +Interconnect providers
> +----------------------
> +
> +Interconnect provider is an entity that implements methods to initialize and
> +configure a interconnect bus hardware. The interconnect provider drivers should
> +be registered with the interconnect provider core.
> +
> +The interconnect framework provider API functions are documented in
> +.. kernel-doc:: include/linux/interconnect-provider.h
> +
> +Interconnect consumers
> +----------------------
> +
> +Interconnect consumers are the clients which use the interconnect APIs to
> +get paths between endpoints and set their bandwidth/latency/QoS requirements
> +for these interconnect paths.
> +
> +The interconnect framework consumer API functions are documented in
> +.. kernel-doc:: include/linux/interconnect.h
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 879dc0604cba..96a1db022cee 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -219,4 +219,6 @@ source "drivers/siox/Kconfig"
>
> source "drivers/slimbus/Kconfig"
>
> +source "drivers/interconnect/Kconfig"
> +
> endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 24cd47014657..0cca95740d9b 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -185,3 +185,4 @@ obj-$(CONFIG_TEE) += tee/
> obj-$(CONFIG_MULTIPLEXER) += mux/
> obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/
> obj-$(CONFIG_SIOX) += siox/
> +obj-$(CONFIG_INTERCONNECT) += interconnect/
> diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
> new file mode 100644
> index 000000000000..a261c7d41deb
> --- /dev/null
> +++ b/drivers/interconnect/Kconfig
> @@ -0,0 +1,10 @@
> +menuconfig INTERCONNECT
> + tristate "On-Chip Interconnect management support"
> + help
> + Support for management of the on-chip interconnects.
> +
> + This framework is designed to provide a generic interface for
> + managing the interconnects in a SoC.
> +
> + If unsure, say no.
> +
> diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
> new file mode 100644
> index 000000000000..5edf0ae80818
> --- /dev/null
> +++ b/drivers/interconnect/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_INTERCONNECT) += core.o
> diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
> new file mode 100644
> index 000000000000..6306e258b9b9
> --- /dev/null
> +++ b/drivers/interconnect/core.c
> @@ -0,0 +1,489 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Interconnect framework core driver
> + *
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#include <linux/device.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/interconnect.h>
> +#include <linux/interconnect-provider.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_IDR(icc_idr);
> +static LIST_HEAD(icc_provider_list);
> +static DEFINE_MUTEX(icc_provider_list_mutex);
> +static DEFINE_MUTEX(icc_path_mutex);
> +
> +/**
> + * 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
> + * @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;
> + u32 avg_bw;
> + u32 peak_bw;
> +};
> +
> +/**
> + * struct icc_path - interconnect path structure
> + * @num_nodes: number of hops (nodes)
> + * @reqs: array of the requests applicable to this path of nodes
> + */
> +struct icc_path {
> + size_t num_nodes;
> + struct icc_req reqs[0];
> +};
> +
> +static struct icc_node *node_find(const int id)
> +{
> + struct icc_node *node;
> +
> + node = idr_find(&icc_idr, id);
> +
> + return node;
> +}
> +
> +static struct icc_path *path_allocate(struct icc_node *node, ssize_t num_nodes)
> +{
> + struct icc_path *path;
> + size_t i;
> +
> + path = kzalloc(sizeof(*path) + num_nodes * sizeof(*path->reqs),
> + GFP_KERNEL);
> + if (!path)
> + return ERR_PTR(-ENOMEM);
> +
> + path->num_nodes = num_nodes;
> +
> + for (i = 0; i < num_nodes; i++) {
> + hlist_add_head(&path->reqs[i].req_node, &node->req_list);
> +
> + path->reqs[i].node = node;
> + /* reference to previous node was saved during path traversal */
> + node = node->reverse;
> + }
> +
> + return path;
> +}
> +
> +static struct icc_path *path_find(struct icc_node *src, struct icc_node *dst)
> +{
> + struct icc_node *node = NULL;
> + struct list_head traverse_list;
> + struct list_head edge_list;
> + struct list_head tmp_list;
> + size_t i, number = 0;
> + bool found = false;
> +
> + INIT_LIST_HEAD(&traverse_list);
> + INIT_LIST_HEAD(&edge_list);
> + INIT_LIST_HEAD(&tmp_list);
> +
> + list_add_tail(&src->search_list, &traverse_list);
> +
> + do {
> + list_for_each_entry(node, &traverse_list, search_list) {
> + if (node == dst) {
> + found = true;
> + list_add(&node->search_list, &tmp_list);
> + break;
> + }
> + for (i = 0; i < node->num_links; i++) {
> + struct icc_node *tmp = node->links[i];
> +
> + if (!tmp)
> + return ERR_PTR(-ENOENT);
> +
> + if (tmp->is_traversed)
> + continue;
> +
> + tmp->is_traversed = true;
> + tmp->reverse = node;
> + list_add_tail(&tmp->search_list, &edge_list);
> + }
> + }
> + if (found)
> + break;
> +
> + list_splice_init(&traverse_list, &tmp_list);
> + list_splice_init(&edge_list, &traverse_list);
> +
> + /* count the number of nodes */
> + number++;
> +
> + } while (!list_empty(&traverse_list));
> +
> + /* reset the traversed state */
> + list_for_each_entry(node, &tmp_list, search_list)
> + node->is_traversed = false;
> +
> + if (found)
> + return path_allocate(dst, number);
> +
> + return ERR_PTR(-EPROBE_DEFER);
> +}
> +
> +static int path_init(struct icc_path *path)
> +{
> + struct icc_node *node;
> + size_t i;
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> +
> + mutex_lock(&node->provider->lock);
> + node->provider->users++;
> + mutex_unlock(&node->provider->lock);
> + }
> +
> + return 0;
> +}
> +
> +static void node_aggregate(struct icc_node *node)
> +{
> + struct icc_req *r;
> + u32 agg_avg = 0;
> + u32 agg_peak = 0;
> +
> + hlist_for_each_entry(r, &node->req_list, req_node) {
> + /* sum(averages) and max(peaks) */
> + agg_avg += r->avg_bw;
> + agg_peak = max(agg_peak, r->peak_bw);
> + }
> +
> + node->avg_bw = agg_avg;
> + node->peak_bw = agg_peak;
> +}
> +
> +static void provider_aggregate(struct icc_provider *provider, u32 *avg_bw,
> + u32 *peak_bw)
> +{
> + struct icc_node *n;
> + u32 agg_avg = 0;
> + u32 agg_peak = 0;
> +
> + /* aggregate for the interconnect provider */
> + list_for_each_entry(n, &provider->nodes, node_list) {
> + /* sum the average and max the peak */
> + agg_avg += n->avg_bw;
> + agg_peak = max(agg_peak, n->peak_bw);
> + }
> +
> + *avg_bw = agg_avg;
> + *peak_bw = agg_peak;
> +}
> +
> +static int constraints_apply(struct icc_path *path)
> +{
> + struct icc_node *next, *prev = NULL;
> + int i;
> +
> + for (i = 0; i < path->num_nodes; i++, prev = next) {
> + struct icc_provider *provider;
> + u32 avg_bw = 0;
> + u32 peak_bw = 0;
> + int ret;
> +
> + next = path->reqs[i].node;
> + /*
> + * Both endpoints should be valid master-slave pairs of the
> + * same interconnect provider that will be configured.
> + */
> + if (!next || !prev)
> + continue;
> +
> + if (next->provider != prev->provider)
> + continue;
> +
> + provider = next->provider;
> + mutex_lock(&provider->lock);
> +
> + /* aggregate requests for the provider */
> + provider_aggregate(provider, &avg_bw, &peak_bw);
> +
> + if (provider->set) {
> + /* set the constraints */
> + ret = provider->set(prev, next, avg_bw, peak_bw);
> + }
> +
> + mutex_unlock(&provider->lock);
> +
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * icc_set() - set constraints on an interconnect path between two endpoints
> + * @path: reference to the path returned by icc_get()
> + * @avg_bw: average bandwidth in kbps
> + * @peak_bw: peak bandwidth in kbps
> + *
> + * This function is used by an interconnect consumer to express its own needs
> + * in term of bandwidth and QoS for a previously requested path between two
> + * endpoints. The requests are aggregated and each node is updated accordingly.
> + *
> + * Returns 0 on success, or an approproate error code otherwise.
> + */
> +int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
> +{
> + struct icc_node *node;
> + size_t i;
> + int ret;
> +
> + if (!path)
> + return 0;
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> +
> + mutex_lock(&icc_path_mutex);
> +
> + /* update the consumer request for this path */
> + path->reqs[i].avg_bw = avg_bw;
> + path->reqs[i].peak_bw = peak_bw;
> +
> + /* aggregate requests for this node */
> + node_aggregate(node);
> +
> + mutex_unlock(&icc_path_mutex);
> + }
> +
> + ret = constraints_apply(path);
> + if (ret)
> + pr_err("interconnect: error applying constraints (%d)", ret);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(icc_set);
> +
> +/**
> + * icc_get() - return a handle for path between two endpoints
> + * @src_id: source device port id
> + * @dst_id: destination device port id
> + *
> + * 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 the they are not needed anymore.
> + *
> + * Return: icc_path pointer on success, or ERR_PTR() on error
> + */
> +struct icc_path *icc_get(const int src_id, const int dst_id)
> +{
> + struct icc_node *src, *dst;
> + struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
> +
> + src = node_find(src_id);
> + if (!src)
> + goto out;
> +
> + dst = node_find(dst_id);
> + if (!dst)
> + goto out;
> +
> + mutex_lock(&icc_path_mutex);
> + path = path_find(src, dst);
> + mutex_unlock(&icc_path_mutex);
> + if (IS_ERR(path))
> + goto out;
> +
> + path_init(path);
> +
> +out:
> + return path;
> +}
> +EXPORT_SYMBOL_GPL(icc_get);
> +
> +/**
> + * icc_put() - release the reference to the icc_path
> + * @path: 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.
> + */
> +void icc_put(struct icc_path *path)
> +{
> + struct icc_node *node;
> + size_t i;
> + int ret;
> +
> + if (!path || WARN_ON_ONCE(IS_ERR(path)))
> + return;
> +
> + ret = icc_set(path, 0, 0);
> + if (ret)
> + pr_err("%s: error (%d)\n", __func__, ret);
> +
> + for (i = 0; i < path->num_nodes; i++) {
> + node = path->reqs[i].node;
> + hlist_del(&path->reqs[i].req_node);
> +
> + mutex_lock(&node->provider->lock);
> + node->provider->users--;
> + mutex_unlock(&node->provider->lock);
> + }
> +
> + kfree(path);
> +}
> +EXPORT_SYMBOL_GPL(icc_put);
> +
> +/**
> + * icc_node_create() - create a node
> + * @id: node id
> + *
> + * Return: icc_node pointer on success, or ERR_PTR() on error
> + */
> +struct icc_node *icc_node_create(int id)
> +{
> + struct icc_node *node;
> +
> + /* check if node already exists */
> + node = node_find(id);
> + if (node)
> + return node;
> +
> + node = kzalloc(sizeof(*node), GFP_KERNEL);
> + if (!node)
> + return ERR_PTR(-ENOMEM);
> +
> + id = idr_alloc(&icc_idr, node, id, id + 1, GFP_KERNEL);
> + if (WARN(id < 0, "couldn't get idr"))
> + return ERR_PTR(id);
> +
> + node->id = id;
> +
> + return node;
> +}
> +EXPORT_SYMBOL_GPL(icc_node_create);
> +
> +/**
> + * icc_link_create() - create a link between two nodes
> + * @src_id: source node id
I guess src_id has become node and is not an id anymore,
so it should be updated.
> + * @dst_id: destination node id
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_link_create(struct icc_node *node, const int dst_id)
> +{
> + struct icc_node *dst;
> + struct icc_node **new;
> + int ret = 0;
> +
> + if (IS_ERR_OR_NULL(node))
> + return PTR_ERR(node);
> +
> + mutex_lock(&node->provider->lock);
> +
> + dst = node_find(dst_id);
> + if (!dst)
> + dst = icc_node_create(dst_id);
> +
> + new = krealloc(node->links,
> + (node->num_links + 1) * sizeof(*node->links),
> + GFP_KERNEL);
> + if (!new) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + node->links = new;
> + node->links[node->num_links++] = dst;
> +
> +out:
> + mutex_unlock(&node->provider->lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_link_create);
> +
> +/**
> + * icc_add_node() - add an interconnect node to interconnect provider
> + * @node: pointer to the interconnect node
> + * @provider: pointer to the interconnect provider
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider)
> +{
> + if (WARN_ON(!node))
> + return -EINVAL;
> +
> + if (WARN_ON(!provider))
> + return -EINVAL;
> +
> + node->provider = provider;
> +
> + mutex_lock(&provider->lock);
> + list_add_tail(&node->node_list, &provider->nodes);
> + mutex_unlock(&provider->lock);
> +
> + return 0;
> +}
> +
> +/**
> + * icc_add_provider() - add a new interconnect provider
> + * @icc_provider: the interconnect provider that will be added into topology
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_add_provider(struct icc_provider *provider)
> +{
> + if (WARN_ON(!provider))
> + return -EINVAL;
> +
> + if (WARN_ON(!provider->set))
> + return -EINVAL;
> +
> + mutex_init(&provider->lock);
> + INIT_LIST_HEAD(&provider->nodes);
> +
> + mutex_lock(&icc_provider_list_mutex);
> + list_add(&provider->provider_list, &icc_provider_list);
> + mutex_unlock(&icc_provider_list_mutex);
> +
> + dev_dbg(provider->dev, "interconnect provider added to topology\n");
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_add_provider);
> +
> +/**
> + * icc_del_provider() - delete previously added interconnect provider
> + * @icc_provider: the interconnect provider that will be removed from topology
> + *
> + * Return: 0 on success, or an error code otherwise
> + */
> +int icc_del_provider(struct icc_provider *provider)
> +{
> + mutex_lock(&provider->lock);
> + if (provider->users) {
> + pr_warn("interconnect provider still has %d users\n",
> + provider->users);
> + }
> + mutex_unlock(&provider->lock);
> +
> + mutex_lock(&icc_provider_list_mutex);
> + list_del(&provider->provider_list);
> + mutex_unlock(&icc_provider_list_mutex);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(icc_del_provider);
> +
> +MODULE_AUTHOR("Georgi Djakov <[email protected]");
> +MODULE_DESCRIPTION("Interconnect Driver Core");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h
> new file mode 100644
> index 000000000000..779b5b5b1306
> --- /dev/null
> +++ b/include/linux/interconnect-provider.h
> @@ -0,0 +1,109 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef _LINUX_INTERCONNECT_PROVIDER_H
> +#define _LINUX_INTERCONNECT_PROVIDER_H
> +
> +#include <linux/interconnect.h>
> +
> +struct icc_node;
> +
> +/**
> + * struct icc_provider - interconnect provider (controller) entity that might
> + * provide multiple interconnect controls
> + *
> + * @provider_list: list of the registered interconnect providers
> + * @nodes: internal list of the interconnect provider nodes
> + * @set: pointer to device specific set operation function
> + * @dev: the device this interconnect provider belongs to
> + * @lock: lock to provide consistency during aggregation/update of constraints
> + * @users: count of active users
> + * @data: pointer to private data
> + */
> +struct icc_provider {
> + struct list_head provider_list;
> + struct list_head nodes;
> + int (*set)(struct icc_node *src, struct icc_node *dst,
> + u32 avg_bw, u32 peak_bw);
> + struct device *dev;
> + struct mutex lock;
> + int users;
> + void *data;
> +};
> +
> +/**
> + * struct icc_node - entity that is part of the interconnect topology
> + *
> + * @id: platform specific node id
> + * @name: node name used in debugfs
> + * @links: a list of targets where we can go next when traversing
> + * @num_links: number of links to other interconnect nodes
> + * @provider: points to the interconnect provider of this node
> + * @node_list: list of interconnect nodes associated with @provider
> + * @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
> + * @peak_bw: aggregated value of peak bandwidth
> + * @data: pointer to private data
> + */
> +struct icc_node {
> + int id;
> + const char *name;
> + struct icc_node **links;
> + size_t num_links;
> +
> + struct icc_provider *provider;
> + struct list_head node_list;
> + struct list_head orphan_list;
> + struct list_head search_list;
> + struct icc_node *reverse;
> + bool is_traversed;
> + struct hlist_head req_list;
> + u32 avg_bw;
> + u32 peak_bw;
> + void *data;
> +};
> +
> +#if IS_ENABLED(CONFIG_INTERCONNECT)
> +
> +struct icc_node *icc_node_create(int id);
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider);
> +int icc_link_create(struct icc_node *node, const int dst_id);
> +int icc_add_provider(struct icc_provider *provider);
> +int icc_del_provider(struct icc_provider *provider);
> +
> +#else
> +
> +static inline struct icc_node *icc_node_create(int id)
> +{
> + return ERR_PTR(-ENOTSUPP);
> +}
> +
> +int icc_node_add(struct icc_node *node, struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int icc_link_create(struct icc_node *node, const int dst_id)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int icc_add_provider(struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline int icc_del_provider(struct icc_provider *provider)
> +{
> + return -ENOTSUPP;
> +}
> +
> +#endif /* CONFIG_INTERCONNECT */
> +
> +#endif /* _LINUX_INTERCONNECT_PROVIDER_H */
> diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h
> new file mode 100644
> index 000000000000..5a7cf72b76a5
> --- /dev/null
> +++ b/include/linux/interconnect.h
> @@ -0,0 +1,40 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, Linaro Ltd.
> + * Author: Georgi Djakov <[email protected]>
> + */
> +
> +#ifndef _LINUX_INTERCONNECT_H
> +#define _LINUX_INTERCONNECT_H
> +
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +
> +struct icc_path;
> +struct device;
> +
> +#if IS_ENABLED(CONFIG_INTERCONNECT)
> +
> +struct icc_path *icc_get(const int src_id, const int dst_id);
> +void icc_put(struct icc_path *path);
> +int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw);
> +
> +#else
> +
> +static inline struct icc_path *icc_get(const int src_id, const int dst_id)
> +{
> + return NULL;
> +}
> +
> +static inline void icc_put(struct icc_path *path)
> +{
> +}
> +
> +static inline int icc_set(struct icc_path *path, u32 avg_bw, u32 peak_bw)
> +{
> + return 0;
> +}
> +
> +#endif /* CONFIG_INTERCONNECT */
> +
> +#endif /* _LINUX_INTERCONNECT_H */
>


2018-06-09 19:16:49

by Georgi Djakov

[permalink] [raw]
Subject: [PATCH v4 1/7] interconnect: Add generic on-chip interconnect API

Hi Alexandre,

On 8.06.18 г. 18:57, Alexandre Bailon wrote:
> On 03/09/2018 10:09 PM, Georgi Djakov wrote:
>> This patch introduce a new API to get requirements and configure the
>> interconnect buses across the entire chipset to fit with the current
>> demand.
>>
>> The API is using a consumer/provider-based model, where the providers are
>> the interconnect buses and the consumers could be various drivers.
>> The consumers request interconnect resources (path) between endpoints and
>> set the desired constraints on this data flow path. The providers receive
>> requests from consumers and aggregate these requests for all master-slave
>> pairs on that path. Then the providers configure each participating in the
>> topology node according to the requested data flow path, physical links and
>> constraints. The topology could be complicated and multi-tiered and is SoC
>> specific.
>>
>> Signed-off-by: Georgi Djakov <[email protected]>
>> ---
>> Documentation/interconnect/interconnect.rst | 96 ++++++
>> drivers/Kconfig | 2 +
>> drivers/Makefile | 1 +
>> drivers/interconnect/Kconfig | 10 +
>> drivers/interconnect/Makefile | 1 +
>> drivers/interconnect/core.c | 489 ++++++++++++++++++++++++++++
>> include/linux/interconnect-provider.h | 109 +++++++
>> include/linux/interconnect.h | 40 +++
>> 8 files changed, 748 insertions(+)
>> create mode 100644 Documentation/interconnect/interconnect.rst
>> create mode 100644 drivers/interconnect/Kconfig
>> create mode 100644 drivers/interconnect/Makefile
>> create mode 100644 drivers/interconnect/core.c
>> create mode 100644 include/linux/interconnect-provider.h
>> create mode 100644 include/linux/interconnect.h
>>

[..]

>> +
>> +/**
>> + * icc_link_create() - create a link between two nodes
>> + * @src_id: source node id
> I guess src_id has become node and is not an id anymore,
> so it should be updated.

Yes, absolutely!

>> + * @dst_id: destination node id
>> + *
>> + * Return: 0 on success, or an error code otherwise
>> + */
>> +int icc_link_create(struct icc_node *node, const int dst_id)
>> +{

Thanks,
Georgi