Commit 10d3e38c authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'icc-5.6-rc1' of https://git.linaro.org/people/georgi.djakov/linux into char-misc-next



Georgi writes:

interconnect patches for 5.6

Here are the interconnect patches for the 5.6-rc1 merge window.

- New core helper functions for some common functionalities in drivers.
- Improvements in the information exposed via debugfs.
- Basic tracepoints support.
- New interconnect driver for msm8916 platforms.
- Misc fixes.

Signed-off-by: default avatarGeorgi Djakov <georgi.djakov@linaro.org>

* tag 'icc-5.6-rc1' of https://git.linaro.org/people/georgi.djakov/linux:
  interconnect: qcom: Add MSM8916 interconnect provider driver
  dt-bindings: interconnect: Add Qualcomm MSM8916 DT bindings
  interconnect: Check for valid path in icc_set_bw()
  interconnect: Print the tag in the debugfs summary
  interconnect: Add interconnect_graph file to debugfs
  interconnect: qcom: Use the standard aggregate function
  interconnect: Add a common standard aggregate function
  interconnect: Add basic tracepoints
  interconnect: Add a name to struct icc_path
  interconnect: Move internal structs into a separate file
  interconnect: qcom: Use the new common helper for node removal
  interconnect: Add a common helper for removing all nodes
parents 3634a4a8 30c8fa3e
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interconnect/qcom,msm8916.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Qualcomm MSM8916 Network-On-Chip interconnect

maintainers:
  - Georgi Djakov <georgi.djakov@linaro.org>

description: |
   The Qualcomm MSM8916 interconnect providers support adjusting the
   bandwidth requirements between the various NoC fabrics.

properties:
  compatible:
    enum:
      - qcom,msm8916-bimc
      - qcom,msm8916-pcnoc
      - qcom,msm8916-snoc

  reg:
    maxItems: 1

  '#interconnect-cells':
    const: 1

  clock-names:
    items:
      - const: bus
      - const: bus_a

  clocks:
    items:
      - description: Bus Clock
      - description: Bus A Clock

required:
  - compatible
  - reg
  - '#interconnect-cells'
  - clock-names
  - clocks

additionalProperties: false

examples:
  - |
      #include <dt-bindings/clock/qcom,rpmcc.h>

      bimc: interconnect@400000 {
              compatible = "qcom,msm8916-bimc";
              reg = <0x00400000 0x62000>;
              #interconnect-cells = <1>;
              clock-names = "bus", "bus_a";
              clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
                       <&rpmcc RPM_SMD_BIMC_A_CLK>;
      };

      pcnoc: interconnect@500000 {
              compatible = "qcom,msm8916-pcnoc";
              reg = <0x00500000 0x11000>;
              #interconnect-cells = <1>;
              clock-names = "bus", "bus_a";
              clocks = <&rpmcc RPM_SMD_PCNOC_CLK>,
                       <&rpmcc RPM_SMD_PCNOC_A_CLK>;
      };

      snoc: interconnect@580000 {
              compatible = "qcom,msm8916-snoc";
              reg = <0x00580000 0x14000>;
              #interconnect-cells = <1>;
              clock-names = "bus", "bus_a";
              clocks = <&rpmcc RPM_SMD_SNOC_CLK>,
                       <&rpmcc RPM_SMD_SNOC_A_CLK>;
      };
+22 −0
Original line number Diff line number Diff line
@@ -91,3 +91,25 @@ 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.  These interfaces are not currently
documented.

Interconnect debugfs interfaces
-------------------------------

Like several other subsystems interconnect will create some files for debugging
and introspection. Files in debugfs are not considered ABI so application
software shouldn't rely on format details change between kernel versions.

``/sys/kernel/debug/interconnect/interconnect_summary``:

Show all interconnect nodes in the system with their aggregated bandwidth
request. Indented under each node show bandwidth requests from each device.

``/sys/kernel/debug/interconnect/interconnect_graph``:

Show the interconnect graph in the graphviz dot format. It shows all
interconnect nodes and links in the system and groups together nodes from the
same provider as subgraphs. The format is human-readable and can also be piped
through dot to generate diagrams in many graphical formats::

        $ cat /sys/kernel/debug/interconnect/interconnect_graph | \
                dot -Tsvg > interconnect_graph.svg
+1 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0

CFLAGS_core.o				:= -I$(src)
icc-core-objs				:= core.o

obj-$(CONFIG_INTERCONNECT)		+= icc-core.o
+131 −37
Original line number Diff line number Diff line
@@ -19,45 +19,22 @@
#include <linux/of.h>
#include <linux/overflow.h>

