summaryrefslogtreecommitdiff
path: root/drivers/interconnect
diff options
context:
space:
mode:
authorGeorgi Djakov <georgi.djakov@linaro.org>2020-08-25 20:01:51 +0300
committerGeorgi Djakov <georgi.djakov@linaro.org>2020-09-18 08:56:52 +0300
commitb1d681d8d32499bcf284462d92aeb5f9fe72bf5b (patch)
tree8c82999b36d2c99a0dc601556bcc2b48ebc9f0fd /drivers/interconnect
parentcc80d10d6fcf6f47a2a3efe2c4139e50906a4764 (diff)
interconnect: Add sync state support
The bootloaders often do some initial configuration of the interconnects in the system and we want to keep this configuration until all consumers have probed and expressed their bandwidth needs. This is because we don't want to change the configuration by starting to disable unused paths until every user had a chance to request the amount of bandwidth it needs. To accomplish this we will implement an interconnect specific sync_state callback which will synchronize (aggregate and set) the current bandwidth settings when all consumers have been probed. Link: https://lore.kernel.org/r/20200825170152.6434-3-georgi.djakov@linaro.org Reviewed-by: Saravana Kannan <saravanak@google.com> Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
Diffstat (limited to 'drivers/interconnect')
-rw-r--r--drivers/interconnect/core.c67
1 files changed, 67 insertions, 0 deletions
diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index cf07491b7415..b94c6c2b8ff9 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -26,6 +26,8 @@
static DEFINE_IDR(icc_idr);
static LIST_HEAD(icc_providers);
+static int providers_count;
+static bool synced_state;
static DEFINE_MUTEX(icc_lock);
static struct dentry *icc_debugfs_dir;
@@ -267,6 +269,12 @@ static int aggregate_requests(struct icc_node *node)
}
p->aggregate(node, r->tag, avg_bw, peak_bw,
&node->avg_bw, &node->peak_bw);
+
+ /* during boot use the initial bandwidth as a floor value */
+ if (!synced_state) {
+ node->avg_bw = max(node->avg_bw, node->init_avg);
+ node->peak_bw = max(node->peak_bw, node->init_peak);
+ }
}
return 0;
@@ -931,6 +939,19 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider)
node->provider = provider;
list_add_tail(&node->node_list, &provider->nodes);
+ /* get the initial bandwidth values and sync them with hardware */
+ if (provider->get_bw) {
+ provider->get_bw(node, &node->init_avg, &node->init_peak);
+ } else {
+ node->init_avg = INT_MAX;
+ node->init_peak = INT_MAX;
+ }
+ node->avg_bw = node->init_avg;
+ node->peak_bw = node->init_peak;
+ provider->set(node, node);
+ node->avg_bw = 0;
+ node->peak_bw = 0;
+
mutex_unlock(&icc_lock);
}
EXPORT_SYMBOL_GPL(icc_node_add);
@@ -1026,8 +1047,54 @@ int icc_provider_del(struct icc_provider *provider)
}
EXPORT_SYMBOL_GPL(icc_provider_del);
+static int of_count_icc_providers(struct device_node *np)
+{
+ struct device_node *child;
+ int count = 0;
+
+ for_each_available_child_of_node(np, child) {
+ if (of_property_read_bool(child, "#interconnect-cells"))
+ count++;
+ count += of_count_icc_providers(child);
+ }
+ of_node_put(np);
+
+ return count;
+}
+
+void icc_sync_state(struct device *dev)
+{
+ struct icc_provider *p;
+ struct icc_node *n;
+ static int count;
+
+ count++;
+
+ if (count < providers_count)
+ return;
+
+ mutex_lock(&icc_lock);
+ synced_state = true;
+ list_for_each_entry(p, &icc_providers, provider_list) {
+ dev_dbg(p->dev, "interconnect provider is in synced state\n");
+ list_for_each_entry(n, &p->nodes, node_list) {
+ if (n->init_avg || n->init_peak) {
+ aggregate_requests(n);
+ p->set(n, n);
+ }
+ }
+ }
+ mutex_unlock(&icc_lock);
+}
+EXPORT_SYMBOL_GPL(icc_sync_state);
+
static int __init icc_init(void)
{
+ struct device_node *root = of_find_node_by_path("/");
+
+ providers_count = of_count_icc_providers(root);
+ of_node_put(root);
+
icc_debugfs_dir = debugfs_create_dir("interconnect", NULL);
debugfs_create_file("interconnect_summary", 0444,
icc_debugfs_dir, NULL, &icc_summary_fops);