From patchwork Mon May 15 15:35:25 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Georgi Djakov X-Patchwork-Id: 99819 Delivered-To: patch@linaro.org Received: by 10.182.142.97 with SMTP id rv1csp1484120obb; Mon, 15 May 2017 08:36:42 -0700 (PDT) X-Received: by 10.99.123.94 with SMTP id k30mr6839190pgn.91.1494862602871; Mon, 15 May 2017 08:36:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1494862602; cv=none; d=google.com; s=arc-20160816; b=N4SY9E+ogOnvPPgGT4wrOew5VvhxQH1R52jhshgbJFoa9T83oSO7qYW5WV9/HinOIR dZyMUkoAg/F/GNq56iB00fiwnpshrSppZ/fSKoGAJcGRf3KuvgiGZbHJa5hE1IkIhFm+ zOD9ag3MLigoDNnTMVAm391vScBHiltSLp7iXeDCKJGCJ8k+GqlxP9RymwN4kUMuFOE4 RS9AlbNHYkpusdiUcHCDZMn3K0KolcsPLBzZjrNyjOfebVZYeBBtVKIX9lii8syS+nU5 F4kD6fwRCXPn/NYFliJPX7K7Xcj2yzCbv3I8983Ea2ZTIknWAet3cqh+wyd8j59JAFKD ZD8Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=mAHvhiM/8RwJiPonlu8LBCJuPgW/zV+iSSPjn+IW030=; b=JSaTzvxTyK52JwaoAIY5ZjParx7YKpLQbAl6BgkbND5jipCiBKyy5jC6dq5/1/pBWZ NpPfawdKqN/Q+h0s3DQroExlyhQh0adIhPoO4rdpDtSVslmNYDZF+udPmNGN5dzJqa/V /Nkwu5Umr6rHlILIM4OYygeB74bYn5zCtQndrXS0Pd3wExvsd18cQgnkK6AZCwx0mDdp osRr2lQFVuKuxHiScP/DBIQnbt56uoDtowDS0aJNS8+s3kqDMO2QrppA9vpGRTWg4hwf o+r6v5q7GSQ4vcI2sPM/i0W8j07/295+L2atGOmxFLvJITqmRlmJj28K8tgLpmA18XLK vlgw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id d189si10884960pfg.73.2017.05.15.08.36.42; Mon, 15 May 2017 08:36:42 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S966031AbdEOPgY (ORCPT + 25 others); Mon, 15 May 2017 11:36:24 -0400 Received: from mail-wm0-f53.google.com ([74.125.82.53]:37085 "EHLO mail-wm0-f53.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934689AbdEOPfd (ORCPT ); Mon, 15 May 2017 11:35:33 -0400 Received: by mail-wm0-f53.google.com with SMTP id d127so80778430wmf.0 for ; Mon, 15 May 2017 08:35:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=mAHvhiM/8RwJiPonlu8LBCJuPgW/zV+iSSPjn+IW030=; b=A4dHiwecyvpws3bzD7yoDnaSTeDu5hHKK1t9yDkvFucQMfR9AFaqLGMwPuIzmwKrNf lnVEG/R8pcIn/RD+lz2cLz7NwyYWPq4MzJMT6DB5spsMtS/qb9sz9RAMzIiiCn/0zKzu aopSE+ko8CJtBLCr4PyOH/17jbmrPayFZFSj0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=mAHvhiM/8RwJiPonlu8LBCJuPgW/zV+iSSPjn+IW030=; b=Rf5TfuY40wAwLRmJLozeMXinTJfepFYHIME0ZXEUGFlzoLzExMxLTNdkbj2xbyNokz qqJS3xyKnOj829gK/2ir2XwzA3ulCAs/pDRazYF25wqqSOxvqj40dqvXYkbkREsfIRrs Dng0YKG6YBJ8j33H745Ho2FVz0RDf0nDe9ukUIIJ0/o1WLhLZFyNoZSQ5Du4UXibrsF6 SqF6Bh/YOSffL15EuLJjj13HwUW4yQAGH8kAJGl7aQDgVovNrJhHWuhYsTXIlmT5gZsw fPiv62zSYk5Gmeacd14r7RmGGvi5KbgC5HZrh3groQ4Ps9mL+deCbKX++cvBXoh58KQ9 OP/Q== X-Gm-Message-State: AODbwcDqb93L2iDT6Q42ws074gaVcnfNDIvi3nif8+c2+ED/iiPQuYTv LJzstmLVpZJwPIk0 X-Received: by 10.28.10.6 with SMTP id 6mr3966809wmk.5.1494862531840; Mon, 15 May 2017 08:35:31 -0700 (PDT) Received: from mms-0441.qualcomm.mm-sol.com ([212.45.67.2]) by smtp.googlemail.com with ESMTPSA id l7sm14548023wrc.52.2017.05.15.08.35.29 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 15 May 2017 08:35:30 -0700 (PDT) From: Georgi Djakov To: linux-pm@vger.kernel.org, rjw@rjwysocki.net Cc: robh+dt@kernel.org, khilman@baylibre.com, mturquette@baylibre.com, gregkh@linuxfoundation.org, vincent.guittot@linaro.org, skannan@codeaurora.org, sboyd@codeaurora.org, andy.gross@linaro.org, seansw@qti.qualcomm.com, davidai@quicinc.com, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, georgi.djakov@linaro.org Subject: [RFC v1 1/3] interconnect: Add generic interconnect controller API Date: Mon, 15 May 2017 18:35:25 +0300 Message-Id: <20170515153527.27649-2-georgi.djakov@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170515153527.27649-1-georgi.djakov@linaro.org> References: <20170515153527.27649-1-georgi.djakov@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch 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 using a consumer/provider-based model, where the providers are the interconnect controllers 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. Signed-off-by: Georgi Djakov --- Documentation/interconnect/interconnect.txt | 65 ++++++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/interconnect/Kconfig | 10 + drivers/interconnect/Makefile | 1 + drivers/interconnect/interconnect.c | 317 ++++++++++++++++++++++++++++ include/linux/interconnect-consumer.h | 84 ++++++++ include/linux/interconnect-provider.h | 110 ++++++++++ 8 files changed, 590 insertions(+) create mode 100644 Documentation/interconnect/interconnect.txt create mode 100644 drivers/interconnect/Kconfig create mode 100644 drivers/interconnect/Makefile create mode 100644 drivers/interconnect/interconnect.c create mode 100644 include/linux/interconnect-consumer.h create mode 100644 include/linux/interconnect-provider.h diff --git a/Documentation/interconnect/interconnect.txt b/Documentation/interconnect/interconnect.txt new file mode 100644 index 000000000000..f761a2fb553c --- /dev/null +++ b/Documentation/interconnect/interconnect.txt @@ -0,0 +1,65 @@ +GENERIC SYSTEM INTERCONNECT CONTROLLER SUBSYSTEM +=============================================== + +1. 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. This can be +controlled dynamically in order to save power or provide maximum performance. + +The interconnect controller 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 controllers are the interconnects between +various components on chipsets. There can be multiple interconnects on a SoC +that can be multi-tiered. + +Below is a simplified diagram of a real-world SoC topology. The interconnect +providers are the memory front end and the NoCs. + ++----------------+ +----------------+ +| HW Accelerator |--->| M NoC |<---------------+ ++----------------+ +----------------+ | + | | +------------+ + +-------------+ V +------+ | | + | +--------+ | PCIe | | | + | | Slaves | +------+ | | + | +--------+ | | C NoC | + V V | | ++------------------+ +------------------------+ | | +-----+ +| |-->| |-->| |-->| CPU | +| |-->| |<--| | +-----+ +| Memory | | S NoC | +------------+ +| |<--| |---------+ | +| |<--| |<------+ | | +--------+ ++------------------+ +------------------------+ | | +-->| Slaves | + ^ ^ ^ ^ | | +--------+ + | | | | | V ++-----+ | +-----+ +-----+ +---------+ +----------------+ +--------+ +| CPU | | | GPU | | DSP | | Masters |-->| P NoC |-->| Slaves | ++-----+ | +-----+ +-----+ +---------+ +----------------+ +--------+ + | + +-------+ + | Modem | + +-------+ + +2. Interconnect providers +------------------------ +Interconnect provider is an entity that implements methods to initialize and +configure a interconnect controller hardware. + +An interconnect controller should register with the interconnect provider core +with interconnect_add_provider(). + +A previously registered interconnect provider is unregistered with +interconnect_del_provider(). + +3. Interconnect consumers +------------------------ +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. + +The interconnect framework consumer API functions are documented in +include/linux/interconnect-consumer.h diff --git a/drivers/Kconfig b/drivers/Kconfig index d2ac339de85f..6e4d80e98f5c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -198,4 +198,6 @@ source "drivers/hwtracing/intel_th/Kconfig" source "drivers/fpga/Kconfig" +source "drivers/interconnect/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 795d0ca714bf..d5b4733f3875 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -172,3 +172,4 @@ obj-$(CONFIG_STM) += hwtracing/stm/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_NVMEM) += nvmem/ obj-$(CONFIG_FPGA) += fpga/ +obj-$(CONFIG_INTERCONNECT) += interconnect/ diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig new file mode 100644 index 000000000000..1e50e951cdc1 --- /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..d9da6a6c3560 --- /dev/null +++ b/drivers/interconnect/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_INTERCONNECT) += interconnect.o diff --git a/drivers/interconnect/interconnect.c b/drivers/interconnect/interconnect.c new file mode 100644 index 000000000000..633ee157226f --- /dev/null +++ b/drivers/interconnect/interconnect.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2017, Linaro Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(interconnect_provider_list_mutex); +static LIST_HEAD(interconnect_provider_list); + +/** + * struct interconnect_path - interconnect path structure + * + * @nodes: array of the nodes in this path + * @num_nodes: number of hops (nodes) + */ +struct interconnect_path { + struct interconnect_node **nodes; + size_t num_nodes; +}; + +static struct interconnect_node *node_find(const char *dev_id, int con_id) +{ + struct icp *icp; + struct interconnect_node *node = ERR_PTR(-EPROBE_DEFER); + int match, best = 0; + + mutex_lock(&interconnect_provider_list_mutex); + + list_for_each_entry(icp, &interconnect_provider_list, icp_list) { + struct interconnect_node *n; + + match = 0; + + list_for_each_entry(n, &icp->nodes, icn_list) { + if (n->dev_id) { + if (!dev_id || strncmp(n->dev_id, dev_id, + strlen(dev_id))) + continue; + match += 2; + } + if (n->con_id) { + if (!con_id || n->con_id != con_id) + continue; + match += 1; + } + + if (match > best) { + node = n; + if (match == 3) + goto out; + + best = match; + } + } + } + +out: + mutex_unlock(&interconnect_provider_list_mutex); + + return node; +} + +static struct interconnect_path *path_find(struct interconnect_node *src, + struct interconnect_node *dst) +{ + struct list_head edge_list; + struct list_head traverse_list; + struct list_head tmp_list; + struct interconnect_path *path = ERR_PTR(-EPROBE_DEFER); + struct interconnect_node *node = NULL; + size_t i, number = 1; + 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 interconnect_node *tmp = node->links[i]; + + if (!tmp) { + WARN_ON(1); + 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) { + path = kzalloc(sizeof(*path), GFP_KERNEL); + if (!path) + return ERR_PTR(-ENOMEM); + + path->nodes = kcalloc(number, sizeof(*node), GFP_KERNEL); + if (!path->nodes) { + kfree(path); + return ERR_PTR(-ENOMEM); + } + + path->num_nodes = number; + + /* start from the destination and go back to source */ + node = dst; + + for (i = 0; i < number; i++) { + path->nodes[i] = node; + node = node->reverse; + } + } + + return path; +} + +int interconnect_set(struct interconnect_path *path, u32 bandwidth) +{ + struct interconnect_node *next, *prev = NULL; + size_t i; + + for (i = 0; i < path->num_nodes; i++) { + struct icn_qos *req; + u32 agg_bw = 0; + + next = path->nodes[i]; + + /* aggregate requests */ + hlist_for_each_entry(req, &next->qos_list, node) { + if (req->path == path) { + /* update the bandwidth for the path */ + req->bandwidth = bandwidth; + } + + agg_bw += req->bandwidth; + } + + if (next->icp->ops->set) + next->icp->ops->set(prev, next, agg_bw); + + prev = next; + + /* is this the last node? */ + if (i == path->num_nodes - 1) { + if (next->icp->ops->set) + next->icp->ops->set(next, NULL, agg_bw); + } + } + + return 0; +} + +struct interconnect_path *interconnect_get(const char *sdev, const int sid, + const char *ddev, const int did) +{ + struct interconnect_node *src, *dst, *node; + struct interconnect_path *path; + size_t i; + + src = node_find(sdev, sid); + if (IS_ERR(src)) + return ERR_CAST(src); + + dst = node_find(ddev, did); + if (IS_ERR(dst)) + return ERR_CAST(dst); + + /* TODO: cache the path */ + path = path_find(src, dst); + if (IS_ERR(path)) { + pr_err("error finding path between %p and %p (%ld)\n", + src, dst, PTR_ERR(path)); + return path; + } + + for (i = 0; i < path->num_nodes; i++) { + struct icn_qos *req; + + node = path->nodes[i]; + + pr_debug("%s: i=%lu node=%p\n", __func__, i, node); + + /* + * Create icn_qos for each separate link between the nodes. + * They may have different constraints and may belong to + * different interconnect providers. + */ + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return ERR_PTR(-ENOMEM); + + req->path = path; + req->bandwidth = 0; + + hlist_add_head(&req->node, &node->qos_list); + + node->icp->users++; + } + + return path; +} +EXPORT_SYMBOL_GPL(interconnect_get); + +void interconnect_put(struct interconnect_path *path) +{ + struct interconnect_node *node; + struct icn_qos *req; + struct hlist_node *tmp; + size_t i; + int ret; + + if (IS_ERR(path)) + return; + + for (i = 0; i < path->num_nodes; i++) { + node = path->nodes[i]; + + hlist_for_each_entry_safe(req, tmp, &node->qos_list, node) { + if (req->path == path) { + /* + * Remove the constraints from the path, + * update the nodes and free the memory + */ + ret = interconnect_set(path, 0); + if (ret) + pr_err("%s error %d\n", __func__, ret); + + hlist_del(&req->node); + kfree(req); + } + } + + node->icp->users--; + } + + kfree(path); +} +EXPORT_SYMBOL_GPL(interconnect_put); + +int interconnect_add_provider(struct icp *icp) +{ + struct interconnect_node *node; + + WARN(!icp->ops->set, "%s: .set is not implemented\n", __func__); + + mutex_lock(&interconnect_provider_list_mutex); + list_add(&icp->icp_list, &interconnect_provider_list); + mutex_unlock(&interconnect_provider_list_mutex); + + list_for_each_entry(node, &icp->nodes, icn_list) { + INIT_HLIST_HEAD(&node->qos_list); + } + + dev_info(icp->dev, "interconnect provider is added to topology\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(interconnect_add_provider); + +int interconnect_del_provider(struct icp *icp) +{ + if (icp->users) + return -EBUSY; + + mutex_lock(&interconnect_provider_list_mutex); + list_del(&icp->icp_list); + mutex_unlock(&interconnect_provider_list_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(interconnect_del_provider); + +MODULE_AUTHOR("Georgi Djakov + +/** + * struct icp_ops - platform specific callback operations for interconnect + * providers that will be called from drivers + * + * @set: set constraints on interconnect + */ +struct icp_ops { + int (*set)(struct interconnect_node *src, struct interconnect_node *dst, u32 bandwidth); +}; + +/** + * struct icp - interconnect provider (controller) entity that might + * provide multiple interconnect controls + * + * @icp_list: list of the registered interconnect providers + * @nodes: internal list of the interconnect provider nodes + * @ops: pointer to device specific struct icp_ops + * @dev: the device this interconnect provider belongs to + * @users: count of active users + * @data: pointer to private data + */ +struct icp { + struct list_head icp_list; + struct list_head nodes; + const struct icp_ops *ops; + struct device *dev; + int users; + void *data; +}; + +/** + * struct interconnect_node - entity that is part of the interconnect topology + * + * @links: links to other interconnect nodes + * @num_links: number of links to other interconnect nodes + * @icp: points to the interconnect provider of this node + * @icn_list: list of interconnect nodes + * @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 + * @qos_list: a list of QoS constraints + * @dev_id: device id + * @con_id: connection id + */ +struct interconnect_node { + struct interconnect_node **links; + size_t num_links; + + struct icp *icp; + struct list_head icn_list; + struct list_head search_list; + struct interconnect_node *reverse; + bool is_traversed; + struct hlist_head qos_list; + + const char *dev_id; + int con_id; +}; + +/** + * struct icn_qos - constraints that are attached to each node + * + * @node: linked list node + * @path: the interconnect path which is using this constraint + * @bandwidth: an integer describing the bandwidth in kbps + */ +struct icn_qos { + struct hlist_node node; + struct interconnect_path *path; + u32 bandwidth; +}; + +#if IS_ENABLED(CONFIG_INTERCONNECT) + +int interconnect_add_provider(struct icp *icp); +int interconnect_del_provider(struct icp *icp); + +#else + +static inline int interconnect_add_provider(struct icp *icp) +{ + return -ENOSYS; +} + +static inline int interconnect_del_provider(struct icp *icp) +{ + return -ENOSYS; +} + +#endif /* CONFIG_INTERCONNECT */ + +#endif /* _LINUX_INTERCONNECT_PROVIDER_H */