#include "internal.h"

#define CREATE_TRACE_POINTS
#include "trace.h"

static DEFINE_IDR(icc_idr);
static LIST_HEAD(icc_providers);
static DEFINE_MUTEX(icc_lock);
static struct dentry *icc_debugfs_dir;

/**
 * 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
 * @dev: reference to the device that sets the constraints
 * @tag: path tag (optional)
 * @avg_bw: an integer describing the average bandwidth in kBps
 * @peak_bw: an integer describing the peak bandwidth in kBps
 */
struct icc_req {
	struct hlist_node req_node;
	struct icc_node *node;
	struct device *dev;
	u32 tag;
	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[];
};

static void icc_summary_show_one(struct seq_file *s, struct icc_node *n)
{
	if (!n)
		return;

	seq_printf(s, "%-30s %12u %12u\n",
	seq_printf(s, "%-42s %12u %12u\n",
		   n->name, n->avg_bw, n->peak_bw);
}

@@ -65,8 +42,8 @@ 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");
	seq_puts(s, " node                                  tag          avg         peak\n");
	seq_puts(s, "--------------------------------------------------------------------\n");

	mutex_lock(&icc_lock);

@@ -81,8 +58,8 @@ static int icc_summary_show(struct seq_file *s, void *data)
				if (!r->dev)
					continue;

				seq_printf(s, "    %-26s %12u %12u\n",
					   dev_name(r->dev), r->avg_bw,
				seq_printf(s, "  %-27s %12u %12u %12u\n",
					   dev_name(r->dev), r->tag, r->avg_bw,
					   r->peak_bw);
			}
		}
@@ -94,6 +71,70 @@ static int icc_summary_show(struct seq_file *s, void *data)
}
DEFINE_SHOW_ATTRIBUTE(icc_summary);

static void icc_graph_show_link(struct seq_file *s, int level,
				struct icc_node *n, struct icc_node *m)
{
	seq_printf(s, "%s\"%d:%s\" -> \"%d:%s\"\n",
		   level == 2 ? "\t\t" : "\t",
		   n->id, n->name, m->id, m->name);
}

static void icc_graph_show_node(struct seq_file *s, struct icc_node *n)
{
	seq_printf(s, "\t\t\"%d:%s\" [label=\"%d:%s",
		   n->id, n->name, n->id, n->name);
	seq_printf(s, "\n\t\t\t|avg_bw=%ukBps", n->avg_bw);
	seq_printf(s, "\n\t\t\t|peak_bw=%ukBps", n->peak_bw);
	seq_puts(s, "\"]\n");
}

static int icc_graph_show(struct seq_file *s, void *data)
{
	struct icc_provider *provider;
	struct icc_node *n;
	int cluster_index = 0;
	int i;

	seq_puts(s, "digraph {\n\trankdir = LR\n\tnode [shape = record]\n");
	mutex_lock(&icc_lock);

	/* draw providers as cluster subgraphs */
	cluster_index = 0;
	list_for_each_entry(provider, &icc_providers, provider_list) {
		seq_printf(s, "\tsubgraph cluster_%d {\n", ++cluster_index);
		if (provider->dev)
			seq_printf(s, "\t\tlabel = \"%s\"\n",
				   dev_name(provider->dev));

		/* draw nodes */
		list_for_each_entry(n, &provider->nodes, node_list)
			icc_graph_show_node(s, n);

		/* draw internal links */
		list_for_each_entry(n, &provider->nodes, node_list)
			for (i = 0; i < n->num_links; ++i)
				if (n->provider == n->links[i]->provider)
					icc_graph_show_link(s, 2, n,
							    n->links[i]);

		seq_puts(s, "\t}\n");
	}

	/* draw external links */
	list_for_each_entry(provider, &icc_providers, provider_list)
		list_for_each_entry(n, &provider->nodes, node_list)
			for (i = 0; i < n->num_links; ++i)
				if (n->provider != n->links[i]->provider)
					icc_graph_show_link(s, 1, n,
							    n->links[i]);

	mutex_unlock(&icc_lock);
	seq_puts(s, "}");

	return 0;
}
DEFINE_SHOW_ATTRIBUTE(icc_graph);

static struct icc_node *node_find(const int id)
{
	return idr_find(&icc_idr, id);
@@ -244,6 +285,16 @@ out:
	return ret;
}

int icc_std_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
		      u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
	*agg_avg += avg_bw;
	*agg_peak = max(*agg_peak, peak_bw);

	return 0;
}
EXPORT_SYMBOL_GPL(icc_std_aggregate);

