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: 9727339 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B829D6028A for ; Mon, 15 May 2017 15:36:14 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B024B28971 for ; Mon, 15 May 2017 15:36:14 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A4AAE28992; Mon, 15 May 2017 15:36:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 8E6542898D for ; Mon, 15 May 2017 15:36:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=KjT+vToCsIuukww2t3e/J+ZS2xPqjMB+CpGzYbhM5uI=; b=UKoeZO7RryIzWI2nQP0EGVsUbN XKHCaCtCFyVhdciqTTkr37eI1EDEyWNcVWSdlz17bLzg921AvEBH7KD7Yo+5qKlzCdTwShQMVcG0Y 1VoTRNSgmfndlclXH02TEP+Of4G6jaXgAgIBzlbR5UDS8dWCwExoYXEUx9ScUv/1SaBLurtlljGpN Ti7MsXELgVgnhrwGlCdC9Cy5U/ZqW8aUDf9JpGsyxGBTACfBbz+iidgEX/IQNggfD/Y3xtVu+IXza kY3XBG+kx4ZXq0fs4kkoQTIfn2dmmgLqpGBSjuR5OCBMzPO+yjW8LF3c4gPUuvV+5d3pCeFQCVrQG TNtqZPuQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dAI2a-0002oE-1t; Mon, 15 May 2017 15:36:08 +0000 Received: from mail-wm0-x22b.google.com ([2a00:1450:400c:c09::22b]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1dAI2M-0002XW-4L for linux-arm-kernel@lists.infradead.org; Mon, 15 May 2017 15:35:58 +0000 Received: by mail-wm0-x22b.google.com with SMTP id u65so26937137wmu.1 for ; Mon, 15 May 2017 08:35:33 -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=ItxKTSEUxXprxxVCFct1kfZGYdM7FxjverBURJPUghmCEcbOZsZwOPEuu5byg68ize PmtG1jJSIX+G6Q+NGzniWMVL314ht4BGxFeQ9Kdg5GsfUGf45L5a7dVD0sLSb6cHctAj IAs7l3ZEfhqvHWHV2eriIH9056/SEffS4BsOngvFAuN5v4ufamlOrJa9xq29OcqGLFOg tEl7CSOEJQ3V/DBJKPtx2RgFo+V1hGfE5vFQelQ5jm0ErfkF/g1Evkz91h1EAAUumO50 7HYQaY0HFKb8s9A/TCzmfnldljJf0jiDBbo8Hf/ES6kW0edisb4Oz3kVr+ZlrjrgciUB P9Sg== X-Gm-Message-State: AODbwcAF8TXr0/qT/2vtqU6KcRwBniPdjx19kVldTgd+f/XexEUn3sFz BuqrssLS5Xn+lFKt 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 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> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170515_083554_520469_53265BE8 X-CRM114-Status: GOOD ( 25.91 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, vincent.guittot@linaro.org, seansw@qti.qualcomm.com, khilman@baylibre.com, mturquette@baylibre.com, sboyd@codeaurora.org, linux-kernel@vger.kernel.org, davidai@quicinc.com, robh+dt@kernel.org, skannan@codeaurora.org, gregkh@linuxfoundation.org, andy.gross@linaro.org, georgi.djakov@linaro.org, linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP 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 */