From 3cce2c6fa70c768e516bff011d77db72e2f38a15 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Mon, 2 Dec 2019 18:21:32 +0200 Subject: [PATCH 01/12] interconnect: Add a common helper for removing all nodes The removal of all nodes from a provider seem to be a common functionality for all existing users and it would make sense to factor out this into a a common helper function. Suggested-by: Dmitry Osipenko Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 22 ++++++++++++++++++++++ include/linux/interconnect-provider.h | 6 ++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index c498796adc07..1b811423020a 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -742,6 +742,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 diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index b16f9effa555..31440c921216 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -98,6 +98,7 @@ int icc_link_create(struct icc_node *node, const int dst_id); int icc_link_destroy(struct icc_node *src, struct icc_node *dst); void icc_node_add(struct icc_node *node, struct icc_provider *provider); void icc_node_del(struct icc_node *node); +int icc_nodes_remove(struct icc_provider *provider); int icc_provider_add(struct icc_provider *provider); int icc_provider_del(struct icc_provider *provider); @@ -130,6 +131,11 @@ void icc_node_del(struct icc_node *node) { } +static inline int icc_nodes_remove(struct icc_provider *provider) +{ + return -ENOTSUPP; +} + static inline int icc_provider_add(struct icc_provider *provider) { return -ENOTSUPP; From ad3703ac24e7d6d2c5ca2ce7abeb1fd0b0afc01e Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Mon, 2 Dec 2019 18:21:33 +0200 Subject: [PATCH 02/12] interconnect: qcom: Use the new common helper for node removal There is a new helper function for removing all nodes. Let's use it instead of duplicating the code. Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8974.c | 17 ++++------------- drivers/interconnect/qcom/qcs404.c | 17 ++++------------- drivers/interconnect/qcom/sdm845.c | 16 +++------------- 3 files changed, 11 insertions(+), 39 deletions(-) diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c index bf8bd1aee358..e669a1f726d2 100644 --- a/drivers/interconnect/qcom/msm8974.c +++ b/drivers/interconnect/qcom/msm8974.c @@ -652,7 +652,7 @@ static int msm8974_icc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct icc_onecell_data *data; struct icc_provider *provider; - struct icc_node *node, *tmp; + struct icc_node *node; size_t num_nodes, i; int ret; @@ -732,10 +732,7 @@ static int msm8974_icc_probe(struct platform_device *pdev) return 0; err_del_icc: - list_for_each_entry_safe(node, tmp, &provider->nodes, node_list) { - icc_node_del(node); - icc_node_destroy(node->id); - } + icc_nodes_remove(provider); icc_provider_del(provider); err_disable_clks: @@ -747,16 +744,10 @@ static int msm8974_icc_probe(struct platform_device *pdev) static int msm8974_icc_remove(struct platform_device *pdev) { struct msm8974_icc_provider *qp = platform_get_drvdata(pdev); - struct icc_provider *provider = &qp->provider; - struct icc_node *n, *tmp; - list_for_each_entry_safe(n, tmp, &provider->nodes, node_list) { - icc_node_del(n); - icc_node_destroy(n->id); - } + icc_nodes_remove(&qp->provider); clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - - return icc_provider_del(provider); + return icc_provider_del(&qp->provider); } static const struct of_device_id msm8974_noc_of_match[] = { diff --git a/drivers/interconnect/qcom/qcs404.c b/drivers/interconnect/qcom/qcs404.c index 8e0735a87040..c8eb1276cce8 100644 --- a/drivers/interconnect/qcom/qcs404.c +++ b/drivers/interconnect/qcom/qcs404.c @@ -414,7 +414,7 @@ static int qnoc_probe(struct platform_device *pdev) struct icc_provider *provider; struct qcom_icc_node **qnodes; struct qcom_icc_provider *qp; - struct icc_node *node, *tmp; + struct icc_node *node; size_t num_nodes, i; int ret; @@ -494,10 +494,7 @@ static int qnoc_probe(struct platform_device *pdev) return 0; err: - list_for_each_entry_safe(node, tmp, &provider->nodes, node_list) { - icc_node_del(node); - icc_node_destroy(node->id); - } + icc_nodes_remove(provider); clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); icc_provider_del(provider); @@ -507,16 +504,10 @@ static int qnoc_probe(struct platform_device *pdev) static int qnoc_remove(struct platform_device *pdev) { struct qcom_icc_provider *qp = platform_get_drvdata(pdev); - struct icc_provider *provider = &qp->provider; - struct icc_node *n, *tmp; - list_for_each_entry_safe(n, tmp, &provider->nodes, node_list) { - icc_node_del(n); - icc_node_destroy(n->id); - } + icc_nodes_remove(&qp->provider); clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - - return icc_provider_del(provider); + return icc_provider_del(&qp->provider); } static const struct of_device_id qcs404_noc_of_match[] = { diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index 387267ee9648..f078cf0fce56 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c @@ -855,11 +855,7 @@ static int qnoc_probe(struct platform_device *pdev) return ret; err: - list_for_each_entry(node, &provider->nodes, node_list) { - icc_node_del(node); - icc_node_destroy(node->id); - } - + icc_nodes_remove(provider); icc_provider_del(provider); return ret; } @@ -867,15 +863,9 @@ static int qnoc_probe(struct platform_device *pdev) static int qnoc_remove(struct platform_device *pdev) { struct qcom_icc_provider *qp = platform_get_drvdata(pdev); - struct icc_provider *provider = &qp->provider; - struct icc_node *n, *tmp; - list_for_each_entry_safe(n, tmp, &provider->nodes, node_list) { - icc_node_del(n); - icc_node_destroy(n->id); - } - - return icc_provider_del(provider); + icc_nodes_remove(&qp->provider); + return icc_provider_del(&qp->provider); } static const struct of_device_id qnoc_of_match[] = { From dd018a9cf9108f9c7d924f6fe09aed745e78a67e Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 28 Nov 2019 16:18:16 +0200 Subject: [PATCH 03/12] interconnect: Move internal structs into a separate file Move the interconnect framework internal structs into a separate file, so that it can be included and used by ftrace code. This will allow us to expose some more useful information in the traces. Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 30 ++----------------------- drivers/interconnect/internal.h | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 28 deletions(-) create mode 100644 drivers/interconnect/internal.h diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 1b811423020a..f30a326dc7ce 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -19,39 +19,13 @@ #include #include +#include "internal.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) diff --git a/drivers/interconnect/internal.h b/drivers/interconnect/internal.h new file mode 100644 index 000000000000..5853e8faf223 --- /dev/null +++ b/drivers/interconnect/internal.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Interconnect framework internal structs + * + * Copyright (c) 2019, Linaro Ltd. + * Author: Georgi Djakov + */ + +#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 + * @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[]; +}; + +#endif From 05309830e1f869f939e283576dd3684313390062 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 28 Nov 2019 16:18:17 +0200 Subject: [PATCH 04/12] interconnect: Add a name to struct icc_path When debugging interconnect things, it turned out that saving the path name and including it in the traces is quite useful, especially for devices with multiple paths. For the path name we use the one specified in DT, or if we use platform data, the name is based on the source and destination node names. Suggested-by: Bjorn Andersson Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 18 +++++++++++++++--- drivers/interconnect/internal.h | 2 ++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index f30a326dc7ce..4f9bdb7f9165 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -356,9 +356,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; } @@ -481,9 +489,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; @@ -519,6 +530,7 @@ void icc_put(struct icc_path *path) } mutex_unlock(&icc_lock); + kfree_const(path->name); kfree(path); } EXPORT_SYMBOL_GPL(icc_put); diff --git a/drivers/interconnect/internal.h b/drivers/interconnect/internal.h index 5853e8faf223..bf18cb7239df 100644 --- a/drivers/interconnect/internal.h +++ b/drivers/interconnect/internal.h @@ -29,10 +29,12 @@ struct icc_req { /** * 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[]; }; From c46ab9db64979b0875fff79e1b00013343ca8286 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 28 Nov 2019 16:18:18 +0200 Subject: [PATCH 05/12] interconnect: Add basic tracepoints The tracepoints can help with understanding the system behavior of a given interconnect path when the consumer drivers change their bandwidth demands. This might be interesting when we want to monitor the requested interconnect bandwidth for each client driver. The paths may share the same nodes and this will help to understand "who and when is requesting what". All this is useful for subsystem drivers developers and may also provide hints when optimizing the power and performance profile of the system. Reviewed-by: Steven Rostedt (VMware) Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov --- drivers/interconnect/Makefile | 1 + drivers/interconnect/core.c | 7 +++ drivers/interconnect/trace.h | 88 +++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 drivers/interconnect/trace.h diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile index 28f2ab0824d5..725029ae7a2c 100644 --- a/drivers/interconnect/Makefile +++ b/drivers/interconnect/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +CFLAGS_core.o := -I$(src) icc-core-objs := core.o obj-$(CONFIG_INTERCONNECT) += icc-core.o diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 4f9bdb7f9165..fbec2e4fdfeb 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -21,6 +21,9 @@ #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); @@ -435,6 +438,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); @@ -453,6 +458,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); diff --git a/drivers/interconnect/trace.h b/drivers/interconnect/trace.h new file mode 100644 index 000000000000..3d668ff566bf --- /dev/null +++ b/drivers/interconnect/trace.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Interconnect framework tracepoints + * Copyright (c) 2019, Linaro Ltd. + * Author: Georgi Djakov + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM interconnect + +#if !defined(_TRACE_INTERCONNECT_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_INTERCONNECT_H + +#include +#include + +TRACE_EVENT(icc_set_bw, + + TP_PROTO(struct icc_path *p, struct icc_node *n, int i, + u32 avg_bw, u32 peak_bw), + + TP_ARGS(p, n, i, avg_bw, peak_bw), + + TP_STRUCT__entry( + __string(path_name, p->name) + __string(dev, dev_name(p->reqs[i].dev)) + __string(node_name, n->name) + __field(u32, avg_bw) + __field(u32, peak_bw) + __field(u32, node_avg_bw) + __field(u32, node_peak_bw) + ), + + TP_fast_assign( + __assign_str(path_name, p->name); + __assign_str(dev, dev_name(p->reqs[i].dev)); + __assign_str(node_name, n->name); + __entry->avg_bw = avg_bw; + __entry->peak_bw = peak_bw; + __entry->node_avg_bw = n->avg_bw; + __entry->node_peak_bw = n->peak_bw; + ), + + TP_printk("path=%s dev=%s node=%s avg_bw=%u peak_bw=%u agg_avg=%u agg_peak=%u", + __get_str(path_name), + __get_str(dev), + __get_str(node_name), + __entry->avg_bw, + __entry->peak_bw, + __entry->node_avg_bw, + __entry->node_peak_bw) +); + +TRACE_EVENT(icc_set_bw_end, + + TP_PROTO(struct icc_path *p, int ret), + + TP_ARGS(p, ret), + + TP_STRUCT__entry( + __string(path_name, p->name) + __string(dev, dev_name(p->reqs[0].dev)) + __field(int, ret) + ), + + TP_fast_assign( + __assign_str(path_name, p->name); + __assign_str(dev, dev_name(p->reqs[0].dev)); + __entry->ret = ret; + ), + + TP_printk("path=%s dev=%s ret=%d", + __get_str(path_name), + __get_str(dev), + __entry->ret) +); + +#endif /* _TRACE_INTERCONNECT_H */ + +/* This part must be outside protection */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include From 3172e4d276315afa82c12b14c8dd0db526c7aff1 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 28 Nov 2019 15:48:38 +0200 Subject: [PATCH 06/12] interconnect: Add a common standard aggregate function Currently there is one very standard aggregation method that is used by several drivers. Let's add this as a common function, so that drivers could just point to it, instead of copy/pasting code. Suggested-by: Evan Green Reviewed-by: Brian Masney Reviewed-by: Bjorn Andersson Reviewed-by: Evan Green Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 10 ++++++++++ include/linux/interconnect-provider.h | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index fbec2e4fdfeb..03625406c69f 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -221,6 +221,16 @@ static int apply_constraints(struct icc_path *path) 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) diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index 31440c921216..0c494534b4d3 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -92,6 +92,8 @@ struct icc_node { #if IS_ENABLED(CONFIG_INTERCONNECT) +int icc_std_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, + u32 peak_bw, u32 *agg_avg, u32 *agg_peak); struct icc_node *icc_node_create(int id); void icc_node_destroy(int id); int icc_link_create(struct icc_node *node, const int dst_id); @@ -104,6 +106,12 @@ int icc_provider_del(struct icc_provider *provider); #else +static inline int icc_std_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, + u32 peak_bw, u32 *agg_avg, u32 *agg_peak) +{ + return -ENOTSUPP; +} + static inline struct icc_node *icc_node_create(int id) { return ERR_PTR(-ENOTSUPP); From b92c35e1b9c9211635df9e8fb060c69311871865 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 28 Nov 2019 15:48:39 +0200 Subject: [PATCH 07/12] interconnect: qcom: Use the standard aggregate function Now we have a common function for standard aggregation, so let's use it, instead of duplicating the code. Reviewed-by: Brian Masney Reviewed-by: Bjorn Andersson Reviewed-by: Evan Green Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8974.c | 15 +++------------ drivers/interconnect/qcom/qcs404.c | 15 +++------------ 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c index e669a1f726d2..3a313e11e73d 100644 --- a/drivers/interconnect/qcom/msm8974.c +++ b/drivers/interconnect/qcom/msm8974.c @@ -550,15 +550,6 @@ static struct msm8974_icc_desc msm8974_snoc = { .num_nodes = ARRAY_SIZE(msm8974_snoc_nodes), }; -static int msm8974_icc_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; -} - static void msm8974_icc_rpm_smd_send(struct device *dev, int rsc_type, char *name, int id, u64 val) { @@ -603,8 +594,8 @@ static int msm8974_icc_set(struct icc_node *src, struct icc_node *dst) qp = to_msm8974_icc_provider(provider); list_for_each_entry(n, &provider->nodes, node_list) - msm8974_icc_aggregate(n, 0, n->avg_bw, n->peak_bw, - &agg_avg, &agg_peak); + provider->aggregate(n, 0, n->avg_bw, n->peak_bw, + &agg_avg, &agg_peak); sum_bw = icc_units_to_bps(agg_avg); max_peak_bw = icc_units_to_bps(agg_peak); @@ -694,7 +685,7 @@ static int msm8974_icc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&provider->nodes); provider->dev = dev; provider->set = msm8974_icc_set; - provider->aggregate = msm8974_icc_aggregate; + provider->aggregate = icc_std_aggregate; provider->xlate = of_icc_xlate_onecell; provider->data = data; diff --git a/drivers/interconnect/qcom/qcs404.c b/drivers/interconnect/qcom/qcs404.c index c8eb1276cce8..d4769a5ea182 100644 --- a/drivers/interconnect/qcom/qcs404.c +++ b/drivers/interconnect/qcom/qcs404.c @@ -327,15 +327,6 @@ static struct qcom_icc_desc qcs404_snoc = { .num_nodes = ARRAY_SIZE(qcs404_snoc_nodes), }; -static int qcom_icc_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; -} - static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) { struct qcom_icc_provider *qp; @@ -354,8 +345,8 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) qp = to_qcom_provider(provider); list_for_each_entry(n, &provider->nodes, node_list) - qcom_icc_aggregate(n, 0, n->avg_bw, n->peak_bw, - &agg_avg, &agg_peak); + provider->aggregate(n, 0, n->avg_bw, n->peak_bw, + &agg_avg, &agg_peak); sum_bw = icc_units_to_bps(agg_avg); max_peak_bw = icc_units_to_bps(agg_peak); @@ -456,7 +447,7 @@ static int qnoc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&provider->nodes); provider->dev = dev; provider->set = qcom_icc_set; - provider->aggregate = qcom_icc_aggregate; + provider->aggregate = icc_std_aggregate; provider->xlate = of_icc_xlate_onecell; provider->data = data; From 1a0013c62b33158dcb67a3c11872a03be50711a3 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 19 Nov 2019 00:34:01 +0200 Subject: [PATCH 08/12] interconnect: Add interconnect_graph file to debugfs The interconnect graphs can be difficult to understand and the current "interconnect_summary" file doesn't even display links in any way. Add a new "interconnect_graph" file to debugfs in the graphviz "dot" format which describes interconnect providers, nodes and links. The file is human-readable and can be visualized by piping through graphviz. Example: ssh $TARGET cat /sys/kernel/debug/interconnect/interconnect_graph \ | dot -Tsvg > interconnect_graph.svg Signed-off-by: Leonard Crestez Reviewed-by: Greg Kroah-Hartman Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov --- Documentation/driver-api/interconnect.rst | 22 ++++++++ drivers/interconnect/core.c | 66 +++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/Documentation/driver-api/interconnect.rst b/Documentation/driver-api/interconnect.rst index cdeb5825f314..5ed4f57a6bac 100644 --- a/Documentation/driver-api/interconnect.rst +++ b/Documentation/driver-api/interconnect.rst @@ -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 diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 03625406c69f..63c164264b73 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -71,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); @@ -827,6 +891,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; } From 2c5127a7fa0369801e623e8c6e58193f86d31d73 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Fri, 20 Dec 2019 18:38:46 +0200 Subject: [PATCH 09/12] interconnect: Print the tag in the debugfs summary Now we can have a tag associated with the path. Add this information to the interconnect_summary file, as the current information in debugfs is incomplete. Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 63c164264b73..10dde5df9251 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -34,7 +34,7 @@ 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); } @@ -42,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); @@ -58,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); } } From 7d7899c5297be8cd6095bcddecbbedccb34dd161 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Mon, 6 Jan 2020 19:27:46 +0200 Subject: [PATCH 10/12] interconnect: Check for valid path in icc_set_bw() Use IS_ERR() to ensure that the path passed to icc_set_bw() is valid. Reviewed-by: Evan Green Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 10dde5df9251..f277e467156f 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -495,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; From ebb37bd06460fff8d6f54f7731f593b7b52143ca Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 15 Aug 2019 13:06:12 +0300 Subject: [PATCH 11/12] dt-bindings: interconnect: Add Qualcomm MSM8916 DT bindings The Qualcomm MSM8916 platform has several bus fabrics that could be controlled and tuned dynamically according to the bandwidth demand. Reviewed-by: Rob Herring Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,msm8916.yaml | 77 ++++++++++++++ .../dt-bindings/interconnect/qcom,msm8916.h | 100 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,msm8916.yaml create mode 100644 include/dt-bindings/interconnect/qcom,msm8916.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8916.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8916.yaml new file mode 100644 index 000000000000..4107e60cab12 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8916.yaml @@ -0,0 +1,77 @@ +# 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 + +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 + + 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>; + }; diff --git a/include/dt-bindings/interconnect/qcom,msm8916.h b/include/dt-bindings/interconnect/qcom,msm8916.h new file mode 100644 index 000000000000..359a75feb198 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8916.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Qualcomm interconnect IDs + * + * Copyright (c) 2019, Linaro Ltd. + * Author: Georgi Djakov + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8916_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8916_H + +#define BIMC_SNOC_SLV 0 +#define MASTER_JPEG 1 +#define MASTER_MDP_PORT0 2 +#define MASTER_QDSS_BAM 3 +#define MASTER_QDSS_ETR 4 +#define MASTER_SNOC_CFG 5 +#define MASTER_VFE 6 +#define MASTER_VIDEO_P0 7 +#define SNOC_MM_INT_0 8 +#define SNOC_MM_INT_1 9 +#define SNOC_MM_INT_2 10 +#define SNOC_MM_INT_BIMC 11 +#define PCNOC_SNOC_SLV 12 +#define SLAVE_APSS 13 +#define SLAVE_CATS_128 14 +#define SLAVE_OCMEM_64 15 +#define SLAVE_IMEM 16 +#define SLAVE_QDSS_STM 17 +#define SLAVE_SRVC_SNOC 18 +#define SNOC_BIMC_0_MAS 19 +#define SNOC_BIMC_1_MAS 20 +#define SNOC_INT_0 21 +#define SNOC_INT_1 22 +#define SNOC_INT_BIMC 23 +#define SNOC_PCNOC_MAS 24 +#define SNOC_QDSS_INT 25 + +#define BIMC_SNOC_MAS 0 +#define MASTER_AMPSS_M0 1 +#define MASTER_GRAPHICS_3D 2 +#define MASTER_TCU0 3 +#define MASTER_TCU1 4 +#define SLAVE_AMPSS_L2 5 +#define SLAVE_EBI_CH0 6 +#define SNOC_BIMC_0_SLV 7 +#define SNOC_BIMC_1_SLV 8 + +#define MASTER_BLSP_1 0 +#define MASTER_DEHR 1 +#define MASTER_LPASS 2 +#define MASTER_CRYPTO_CORE0 3 +#define MASTER_SDCC_1 4 +#define MASTER_SDCC_2 5 +#define MASTER_SPDM 6 +#define MASTER_USB_HS 7 +#define PCNOC_INT_0 8 +#define PCNOC_INT_1 9 +#define PCNOC_MAS_0 10 +#define PCNOC_MAS_1 11 +#define PCNOC_SLV_0 12 +#define PCNOC_SLV_1 13 +#define PCNOC_SLV_2 14 +#define PCNOC_SLV_3 15 +#define PCNOC_SLV_4 16 +#define PCNOC_SLV_8 17 +#define PCNOC_SLV_9 18 +#define PCNOC_SNOC_MAS 19 +#define SLAVE_BIMC_CFG 20 +#define SLAVE_BLSP_1 21 +#define SLAVE_BOOT_ROM 22 +#define SLAVE_CAMERA_CFG 23 +#define SLAVE_CLK_CTL 24 +#define SLAVE_CRYPTO_0_CFG 25 +#define SLAVE_DEHR_CFG 26 +#define SLAVE_DISPLAY_CFG 27 +#define SLAVE_GRAPHICS_3D_CFG 28 +#define SLAVE_IMEM_CFG 29 +#define SLAVE_LPASS 30 +#define SLAVE_MPM 31 +#define SLAVE_MSG_RAM 32 +#define SLAVE_MSS 33 +#define SLAVE_PDM 34 +#define SLAVE_PMIC_ARB 35 +#define SLAVE_PCNOC_CFG 36 +#define SLAVE_PRNG 37 +#define SLAVE_QDSS_CFG 38 +#define SLAVE_RBCPR_CFG 39 +#define SLAVE_SDCC_1 40 +#define SLAVE_SDCC_2 41 +#define SLAVE_SECURITY 42 +#define SLAVE_SNOC_CFG 43 +#define SLAVE_SPDM 44 +#define SLAVE_TCSR 45 +#define SLAVE_TLMM 46 +#define SLAVE_USB_HS 47 +#define SLAVE_VENUS_CFG 48 +#define SNOC_PCNOC_SLV 49 + +#endif From 30c8fa3ec61a46da80698e1f8ab95df4d42bf374 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Fri, 7 Jun 2019 16:57:37 +0300 Subject: [PATCH 12/12] interconnect: qcom: Add MSM8916 interconnect provider driver Add driver for the Qualcomm interconnect buses found in MSM8916 based platforms. The topology consists of three NoCs that are controlled by a remote processor that collects the aggregated bandwidth for each master-slave pairs. Reviewed-by: Evan Green Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/msm8916.c | 554 ++++++++++++++++++++++++++++ 3 files changed, 565 insertions(+) create mode 100644 drivers/interconnect/qcom/msm8916.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 2f9304d1db49..76938ece1658 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -5,6 +5,15 @@ config INTERCONNECT_QCOM help Support for Qualcomm's Network-on-Chip interconnect hardware. +config INTERCONNECT_QCOM_MSM8916 + tristate "Qualcomm MSM8916 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on msm8916-based + platforms. + config INTERCONNECT_QCOM_MSM8974 tristate "Qualcomm MSM8974 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 9adf9e380545..e8271575e3d8 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -1,10 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 +qnoc-msm8916-objs := msm8916.o qnoc-msm8974-objs := msm8974.o qnoc-qcs404-objs := qcs404.o qnoc-sdm845-objs := sdm845.o icc-smd-rpm-objs := smd-rpm.o +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c new file mode 100644 index 000000000000..e94f3c5228b7 --- /dev/null +++ b/drivers/interconnect/qcom/msm8916.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2020 Linaro Ltd + * Author: Georgi Djakov + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "smd-rpm.h" + +#define RPM_BUS_MASTER_REQ 0x73616d62 +#define RPM_BUS_SLAVE_REQ 0x766c7362 + +enum { + MSM8916_BIMC_SNOC_MAS = 1, + MSM8916_BIMC_SNOC_SLV, + MSM8916_MASTER_AMPSS_M0, + MSM8916_MASTER_LPASS, + MSM8916_MASTER_BLSP_1, + MSM8916_MASTER_DEHR, + MSM8916_MASTER_GRAPHICS_3D, + MSM8916_MASTER_JPEG, + MSM8916_MASTER_MDP_PORT0, + MSM8916_MASTER_CRYPTO_CORE0, + MSM8916_MASTER_SDCC_1, + MSM8916_MASTER_SDCC_2, + MSM8916_MASTER_QDSS_BAM, + MSM8916_MASTER_QDSS_ETR, + MSM8916_MASTER_SNOC_CFG, + MSM8916_MASTER_SPDM, + MSM8916_MASTER_TCU0, + MSM8916_MASTER_TCU1, + MSM8916_MASTER_USB_HS, + MSM8916_MASTER_VFE, + MSM8916_MASTER_VIDEO_P0, + MSM8916_SNOC_MM_INT_0, + MSM8916_SNOC_MM_INT_1, + MSM8916_SNOC_MM_INT_2, + MSM8916_SNOC_MM_INT_BIMC, + MSM8916_PNOC_INT_0, + MSM8916_PNOC_INT_1, + MSM8916_PNOC_MAS_0, + MSM8916_PNOC_MAS_1, + MSM8916_PNOC_SLV_0, + MSM8916_PNOC_SLV_1, + MSM8916_PNOC_SLV_2, + MSM8916_PNOC_SLV_3, + MSM8916_PNOC_SLV_4, + MSM8916_PNOC_SLV_8, + MSM8916_PNOC_SLV_9, + MSM8916_PNOC_SNOC_MAS, + MSM8916_PNOC_SNOC_SLV, + MSM8916_SNOC_QDSS_INT, + MSM8916_SLAVE_AMPSS_L2, + MSM8916_SLAVE_APSS, + MSM8916_SLAVE_LPASS, + MSM8916_SLAVE_BIMC_CFG, + MSM8916_SLAVE_BLSP_1, + MSM8916_SLAVE_BOOT_ROM, + MSM8916_SLAVE_CAMERA_CFG, + MSM8916_SLAVE_CATS_128, + MSM8916_SLAVE_OCMEM_64, + MSM8916_SLAVE_CLK_CTL, + MSM8916_SLAVE_CRYPTO_0_CFG, + MSM8916_SLAVE_DEHR_CFG, + MSM8916_SLAVE_DISPLAY_CFG, + MSM8916_SLAVE_EBI_CH0, + MSM8916_SLAVE_GRAPHICS_3D_CFG, + MSM8916_SLAVE_IMEM_CFG, + MSM8916_SLAVE_IMEM, + MSM8916_SLAVE_MPM, + MSM8916_SLAVE_MSG_RAM, + MSM8916_SLAVE_MSS, + MSM8916_SLAVE_PDM, + MSM8916_SLAVE_PMIC_ARB, + MSM8916_SLAVE_PNOC_CFG, + MSM8916_SLAVE_PRNG, + MSM8916_SLAVE_QDSS_CFG, + MSM8916_SLAVE_QDSS_STM, + MSM8916_SLAVE_RBCPR_CFG, + MSM8916_SLAVE_SDCC_1, + MSM8916_SLAVE_SDCC_2, + MSM8916_SLAVE_SECURITY, + MSM8916_SLAVE_SNOC_CFG, + MSM8916_SLAVE_SPDM, + MSM8916_SLAVE_SRVC_SNOC, + MSM8916_SLAVE_TCSR, + MSM8916_SLAVE_TLMM, + MSM8916_SLAVE_USB_HS, + MSM8916_SLAVE_VENUS_CFG, + MSM8916_SNOC_BIMC_0_MAS, + MSM8916_SNOC_BIMC_0_SLV, + MSM8916_SNOC_BIMC_1_MAS, + MSM8916_SNOC_BIMC_1_SLV, + MSM8916_SNOC_INT_0, + MSM8916_SNOC_INT_1, + MSM8916_SNOC_INT_BIMC, + MSM8916_SNOC_PNOC_MAS, + MSM8916_SNOC_PNOC_SLV, +}; + +#define to_msm8916_provider(_provider) \ + container_of(_provider, struct msm8916_icc_provider, provider) + +static const struct clk_bulk_data msm8916_bus_clocks[] = { + { .id = "bus" }, + { .id = "bus_a" }, +}; + +/** + * struct msm8916_icc_provider - Qualcomm specific interconnect provider + * @provider: generic interconnect provider + * @bus_clks: the clk_bulk_data table of bus clocks + * @num_clks: the total number of clk_bulk_data entries + */ +struct msm8916_icc_provider { + struct icc_provider provider; + struct clk_bulk_data *bus_clks; + int num_clks; +}; + +#define MSM8916_MAX_LINKS 8 + +/** + * struct msm8916_icc_node - Qualcomm specific interconnect nodes + * @name: the node name used in debugfs + * @id: a unique node identifier + * @links: an array of nodes where we can go next while traversing + * @num_links: the total number of @links + * @buswidth: width of the interconnect between a node and the bus (bytes) + * @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 msm8916_icc_node { + unsigned char *name; + u16 id; + u16 links[MSM8916_MAX_LINKS]; + u16 num_links; + u16 buswidth; + int mas_rpm_id; + int slv_rpm_id; + u64 rate; +}; + +struct msm8916_icc_desc { + struct msm8916_icc_node **nodes; + size_t num_nodes; +}; + +#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ + ...) \ + static struct msm8916_icc_node _name = { \ + .name = #_name, \ + .id = _id, \ + .buswidth = _buswidth, \ + .mas_rpm_id = _mas_rpm_id, \ + .slv_rpm_id = _slv_rpm_id, \ + .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ + .links = { __VA_ARGS__ }, \ + } + +DEFINE_QNODE(bimc_snoc_mas, MSM8916_BIMC_SNOC_MAS, 8, -1, -1, MSM8916_BIMC_SNOC_SLV); +DEFINE_QNODE(bimc_snoc_slv, MSM8916_BIMC_SNOC_SLV, 8, -1, -1, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_1); +DEFINE_QNODE(mas_apss, MSM8916_MASTER_AMPSS_M0, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2); +DEFINE_QNODE(mas_audio, MSM8916_MASTER_LPASS, 4, -1, -1, MSM8916_PNOC_MAS_0); +DEFINE_QNODE(mas_blsp_1, MSM8916_MASTER_BLSP_1, 4, -1, -1, MSM8916_PNOC_MAS_1); +DEFINE_QNODE(mas_dehr, MSM8916_MASTER_DEHR, 4, -1, -1, MSM8916_PNOC_MAS_0); +DEFINE_QNODE(mas_gfx, MSM8916_MASTER_GRAPHICS_3D, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2); +DEFINE_QNODE(mas_jpeg, MSM8916_MASTER_JPEG, 16, -1, -1, MSM8916_SNOC_MM_INT_0, MSM8916_SNOC_MM_INT_2); +DEFINE_QNODE(mas_mdp, MSM8916_MASTER_MDP_PORT0, 16, -1, -1, MSM8916_SNOC_MM_INT_0, MSM8916_SNOC_MM_INT_2); +DEFINE_QNODE(mas_pcnoc_crypto_0, MSM8916_MASTER_CRYPTO_CORE0, 8, -1, -1, MSM8916_PNOC_INT_1); +DEFINE_QNODE(mas_pcnoc_sdcc_1, MSM8916_MASTER_SDCC_1, 8, -1, -1, MSM8916_PNOC_INT_1); +DEFINE_QNODE(mas_pcnoc_sdcc_2, MSM8916_MASTER_SDCC_2, 8, -1, -1, MSM8916_PNOC_INT_1); +DEFINE_QNODE(mas_qdss_bam, MSM8916_MASTER_QDSS_BAM, 8, -1, -1, MSM8916_SNOC_QDSS_INT); +DEFINE_QNODE(mas_qdss_etr, MSM8916_MASTER_QDSS_ETR, 8, -1, -1, MSM8916_SNOC_QDSS_INT); +DEFINE_QNODE(mas_snoc_cfg, MSM8916_MASTER_SNOC_CFG, 4, 20, -1, MSM8916_SNOC_QDSS_INT); +DEFINE_QNODE(mas_spdm, MSM8916_MASTER_SPDM, 4, -1, -1, MSM8916_PNOC_MAS_0); +DEFINE_QNODE(mas_tcu0, MSM8916_MASTER_TCU0, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2); +DEFINE_QNODE(mas_tcu1, MSM8916_MASTER_TCU1, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2); +DEFINE_QNODE(mas_usb_hs, MSM8916_MASTER_USB_HS, 4, -1, -1, MSM8916_PNOC_MAS_1); +DEFINE_QNODE(mas_vfe, MSM8916_MASTER_VFE, 16, -1, -1, MSM8916_SNOC_MM_INT_1, MSM8916_SNOC_MM_INT_2); +DEFINE_QNODE(mas_video, MSM8916_MASTER_VIDEO_P0, 16, -1, -1, MSM8916_SNOC_MM_INT_0, MSM8916_SNOC_MM_INT_2); +DEFINE_QNODE(mm_int_0, MSM8916_SNOC_MM_INT_0, 16, -1, -1, MSM8916_SNOC_MM_INT_BIMC); +DEFINE_QNODE(mm_int_1, MSM8916_SNOC_MM_INT_1, 16, -1, -1, MSM8916_SNOC_MM_INT_BIMC); +DEFINE_QNODE(mm_int_2, MSM8916_SNOC_MM_INT_2, 16, -1, -1, MSM8916_SNOC_INT_0); +DEFINE_QNODE(mm_int_bimc, MSM8916_SNOC_MM_INT_BIMC, 16, -1, -1, MSM8916_SNOC_BIMC_1_MAS); +DEFINE_QNODE(pcnoc_int_0, MSM8916_PNOC_INT_0, 8, -1, -1, MSM8916_PNOC_SNOC_MAS, MSM8916_PNOC_SLV_0, MSM8916_PNOC_SLV_1, MSM8916_PNOC_SLV_2, MSM8916_PNOC_SLV_3, MSM8916_PNOC_SLV_4, MSM8916_PNOC_SLV_8, MSM8916_PNOC_SLV_9); +DEFINE_QNODE(pcnoc_int_1, MSM8916_PNOC_INT_1, 8, -1, -1, MSM8916_PNOC_SNOC_MAS); +DEFINE_QNODE(pcnoc_m_0, MSM8916_PNOC_MAS_0, 8, -1, -1, MSM8916_PNOC_INT_0); +DEFINE_QNODE(pcnoc_m_1, MSM8916_PNOC_MAS_1, 8, -1, -1, MSM8916_PNOC_SNOC_MAS); +DEFINE_QNODE(pcnoc_s_0, MSM8916_PNOC_SLV_0, 8, -1, -1, MSM8916_SLAVE_CLK_CTL, MSM8916_SLAVE_TLMM, MSM8916_SLAVE_TCSR, MSM8916_SLAVE_SECURITY, MSM8916_SLAVE_MSS); +DEFINE_QNODE(pcnoc_s_1, MSM8916_PNOC_SLV_1, 8, -1, -1, MSM8916_SLAVE_IMEM_CFG, MSM8916_SLAVE_CRYPTO_0_CFG, MSM8916_SLAVE_MSG_RAM, MSM8916_SLAVE_PDM, MSM8916_SLAVE_PRNG); +DEFINE_QNODE(pcnoc_s_2, MSM8916_PNOC_SLV_2, 8, -1, -1, MSM8916_SLAVE_SPDM, MSM8916_SLAVE_BOOT_ROM, MSM8916_SLAVE_BIMC_CFG, MSM8916_SLAVE_PNOC_CFG, MSM8916_SLAVE_PMIC_ARB); +DEFINE_QNODE(pcnoc_s_3, MSM8916_PNOC_SLV_3, 8, -1, -1, MSM8916_SLAVE_MPM, MSM8916_SLAVE_SNOC_CFG, MSM8916_SLAVE_RBCPR_CFG, MSM8916_SLAVE_QDSS_CFG, MSM8916_SLAVE_DEHR_CFG); +DEFINE_QNODE(pcnoc_s_4, MSM8916_PNOC_SLV_4, 8, -1, -1, MSM8916_SLAVE_VENUS_CFG, MSM8916_SLAVE_CAMERA_CFG, MSM8916_SLAVE_DISPLAY_CFG); +DEFINE_QNODE(pcnoc_s_8, MSM8916_PNOC_SLV_8, 8, -1, -1, MSM8916_SLAVE_USB_HS, MSM8916_SLAVE_SDCC_1, MSM8916_SLAVE_BLSP_1); +DEFINE_QNODE(pcnoc_s_9, MSM8916_PNOC_SLV_9, 8, -1, -1, MSM8916_SLAVE_SDCC_2, MSM8916_SLAVE_LPASS, MSM8916_SLAVE_GRAPHICS_3D_CFG); +DEFINE_QNODE(pcnoc_snoc_mas, MSM8916_PNOC_SNOC_MAS, 8, 29, -1, MSM8916_PNOC_SNOC_SLV); +DEFINE_QNODE(pcnoc_snoc_slv, MSM8916_PNOC_SNOC_SLV, 8, -1, 45, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_BIMC, MSM8916_SNOC_INT_1); +DEFINE_QNODE(qdss_int, MSM8916_SNOC_QDSS_INT, 8, -1, -1, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_BIMC); +DEFINE_QNODE(slv_apps_l2, MSM8916_SLAVE_AMPSS_L2, 8, -1, -1, 0); +DEFINE_QNODE(slv_apss, MSM8916_SLAVE_APSS, 4, -1, 20, 0); +DEFINE_QNODE(slv_audio, MSM8916_SLAVE_LPASS, 4, -1, -1, 0); +DEFINE_QNODE(slv_bimc_cfg, MSM8916_SLAVE_BIMC_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_blsp_1, MSM8916_SLAVE_BLSP_1, 4, -1, -1, 0); +DEFINE_QNODE(slv_boot_rom, MSM8916_SLAVE_BOOT_ROM, 4, -1, -1, 0); +DEFINE_QNODE(slv_camera_cfg, MSM8916_SLAVE_CAMERA_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_cats_0, MSM8916_SLAVE_CATS_128, 16, -1, 106, 0); +DEFINE_QNODE(slv_cats_1, MSM8916_SLAVE_OCMEM_64, 8, -1, 107, 0); +DEFINE_QNODE(slv_clk_ctl, MSM8916_SLAVE_CLK_CTL, 4, -1, -1, 0); +DEFINE_QNODE(slv_crypto_0_cfg, MSM8916_SLAVE_CRYPTO_0_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_dehr_cfg, MSM8916_SLAVE_DEHR_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_display_cfg, MSM8916_SLAVE_DISPLAY_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_ebi_ch0, MSM8916_SLAVE_EBI_CH0, 8, -1, 0, 0); +DEFINE_QNODE(slv_gfx_cfg, MSM8916_SLAVE_GRAPHICS_3D_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_imem_cfg, MSM8916_SLAVE_IMEM_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_imem, MSM8916_SLAVE_IMEM, 8, -1, 26, 0); +DEFINE_QNODE(slv_mpm, MSM8916_SLAVE_MPM, 4, -1, -1, 0); +DEFINE_QNODE(slv_msg_ram, MSM8916_SLAVE_MSG_RAM, 4, -1, -1, 0); +DEFINE_QNODE(slv_mss, MSM8916_SLAVE_MSS, 4, -1, -1, 0); +DEFINE_QNODE(slv_pdm, MSM8916_SLAVE_PDM, 4, -1, -1, 0); +DEFINE_QNODE(slv_pmic_arb, MSM8916_SLAVE_PMIC_ARB, 4, -1, -1, 0); +DEFINE_QNODE(slv_pcnoc_cfg, MSM8916_SLAVE_PNOC_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_prng, MSM8916_SLAVE_PRNG, 4, -1, -1, 0); +DEFINE_QNODE(slv_qdss_cfg, MSM8916_SLAVE_QDSS_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_qdss_stm, MSM8916_SLAVE_QDSS_STM, 4, -1, 30, 0); +DEFINE_QNODE(slv_rbcpr_cfg, MSM8916_SLAVE_RBCPR_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_sdcc_1, MSM8916_SLAVE_SDCC_1, 4, -1, -1, 0); +DEFINE_QNODE(slv_sdcc_2, MSM8916_SLAVE_SDCC_2, 4, -1, -1, 0); +DEFINE_QNODE(slv_security, MSM8916_SLAVE_SECURITY, 4, -1, -1, 0); +DEFINE_QNODE(slv_snoc_cfg, MSM8916_SLAVE_SNOC_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_spdm, MSM8916_SLAVE_SPDM, 4, -1, -1, 0); +DEFINE_QNODE(slv_srvc_snoc, MSM8916_SLAVE_SRVC_SNOC, 8, -1, 29, 0); +DEFINE_QNODE(slv_tcsr, MSM8916_SLAVE_TCSR, 4, -1, -1, 0); +DEFINE_QNODE(slv_tlmm, MSM8916_SLAVE_TLMM, 4, -1, -1, 0); +DEFINE_QNODE(slv_usb_hs, MSM8916_SLAVE_USB_HS, 4, -1, -1, 0); +DEFINE_QNODE(slv_venus_cfg, MSM8916_SLAVE_VENUS_CFG, 4, -1, -1, 0); +DEFINE_QNODE(snoc_bimc_0_mas, MSM8916_SNOC_BIMC_0_MAS, 8, 3, -1, MSM8916_SNOC_BIMC_0_SLV); +DEFINE_QNODE(snoc_bimc_0_slv, MSM8916_SNOC_BIMC_0_SLV, 8, -1, 24, MSM8916_SLAVE_EBI_CH0); +DEFINE_QNODE(snoc_bimc_1_mas, MSM8916_SNOC_BIMC_1_MAS, 16, -1, -1, MSM8916_SNOC_BIMC_1_SLV); +DEFINE_QNODE(snoc_bimc_1_slv, MSM8916_SNOC_BIMC_1_SLV, 8, -1, -1, MSM8916_SLAVE_EBI_CH0); +DEFINE_QNODE(snoc_int_0, MSM8916_SNOC_INT_0, 8, 99, 130, MSM8916_SLAVE_QDSS_STM, MSM8916_SLAVE_IMEM, MSM8916_SNOC_PNOC_MAS); +DEFINE_QNODE(snoc_int_1, MSM8916_SNOC_INT_1, 8, 100, 131, MSM8916_SLAVE_APSS, MSM8916_SLAVE_CATS_128, MSM8916_SLAVE_OCMEM_64); +DEFINE_QNODE(snoc_int_bimc, MSM8916_SNOC_INT_BIMC, 8, 101, 132, MSM8916_SNOC_BIMC_0_MAS); +DEFINE_QNODE(snoc_pcnoc_mas, MSM8916_SNOC_PNOC_MAS, 8, -1, -1, MSM8916_SNOC_PNOC_SLV); +DEFINE_QNODE(snoc_pcnoc_slv, MSM8916_SNOC_PNOC_SLV, 8, -1, -1, MSM8916_PNOC_INT_0); + +static struct msm8916_icc_node *msm8916_snoc_nodes[] = { + [BIMC_SNOC_SLV] = &bimc_snoc_slv, + [MASTER_JPEG] = &mas_jpeg, + [MASTER_MDP_PORT0] = &mas_mdp, + [MASTER_QDSS_BAM] = &mas_qdss_bam, + [MASTER_QDSS_ETR] = &mas_qdss_etr, + [MASTER_SNOC_CFG] = &mas_snoc_cfg, + [MASTER_VFE] = &mas_vfe, + [MASTER_VIDEO_P0] = &mas_video, + [SNOC_MM_INT_0] = &mm_int_0, + [SNOC_MM_INT_1] = &mm_int_1, + [SNOC_MM_INT_2] = &mm_int_2, + [SNOC_MM_INT_BIMC] = &mm_int_bimc, + [PCNOC_SNOC_SLV] = &pcnoc_snoc_slv, + [SLAVE_APSS] = &slv_apss, + [SLAVE_CATS_128] = &slv_cats_0, + [SLAVE_OCMEM_64] = &slv_cats_1, + [SLAVE_IMEM] = &slv_imem, + [SLAVE_QDSS_STM] = &slv_qdss_stm, + [SLAVE_SRVC_SNOC] = &slv_srvc_snoc, + [SNOC_BIMC_0_MAS] = &snoc_bimc_0_mas, + [SNOC_BIMC_1_MAS] = &snoc_bimc_1_mas, + [SNOC_INT_0] = &snoc_int_0, + [SNOC_INT_1] = &snoc_int_1, + [SNOC_INT_BIMC] = &snoc_int_bimc, + [SNOC_PCNOC_MAS] = &snoc_pcnoc_mas, + [SNOC_QDSS_INT] = &qdss_int, +}; + +static struct msm8916_icc_desc msm8916_snoc = { + .nodes = msm8916_snoc_nodes, + .num_nodes = ARRAY_SIZE(msm8916_snoc_nodes), +}; + +static struct msm8916_icc_node *msm8916_bimc_nodes[] = { + [BIMC_SNOC_MAS] = &bimc_snoc_mas, + [MASTER_AMPSS_M0] = &mas_apss, + [MASTER_GRAPHICS_3D] = &mas_gfx, + [MASTER_TCU0] = &mas_tcu0, + [MASTER_TCU1] = &mas_tcu1, + [SLAVE_AMPSS_L2] = &slv_apps_l2, + [SLAVE_EBI_CH0] = &slv_ebi_ch0, + [SNOC_BIMC_0_SLV] = &snoc_bimc_0_slv, + [SNOC_BIMC_1_SLV] = &snoc_bimc_1_slv, +}; + +static struct msm8916_icc_desc msm8916_bimc = { + .nodes = msm8916_bimc_nodes, + .num_nodes = ARRAY_SIZE(msm8916_bimc_nodes), +}; + +static struct msm8916_icc_node *msm8916_pcnoc_nodes[] = { + [MASTER_BLSP_1] = &mas_blsp_1, + [MASTER_DEHR] = &mas_dehr, + [MASTER_LPASS] = &mas_audio, + [MASTER_CRYPTO_CORE0] = &mas_pcnoc_crypto_0, + [MASTER_SDCC_1] = &mas_pcnoc_sdcc_1, + [MASTER_SDCC_2] = &mas_pcnoc_sdcc_2, + [MASTER_SPDM] = &mas_spdm, + [MASTER_USB_HS] = &mas_usb_hs, + [PCNOC_INT_0] = &pcnoc_int_0, + [PCNOC_INT_1] = &pcnoc_int_1, + [PCNOC_MAS_0] = &pcnoc_m_0, + [PCNOC_MAS_1] = &pcnoc_m_1, + [PCNOC_SLV_0] = &pcnoc_s_0, + [PCNOC_SLV_1] = &pcnoc_s_1, + [PCNOC_SLV_2] = &pcnoc_s_2, + [PCNOC_SLV_3] = &pcnoc_s_3, + [PCNOC_SLV_4] = &pcnoc_s_4, + [PCNOC_SLV_8] = &pcnoc_s_8, + [PCNOC_SLV_9] = &pcnoc_s_9, + [PCNOC_SNOC_MAS] = &pcnoc_snoc_mas, + [SLAVE_BIMC_CFG] = &slv_bimc_cfg, + [SLAVE_BLSP_1] = &slv_blsp_1, + [SLAVE_BOOT_ROM] = &slv_boot_rom, + [SLAVE_CAMERA_CFG] = &slv_camera_cfg, + [SLAVE_CLK_CTL] = &slv_clk_ctl, + [SLAVE_CRYPTO_0_CFG] = &slv_crypto_0_cfg, + [SLAVE_DEHR_CFG] = &slv_dehr_cfg, + [SLAVE_DISPLAY_CFG] = &slv_display_cfg, + [SLAVE_GRAPHICS_3D_CFG] = &slv_gfx_cfg, + [SLAVE_IMEM_CFG] = &slv_imem_cfg, + [SLAVE_LPASS] = &slv_audio, + [SLAVE_MPM] = &slv_mpm, + [SLAVE_MSG_RAM] = &slv_msg_ram, + [SLAVE_MSS] = &slv_mss, + [SLAVE_PDM] = &slv_pdm, + [SLAVE_PMIC_ARB] = &slv_pmic_arb, + [SLAVE_PCNOC_CFG] = &slv_pcnoc_cfg, + [SLAVE_PRNG] = &slv_prng, + [SLAVE_QDSS_CFG] = &slv_qdss_cfg, + [SLAVE_RBCPR_CFG] = &slv_rbcpr_cfg, + [SLAVE_SDCC_1] = &slv_sdcc_1, + [SLAVE_SDCC_2] = &slv_sdcc_2, + [SLAVE_SECURITY] = &slv_security, + [SLAVE_SNOC_CFG] = &slv_snoc_cfg, + [SLAVE_SPDM] = &slv_spdm, + [SLAVE_TCSR] = &slv_tcsr, + [SLAVE_TLMM] = &slv_tlmm, + [SLAVE_USB_HS] = &slv_usb_hs, + [SLAVE_VENUS_CFG] = &slv_venus_cfg, + [SNOC_PCNOC_SLV] = &snoc_pcnoc_slv, +}; + +static struct msm8916_icc_desc msm8916_pcnoc = { + .nodes = msm8916_pcnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8916_pcnoc_nodes), +}; + +static int msm8916_icc_set(struct icc_node *src, struct icc_node *dst) +{ + struct msm8916_icc_provider *qp; + struct msm8916_icc_node *qn; + u64 sum_bw, max_peak_bw, rate; + u32 agg_avg = 0, agg_peak = 0; + struct icc_provider *provider; + struct icc_node *n; + int ret, i; + + qn = src->data; + provider = src->provider; + qp = to_msm8916_provider(provider); + + list_for_each_entry(n, &provider->nodes, node_list) + provider->aggregate(n, 0, n->avg_bw, n->peak_bw, + &agg_avg, &agg_peak); + + sum_bw = icc_units_to_bps(agg_avg); + max_peak_bw = icc_units_to_bps(agg_peak); + + /* send bandwidth request 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, + sum_bw); + if (ret) { + pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", + qn->mas_rpm_id, ret); + return ret; + } + } + + 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, + sum_bw); + if (ret) { + pr_err("qcom_icc_rpm_smd_send slv error %d\n", + ret); + return ret; + } + } + + rate = max(sum_bw, max_peak_bw); + + do_div(rate, qn->buswidth); + + if (qn->rate == rate) + return 0; + + for (i = 0; i < qp->num_clks; i++) { + ret = clk_set_rate(qp->bus_clks[i].clk, rate); + if (ret) { + pr_err("%s clk_set_rate error: %d\n", + qp->bus_clks[i].id, ret); + return ret; + } + } + + qn->rate = rate; + + return 0; +} + +static int msm8916_qnoc_probe(struct platform_device *pdev) +{ + const struct msm8916_icc_desc *desc; + struct msm8916_icc_node **qnodes; + struct msm8916_icc_provider *qp; + struct device *dev = &pdev->dev; + struct icc_onecell_data *data; + struct icc_provider *provider; + struct icc_node *node; + size_t num_nodes, i; + int ret; + + /* wait for the RPM proxy */ + if (!qcom_icc_rpm_smd_available()) + return -EPROBE_DEFER; + + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; + + qnodes = desc->nodes; + num_nodes = desc->num_nodes; + + qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + if (!qp) + return -ENOMEM; + + data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + qp->bus_clks = devm_kmemdup(dev, msm8916_bus_clocks, + sizeof(msm8916_bus_clocks), GFP_KERNEL); + if (!qp->bus_clks) + return -ENOMEM; + + qp->num_clks = ARRAY_SIZE(msm8916_bus_clocks); + ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + provider = &qp->provider; + INIT_LIST_HEAD(&provider->nodes); + provider->dev = dev; + provider->set = msm8916_icc_set; + provider->aggregate = icc_std_aggregate; + provider->xlate = of_icc_xlate_onecell; + provider->data = data; + + ret = icc_provider_add(provider); + if (ret) { + dev_err(dev, "error adding interconnect provider: %d\n", ret); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + return ret; + } + + for (i = 0; i < num_nodes; i++) { + 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); + + for (j = 0; j < qnodes[i]->num_links; j++) + icc_link_create(node, qnodes[i]->links[j]); + + data->nodes[i] = node; + } + data->num_nodes = num_nodes; + + platform_set_drvdata(pdev, qp); + + return 0; + +err: + icc_nodes_remove(provider); + icc_provider_del(provider); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + + return ret; +} + +static int msm8916_qnoc_remove(struct platform_device *pdev) +{ + struct msm8916_icc_provider *qp = platform_get_drvdata(pdev); + + icc_nodes_remove(&qp->provider); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + return icc_provider_del(&qp->provider); +} + +static const struct of_device_id msm8916_noc_of_match[] = { + { .compatible = "qcom,msm8916-bimc", .data = &msm8916_bimc }, + { .compatible = "qcom,msm8916-pcnoc", .data = &msm8916_pcnoc }, + { .compatible = "qcom,msm8916-snoc", .data = &msm8916_snoc }, + { } +}; +MODULE_DEVICE_TABLE(of, msm8916_noc_of_match); + +static struct platform_driver msm8916_noc_driver = { + .probe = msm8916_qnoc_probe, + .remove = msm8916_qnoc_remove, + .driver = { + .name = "qnoc-msm8916", + .of_match_table = msm8916_noc_of_match, + }, +}; +module_platform_driver(msm8916_noc_driver); +MODULE_AUTHOR("Georgi Djakov "); +MODULE_DESCRIPTION("Qualcomm MSM8916 NoC driver"); +MODULE_LICENSE("GPL v2");