/* of_icc_xlate_onecell() - Translate function using a single index.
 * @spec: OF phandle args to map into an interconnect node.
 * @data: private data (pointer to struct icc_onecell_data)
@@ -382,9 +433,17 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)

	mutex_lock(&icc_lock);
	path = path_find(dev, src_node, dst_node);
	if (IS_ERR(path))
		dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
	mutex_unlock(&icc_lock);
	if (IS_ERR(path)) {
		dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
		return path;
	}

	if (name)
		path->name = kstrdup_const(name, GFP_KERNEL);
	else
		path->name = kasprintf(GFP_KERNEL, "%s-%s",
				       src_node->name, dst_node->name);

	return path;
}
@@ -436,9 +495,12 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
	size_t i;
	int ret;

	if (!path || !path->num_nodes)
	if (!path)
		return 0;

	if (WARN_ON(IS_ERR(path) || !path->num_nodes))
		return -EINVAL;

	mutex_lock(&icc_lock);

	old_avg = path->reqs[0].avg_bw;
@@ -453,6 +515,8 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)

		/* aggregate requests for this node */
		aggregate_requests(node);

		trace_icc_set_bw(path, node, i, avg_bw, peak_bw);
	}

	ret = apply_constraints(path);
@@ -471,6 +535,8 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)

	mutex_unlock(&icc_lock);

	trace_icc_set_bw_end(path, ret);

	return ret;
}
EXPORT_SYMBOL_GPL(icc_set_bw);
@@ -507,9 +573,12 @@ struct icc_path *icc_get(struct device *dev, const int src_id, const int dst_id)
		goto out;

	path = path_find(dev, src, dst);
	if (IS_ERR(path))
	if (IS_ERR(path)) {
		dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
		goto out;
	}

	path->name = kasprintf(GFP_KERNEL, "%s-%s", src->name, dst->name);
out:
	mutex_unlock(&icc_lock);
	return path;
@@ -545,6 +614,7 @@ void icc_put(struct icc_path *path)
	}
	mutex_unlock(&icc_lock);

	kfree_const(path->name);
	kfree(path);
}
EXPORT_SYMBOL_GPL(icc_put);
@@ -742,6 +812,28 @@ void icc_node_del(struct icc_node *node)
}
EXPORT_SYMBOL_GPL(icc_node_del);

/**
 * icc_nodes_remove() - remove all previously added nodes from provider
 * @provider: the interconnect provider we are removing nodes from
 *
 * Return: 0 on success, or an error code otherwise
 */
int icc_nodes_remove(struct icc_provider *provider)
{
	struct icc_node *n, *tmp;

	if (WARN_ON(IS_ERR_OR_NULL(provider)))
		return -EINVAL;

	list_for_each_entry_safe_reverse(n, tmp, &provider->nodes, node_list) {
		icc_node_del(n);
		icc_node_destroy(n->id);
	}

	return 0;
}
EXPORT_SYMBOL_GPL(icc_nodes_remove);

/**
 * icc_provider_add() - add a new interconnect provider
 * @provider: the interconnect provider that will be added into topology
@@ -802,6 +894,8 @@ static int __init icc_init(void)
	icc_debugfs_dir = debugfs_create_dir("interconnect", NULL);
	debugfs_create_file("interconnect_summary", 0444,
			    icc_debugfs_dir, NULL, &icc_summary_fops);
	debugfs_create_file("interconnect_graph", 0444,
			    icc_debugfs_dir, NULL, &icc_graph_fops);
	return 0;
}

+42 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Interconnect framework internal structs
 *
 * Copyright (c) 2019, Linaro Ltd.
 * Author: Georgi Djakov <georgi.djakov@linaro.org>
 */

#ifndef __DRIVERS_INTERCONNECT_INTERNAL_H
#define __DRIVERS_INTERCONNECT_INTERNAL_H

/**
 * 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
 * @dev: reference to the device that sets the constraints
 * @tag: path tag (optional)
 * @avg_bw: an integer describing the average bandwidth in kBps
 * @peak_bw: an integer describing the peak bandwidth in kBps
 */
struct icc_req {
	struct hlist_node req_node;
	struct icc_node *node;
	struct device *dev;
	u32 tag;
	u32 avg_bw;
	u32 peak_bw;
};

/**
 * struct icc_path - interconnect path structure
 * @name: a string name of the path (useful for ftrace)
 * @num_nodes: number of hops (nodes)
 * @reqs: array of the requests applicable to this path of nodes
 */
struct icc_path {
	const char *name;
	size_t num_nodes;
	struct icc_req reqs[];
};

#endif
Loading