Linux-ARM-MSM Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v3 1/2] dt-bindings: interconnect: Add OSM L3 DT bindings
       [not found] <20191118154435.20357-1-sibis@codeaurora.org>
@ 2019-11-18 15:45 ` Sibi Sankar
  2019-11-19  0:31   ` Stephen Boyd
  2019-11-18 15:45 ` [PATCH v3 2/2] interconnect: qcom: Add OSM L3 interconnect provider support Sibi Sankar
       [not found] ` <0101016e7f30ad15-18908ef0-a2b9-4a2a-bf32-6cb3aa447b01-000000@us-west-2.amazonses.com>
  2 siblings, 1 reply; 9+ messages in thread
From: Sibi Sankar @ 2019-11-18 15:45 UTC (permalink / raw)
  To: robh+dt, georgi.djakov
  Cc: bjorn.andersson, agross, linux-kernel, devicetree, linux-arm-msm,
	mark.rutland, evgreen, daidavid1, saravanak, viresh.kumar,
	Sibi Sankar

Add bindings for Operating State Manager (OSM) L3 interconnect provider
on SDM845 SoCs.

Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
---
 .../bindings/interconnect/qcom,osm-l3.yaml    | 56 +++++++++++++++++++
 .../dt-bindings/interconnect/qcom,osm-l3.h    | 12 ++++
 2 files changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml
 create mode 100644 include/dt-bindings/interconnect/qcom,osm-l3.h

diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml
new file mode 100644
index 0000000000000..fec8289ceeeed
--- /dev/null
+++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interconnect/qcom,osm-l3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Operating State Manager (OSM) L3 Interconnect Provider
+
+maintainers:
+  - Sibi Sankar <sibis@codeaurora.org>
+
+description:
+  L3 cache bandwidth requirements on Qualcomm SoCs is serviced by the OSM.
+  The OSM L3 interconnect provider aggregates the L3 bandwidth requests
+  from CPU/GPU and relays it to the OSM.
+
+properties:
+  compatible:
+    const: "qcom,sdm845-osm-l3"
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: xo clock
+      - description: alternate clock
+
+  clock-names:
+    items:
+      - const: xo
+      - const: alternate
+
+  '#interconnect-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#interconnect-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    osm_l3: interconnect@17d41000 {
+      compatible = "qcom,sdm845-osm-l3";
+      reg = <0x17d41000 0x1400>;
+
+      clocks = <&rpmhcc 0>, <&gcc 165>;
+      clock-names = "xo", "alternate";
+
+      #interconnect-cells = <1>;
+    };
diff --git a/include/dt-bindings/interconnect/qcom,osm-l3.h b/include/dt-bindings/interconnect/qcom,osm-l3.h
new file mode 100644
index 0000000000000..54858ff7674d7
--- /dev/null
+++ b/include/dt-bindings/interconnect/qcom,osm-l3.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_OSM_L3_H
+#define __DT_BINDINGS_INTERCONNECT_QCOM_OSM_L3_H
+
+#define MASTER_OSM_L3_APPS	0
+#define SLAVE_OSM_L3		1
+
+#endif
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 2/2] interconnect: qcom: Add OSM L3 interconnect provider support
       [not found] <20191118154435.20357-1-sibis@codeaurora.org>
  2019-11-18 15:45 ` [PATCH v3 1/2] dt-bindings: interconnect: Add OSM L3 DT bindings Sibi Sankar
@ 2019-11-18 15:45 ` Sibi Sankar
       [not found] ` <0101016e7f30ad15-18908ef0-a2b9-4a2a-bf32-6cb3aa447b01-000000@us-west-2.amazonses.com>
  2 siblings, 0 replies; 9+ messages in thread
From: Sibi Sankar @ 2019-11-18 15:45 UTC (permalink / raw)
  To: robh+dt, georgi.djakov
  Cc: bjorn.andersson, agross, linux-kernel, devicetree, linux-arm-msm,
	mark.rutland, evgreen, daidavid1, saravanak, viresh.kumar,
	Sibi Sankar

On some Qualcomm SoCs, Operating State Manager (OSM) controls the
resources of scaling L3 caches. Add a driver to handle bandwidth
requests to OSM L3 from CPU/GPU.

Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
---
 drivers/interconnect/qcom/Kconfig  |   7 +
 drivers/interconnect/qcom/Makefile |   2 +
 drivers/interconnect/qcom/osm-l3.c | 284 +++++++++++++++++++++++++++++
 3 files changed, 293 insertions(+)
 create mode 100644 drivers/interconnect/qcom/osm-l3.c

diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
index ecf057d7e2409..17aee5b0f15b7 100644
--- a/drivers/interconnect/qcom/Kconfig
+++ b/drivers/interconnect/qcom/Kconfig
@@ -5,6 +5,13 @@ config INTERCONNECT_QCOM
 	help
 	  Support for Qualcomm's Network-on-Chip interconnect hardware.
 
+config INTERCONNECT_QCOM_OSM_L3
+	tristate "Qualcomm OSM L3 interconnect driver"
+	depends on INTERCONNECT_QCOM || COMPILE_TEST
+	help
+	  Say y here to support the Operating State Manager (OSM) interconnect
+	  driver which controls the scaling of L3 caches on Qualcomm SoCs.
+
 config INTERCONNECT_QCOM_QCS404
 	tristate "Qualcomm QCS404 interconnect driver"
 	depends on INTERCONNECT_QCOM
diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
index 9adf9e380545e..8d86d6515ffc9 100644
--- a/drivers/interconnect/qcom/Makefile
+++ b/drivers/interconnect/qcom/Makefile
@@ -1,10 +1,12 @@
 # SPDX-License-Identifier: GPL-2.0
 
+icc-osm-l3-objs				:= osm-l3.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_OSM_L3) += icc-osm-l3.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/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c
new file mode 100644
index 0000000000000..5e9f9ce02863b
--- /dev/null
+++ b/drivers/interconnect/qcom/osm-l3.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <dt-bindings/interconnect/qcom,osm-l3.h>
+#include <dt-bindings/interconnect/qcom,sdm845.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interconnect-provider.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#define LUT_MAX_ENTRIES			40U
+#define LUT_SRC				GENMASK(31, 30)
+#define LUT_L_VAL			GENMASK(7, 0)
+#define LUT_ROW_SIZE			32
+#define CLK_HW_DIV			2
+
+/* Register offsets */
+#define REG_ENABLE			0x0
+#define REG_FREQ_LUT			0x110
+#define REG_PERF_STATE			0x920
+
+#define OSM_L3_MAX_LINKS		1
+
+#define to_qcom_provider(_provider) \
+	container_of(_provider, struct qcom_osm_l3_icc_provider, provider)
+
+enum {
+	SDM845_MASTER_OSM_L3_APPS = SLAVE_TCU + 1,
+	SDM845_SLAVE_OSM_L3,
+};
+
+struct qcom_osm_l3_icc_provider {
+	void __iomem *base;
+	unsigned int max_state;
+	unsigned long lut_tables[LUT_MAX_ENTRIES];
+	struct icc_provider provider;
+};
+
+/**
+ * struct qcom_icc_node - Qualcomm specific interconnect nodes
+ * @name: the node name used in debugfs
+ * @links: an array of nodes where we can go next while traversing
+ * @id: a unique node identifier
+ * @num_links: the total number of @links
+ * @buswidth: width of the interconnect between a node and the bus
+ */
+struct qcom_icc_node {
+	const char *name;
+	u16 links[OSM_L3_MAX_LINKS];
+	u16 id;
+	u16 num_links;
+	u16 buswidth;
+};
+
+struct qcom_icc_desc {
+	struct qcom_icc_node **nodes;
+	size_t num_nodes;
+};
+
+#define DEFINE_QNODE(_name, _id, _buswidth, ...)			\
+		static struct qcom_icc_node _name = {			\
+		.name = #_name,						\
+		.id = _id,						\
+		.buswidth = _buswidth,					\
+		.num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })),	\
+		.links = { __VA_ARGS__ },				\
+	}
+
+DEFINE_QNODE(osm_apps_l3, SDM845_MASTER_OSM_L3_APPS, 16, SDM845_SLAVE_OSM_L3);
+DEFINE_QNODE(osm_l3, SDM845_SLAVE_OSM_L3, 16);
+
+static struct qcom_icc_node *sdm845_osm_l3_nodes[] = {
+	[MASTER_OSM_L3_APPS] = &osm_apps_l3,
+	[SLAVE_OSM_L3] = &osm_l3,
+};
+
+static struct qcom_icc_desc sdm845_osm_l3 = {
+	.nodes = sdm845_osm_l3_nodes,
+	.num_nodes = ARRAY_SIZE(sdm845_osm_l3_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_t(u32, *agg_peak, peak_bw);
+
+	return 0;
+}
+
+static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
+{
+	struct qcom_osm_l3_icc_provider *qp;
+	struct icc_provider *provider;
+	struct qcom_icc_node *qn;
+	struct icc_node *n;
+	unsigned int index;
+	u32 agg_peak = 0;
+	u32 agg_avg = 0;
+	u64 rate;
+
+	qn = src->data;
+	provider = src->provider;
+	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);
+
+	rate = max(agg_avg, agg_peak);
+	rate = icc_units_to_bps(rate);
+	do_div(rate, qn->buswidth);
+
+	for (index = 0; index < qp->max_state; index++) {
+		if (qp->lut_tables[index] >= rate)
+			break;
+	}
+
+	writel_relaxed(index, qp->base + REG_PERF_STATE);
+
+	return 0;
+}
+
+static int qcom_osm_l3_remove(struct platform_device *pdev)
+{
+	struct qcom_osm_l3_icc_provider *qp = platform_get_drvdata(pdev);
+	struct icc_provider *provider = &qp->provider;
+	struct icc_node *n;
+
+	list_for_each_entry(n, &provider->nodes, node_list) {
+		icc_node_del(n);
+		icc_node_destroy(n->id);
+	}
+
+	return icc_provider_del(provider);
+}
+
+static int qcom_osm_l3_probe(struct platform_device *pdev)
+{
+	u32 info, src, lval, i, prev_freq = 0, freq;
+	static unsigned long hw_rate, xo_rate;
+	struct qcom_osm_l3_icc_provider *qp;
+	const struct qcom_icc_desc *desc;
+	struct icc_onecell_data *data;
+	struct icc_provider *provider;
+	struct qcom_icc_node **qnodes;
+	struct icc_node *node;
+	size_t num_nodes;
+	struct clk *clk;
+	int ret;
+
+	clk = clk_get(&pdev->dev, "xo");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	xo_rate = clk_get_rate(clk);
+	clk_put(clk);
+
+	clk = clk_get(&pdev->dev, "alternate");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	hw_rate = clk_get_rate(clk) / CLK_HW_DIV;
+	clk_put(clk);
+
+	qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
+	if (!qp)
+		return -ENOMEM;
+
+	qp->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(qp->base))
+		return PTR_ERR(qp->base);
+
+	/* HW should be in enabled state to proceed */
+	if (!(readl_relaxed(qp->base + REG_ENABLE) & 0x1)) {
+		dev_err(&pdev->dev, "error hardware not enabled\n");
+		return -ENODEV;
+	}
+
+	for (i = 0; i < LUT_MAX_ENTRIES; i++) {
+		info = readl_relaxed(qp->base + REG_FREQ_LUT +
+				     i * LUT_ROW_SIZE);
+		src = FIELD_GET(LUT_SRC, info);
+		lval = FIELD_GET(LUT_L_VAL, info);
+		if (src)
+			freq = xo_rate * lval;
+		else
+			freq = hw_rate;
+
+		/*
+		 * Two of the same frequencies with the same core counts means
+		 * end of table
+		 */
+		if (i > 0 && prev_freq == freq)
+			break;
+
+		qp->lut_tables[i] = freq;
+		prev_freq = freq;
+	}
+	qp->max_state = i;
+
+	desc = of_device_get_match_data(&pdev->dev);
+	if (!desc)
+		return -EINVAL;
+
+	qnodes = desc->nodes;
+	num_nodes = desc->num_nodes;
+
+	data = devm_kcalloc(&pdev->dev, num_nodes, sizeof(*node), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	provider = &qp->provider;
+	provider->dev = &pdev->dev;
+	provider->set = qcom_icc_set;
+	provider->aggregate = qcom_icc_aggregate;
+	provider->xlate = of_icc_xlate_onecell;
+	INIT_LIST_HEAD(&provider->nodes);
+	provider->data = data;
+
+	ret = icc_provider_add(provider);
+	if (ret) {
+		dev_err(&pdev->dev, "error adding interconnect provider\n");
+		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);
+
+		dev_dbg(&pdev->dev, "registered node %p %s %d\n", node,
+			qnodes[i]->name, node->id);
+
+		/* populate links */
+		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 ret;
+err:
+	qcom_osm_l3_remove(pdev);
+	return ret;
+}
+
+static const struct of_device_id osm_l3_of_match[] = {
+	{ .compatible = "qcom,sdm845-osm-l3", .data = &sdm845_osm_l3 },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, osm_l3_of_match);
+
+static struct platform_driver osm_l3_driver = {
+	.probe = qcom_osm_l3_probe,
+	.remove = qcom_osm_l3_remove,
+	.driver = {
+		.name = "osm-l3",
+		.of_match_table = osm_l3_of_match,
+	},
+};
+module_platform_driver(osm_l3_driver);
+
+MODULE_DESCRIPTION("Qualcomm OSM L3 interconnect driver");
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 2/2] interconnect: qcom: Add OSM L3 interconnect provider support
       [not found] ` <0101016e7f30ad15-18908ef0-a2b9-4a2a-bf32-6cb3aa447b01-000000@us-west-2.amazonses.com>
@ 2019-11-18 22:42   ` Evan Green
  2019-11-19 12:00     ` sibis
  2019-11-21 12:58     ` Georgi Djakov
  0 siblings, 2 replies; 9+ messages in thread
From: Evan Green @ 2019-11-18 22:42 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Rob Herring, Georgi Djakov, Bjorn Andersson, Andy Gross, LKML,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-arm-msm, Mark Rutland, David Dai, Saravana Kannan,
	Viresh Kumar

Hi Sibi,

On Mon, Nov 18, 2019 at 7:45 AM Sibi Sankar <sibis@codeaurora.org> wrote:
>
> On some Qualcomm SoCs, Operating State Manager (OSM) controls the
> resources of scaling L3 caches. Add a driver to handle bandwidth
> requests to OSM L3 from CPU/GPU.
>
> Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
> ---
>  drivers/interconnect/qcom/Kconfig  |   7 +
>  drivers/interconnect/qcom/Makefile |   2 +
>  drivers/interconnect/qcom/osm-l3.c | 284 +++++++++++++++++++++++++++++
>  3 files changed, 293 insertions(+)
>  create mode 100644 drivers/interconnect/qcom/osm-l3.c
>
> diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
> index ecf057d7e2409..17aee5b0f15b7 100644
> --- a/drivers/interconnect/qcom/Kconfig
> +++ b/drivers/interconnect/qcom/Kconfig
> @@ -5,6 +5,13 @@ config INTERCONNECT_QCOM
>         help
>           Support for Qualcomm's Network-on-Chip interconnect hardware.
>
> +config INTERCONNECT_QCOM_OSM_L3
> +       tristate "Qualcomm OSM L3 interconnect driver"
> +       depends on INTERCONNECT_QCOM || COMPILE_TEST

Should we depend on something sdm845 here?

> +       help
> +         Say y here to support the Operating State Manager (OSM) interconnect
> +         driver which controls the scaling of L3 caches on Qualcomm SoCs.
> +
>  config INTERCONNECT_QCOM_QCS404
>         tristate "Qualcomm QCS404 interconnect driver"
>         depends on INTERCONNECT_QCOM
> diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
> index 9adf9e380545e..8d86d6515ffc9 100644
> --- a/drivers/interconnect/qcom/Makefile
> +++ b/drivers/interconnect/qcom/Makefile
> @@ -1,10 +1,12 @@
>  # SPDX-License-Identifier: GPL-2.0
>
> +icc-osm-l3-objs                                := osm-l3.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_OSM_L3) += icc-osm-l3.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/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c
> new file mode 100644
> index 0000000000000..5e9f9ce02863b
> --- /dev/null
> +++ b/drivers/interconnect/qcom/osm-l3.c
> @@ -0,0 +1,284 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019, The Linux Foundation. All rights reserved.
> + *
> + */
> +
> +#include <dt-bindings/interconnect/qcom,osm-l3.h>
> +#include <dt-bindings/interconnect/qcom,sdm845.h>
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/interconnect-provider.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +#define LUT_MAX_ENTRIES                        40U
> +#define LUT_SRC                                GENMASK(31, 30)
> +#define LUT_L_VAL                      GENMASK(7, 0)
> +#define LUT_ROW_SIZE                   32
> +#define CLK_HW_DIV                     2
> +
> +/* Register offsets */
> +#define REG_ENABLE                     0x0
> +#define REG_FREQ_LUT                   0x110
> +#define REG_PERF_STATE                 0x920
> +
> +#define OSM_L3_MAX_LINKS               1
> +
> +#define to_qcom_provider(_provider) \
> +       container_of(_provider, struct qcom_osm_l3_icc_provider, provider)
> +
> +enum {
> +       SDM845_MASTER_OSM_L3_APPS = SLAVE_TCU + 1,
> +       SDM845_SLAVE_OSM_L3,
> +};

Should these just go in qcom,sdm845.h? Seems nice to have them all in
one place, and then they can be accessed in the DT if needed.

> +
> +struct qcom_osm_l3_icc_provider {
> +       void __iomem *base;
> +       unsigned int max_state;
> +       unsigned long lut_tables[LUT_MAX_ENTRIES];
> +       struct icc_provider provider;
> +};
> +
> +/**
> + * struct qcom_icc_node - Qualcomm specific interconnect nodes
> + * @name: the node name used in debugfs
> + * @links: an array of nodes where we can go next while traversing
> + * @id: a unique node identifier
> + * @num_links: the total number of @links
> + * @buswidth: width of the interconnect between a node and the bus
> + */
> +struct qcom_icc_node {
> +       const char *name;
> +       u16 links[OSM_L3_MAX_LINKS];
> +       u16 id;
> +       u16 num_links;
> +       u16 buswidth;
> +};
> +
> +struct qcom_icc_desc {
> +       struct qcom_icc_node **nodes;
> +       size_t num_nodes;
> +};
> +
> +#define DEFINE_QNODE(_name, _id, _buswidth, ...)                       \
> +               static struct qcom_icc_node _name = {                   \
> +               .name = #_name,                                         \
> +               .id = _id,                                              \
> +               .buswidth = _buswidth,                                  \
> +               .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })),      \
> +               .links = { __VA_ARGS__ },                               \
> +       }
> +
> +DEFINE_QNODE(osm_apps_l3, SDM845_MASTER_OSM_L3_APPS, 16, SDM845_SLAVE_OSM_L3);
> +DEFINE_QNODE(osm_l3, SDM845_SLAVE_OSM_L3, 16);
> +
> +static struct qcom_icc_node *sdm845_osm_l3_nodes[] = {

const?

> +       [MASTER_OSM_L3_APPS] = &osm_apps_l3,
> +       [SLAVE_OSM_L3] = &osm_l3,
> +};
> +
> +static struct qcom_icc_desc sdm845_osm_l3 = {
> +       .nodes = sdm845_osm_l3_nodes,
> +       .num_nodes = ARRAY_SIZE(sdm845_osm_l3_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_t(u32, *agg_peak, peak_bw);
> +
> +       return 0;
> +}

Georgi, I wonder if it's a good idea to make a small collection of
"std" aggregate functions in the interconnect core that a driver can
just point to if it's doing something super standard like this (ie
driver->aggregate = icc_std_aggregate;). This is probably fine as-is
for now, but if we see a lot more copy/pastes of this function we
should think about it.

> +
> +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
> +{
> +       struct qcom_osm_l3_icc_provider *qp;
> +       struct icc_provider *provider;
> +       struct qcom_icc_node *qn;
> +       struct icc_node *n;
> +       unsigned int index;
> +       u32 agg_peak = 0;
> +       u32 agg_avg = 0;
> +       u64 rate;
> +
> +       qn = src->data;
> +       provider = src->provider;
> +       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);
> +
> +       rate = max(agg_avg, agg_peak);
> +       rate = icc_units_to_bps(rate);
> +       do_div(rate, qn->buswidth);
> +
> +       for (index = 0; index < qp->max_state; index++) {

If the rate is too high, you'll end up setting max_state into the
register. That's probably bad, right? (Or maybe it's not because the
table ends with the same value twice, but it seems like relying on an
implementation detail). We could guard against that by only iterating
to index < qp->max_state - 1.

> +               if (qp->lut_tables[index] >= rate)
> +                       break;
> +       }
> +
> +       writel_relaxed(index, qp->base + REG_PERF_STATE);
> +
> +       return 0;
> +}
> +
> +static int qcom_osm_l3_remove(struct platform_device *pdev)
> +{
> +       struct qcom_osm_l3_icc_provider *qp = platform_get_drvdata(pdev);
> +       struct icc_provider *provider = &qp->provider;
> +       struct icc_node *n;
> +
> +       list_for_each_entry(n, &provider->nodes, node_list) {

There was a comment on one of the other threads that we've been
copy/pasting this snippet around and it's wrong because it doesn't use
the _safe variant of the macro. So we end up destroying the list we're
iterating over.

Georgi, did you have a plan to refactor this, or should we just change
this to be the _safe version?

> +               icc_node_del(n);
> +               icc_node_destroy(n->id);
> +       }
> +
> +       return icc_provider_del(provider);
> +}
> +
> +static int qcom_osm_l3_probe(struct platform_device *pdev)
> +{
> +       u32 info, src, lval, i, prev_freq = 0, freq;
> +       static unsigned long hw_rate, xo_rate;
> +       struct qcom_osm_l3_icc_provider *qp;
> +       const struct qcom_icc_desc *desc;
> +       struct icc_onecell_data *data;
> +       struct icc_provider *provider;
> +       struct qcom_icc_node **qnodes;
> +       struct icc_node *node;
> +       size_t num_nodes;
> +       struct clk *clk;
> +       int ret;
> +
> +       clk = clk_get(&pdev->dev, "xo");
> +       if (IS_ERR(clk))
> +               return PTR_ERR(clk);
> +
> +       xo_rate = clk_get_rate(clk);
> +       clk_put(clk);
> +
> +       clk = clk_get(&pdev->dev, "alternate");
> +       if (IS_ERR(clk))
> +               return PTR_ERR(clk);
> +
> +       hw_rate = clk_get_rate(clk) / CLK_HW_DIV;

It's a little weird there's a constant divide in there, though I guess
it's in the cpufreq driver as well. I guess this is fine if it's
likely to stay there (and the same) when this driver is generalized
for other SoCs.

> +       clk_put(clk);
> +
> +       qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
> +       if (!qp)
> +               return -ENOMEM;
> +
> +       qp->base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(qp->base))
> +               return PTR_ERR(qp->base);
> +
> +       /* HW should be in enabled state to proceed */
> +       if (!(readl_relaxed(qp->base + REG_ENABLE) & 0x1)) {
> +               dev_err(&pdev->dev, "error hardware not enabled\n");
> +               return -ENODEV;
> +       }
> +
> +       for (i = 0; i < LUT_MAX_ENTRIES; i++) {
> +               info = readl_relaxed(qp->base + REG_FREQ_LUT +
> +                                    i * LUT_ROW_SIZE);
> +               src = FIELD_GET(LUT_SRC, info);
> +               lval = FIELD_GET(LUT_L_VAL, info);
> +               if (src)
> +                       freq = xo_rate * lval;
> +               else
> +                       freq = hw_rate;
> +
> +               /*
> +                * Two of the same frequencies with the same core counts means

"core counts" seems like a copied comment that doesn't apply.

But you only look at freq and not core count, is that really
equivalent to the table's boundary condition? Or do you need to be
comparing info == info_prev?

> +                * end of table
> +                */
> +               if (i > 0 && prev_freq == freq)
> +                       break;
> +
> +               qp->lut_tables[i] = freq;
> +               prev_freq = freq;
> +       }
> +       qp->max_state = i;

Should we error out or complain if there are too few entries, or if
the table is not in increasing order?

> +
> +       desc = of_device_get_match_data(&pdev->dev);
> +       if (!desc)
> +               return -EINVAL;
> +
> +       qnodes = desc->nodes;
> +       num_nodes = desc->num_nodes;
> +
> +       data = devm_kcalloc(&pdev->dev, num_nodes, sizeof(*node), GFP_KERNEL);
> +       if (!data)
> +               return -ENOMEM;
> +
> +       provider = &qp->provider;
> +       provider->dev = &pdev->dev;
> +       provider->set = qcom_icc_set;
> +       provider->aggregate = qcom_icc_aggregate;
> +       provider->xlate = of_icc_xlate_onecell;
> +       INIT_LIST_HEAD(&provider->nodes);
> +       provider->data = data;
> +
> +       ret = icc_provider_add(provider);
> +       if (ret) {
> +               dev_err(&pdev->dev, "error adding interconnect provider\n");
> +               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);
> +
> +               dev_dbg(&pdev->dev, "registered node %p %s %d\n", node,
> +                       qnodes[i]->name, node->id);
> +
> +               /* populate links */

Not a super useful comment.


> +               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 ret;
> +err:
> +       qcom_osm_l3_remove(pdev);
> +       return ret;
> +}
> +
> +static const struct of_device_id osm_l3_of_match[] = {
> +       { .compatible = "qcom,sdm845-osm-l3", .data = &sdm845_osm_l3 },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, osm_l3_of_match);
> +
> +static struct platform_driver osm_l3_driver = {
> +       .probe = qcom_osm_l3_probe,
> +       .remove = qcom_osm_l3_remove,
> +       .driver = {
> +               .name = "osm-l3",
> +               .of_match_table = osm_l3_of_match,
> +       },
> +};
> +module_platform_driver(osm_l3_driver);
> +
> +MODULE_DESCRIPTION("Qualcomm OSM L3 interconnect driver");
> +MODULE_LICENSE("GPL v2");
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 1/2] dt-bindings: interconnect: Add OSM L3 DT bindings
  2019-11-18 15:45 ` [PATCH v3 1/2] dt-bindings: interconnect: Add OSM L3 DT bindings Sibi Sankar
@ 2019-11-19  0:31   ` Stephen Boyd
  2019-11-19 10:29     ` sibis
  0 siblings, 1 reply; 9+ messages in thread
From: Stephen Boyd @ 2019-11-19  0:31 UTC (permalink / raw)
  To: Sibi Sankar, georgi.djakov, robh+dt
  Cc: bjorn.andersson, agross, linux-kernel, devicetree, linux-arm-msm,
	mark.rutland, evgreen, daidavid1, saravanak, viresh.kumar,
	Sibi Sankar

Quoting Sibi Sankar (2019-11-18 07:45:21)
> diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml
> new file mode 100644
> index 0000000000000..fec8289ceeeed
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml
> @@ -0,0 +1,56 @@
> +examples:
> +  - |
> +    osm_l3: interconnect@17d41000 {
> +      compatible = "qcom,sdm845-osm-l3";
> +      reg = <0x17d41000 0x1400>;
> +
> +      clocks = <&rpmhcc 0>, <&gcc 165>;

Can you use #define names here? That would make it clearer what sort of
clk is expected here.

> +      clock-names = "xo", "alternate";
> +
> +      #interconnect-cells = <1>;
> +    };

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 1/2] dt-bindings: interconnect: Add OSM L3 DT bindings
  2019-11-19  0:31   ` Stephen Boyd
@ 2019-11-19 10:29     ` sibis
  0 siblings, 0 replies; 9+ messages in thread
From: sibis @ 2019-11-19 10:29 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: georgi.djakov, robh+dt, bjorn.andersson, agross, linux-kernel,
	devicetree, linux-arm-msm, mark.rutland, evgreen, daidavid1,
	saravanak, viresh.kumar

Hey Stephen,

Thanks for taking time to
review the series.

On 2019-11-19 06:01, Stephen Boyd wrote:
> Quoting Sibi Sankar (2019-11-18 07:45:21)
>> diff --git 
>> a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml 
>> b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml
>> new file mode 100644
>> index 0000000000000..fec8289ceeeed
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml
>> @@ -0,0 +1,56 @@
>> +examples:
>> +  - |
>> +    osm_l3: interconnect@17d41000 {
>> +      compatible = "qcom,sdm845-osm-l3";
>> +      reg = <0x17d41000 0x1400>;
>> +
>> +      clocks = <&rpmhcc 0>, <&gcc 165>;
> 
> Can you use #define names here? That would make it clearer what sort of
> clk is expected here.

yes will do that.

> 
>> +      clock-names = "xo", "alternate";
>> +
>> +      #interconnect-cells = <1>;
>> +    };

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 2/2] interconnect: qcom: Add OSM L3 interconnect provider support
  2019-11-18 22:42   ` Evan Green
@ 2019-11-19 12:00     ` sibis
  2019-11-27  8:42       ` Sibi Sankar
  2019-11-21 12:58     ` Georgi Djakov
  1 sibling, 1 reply; 9+ messages in thread
From: sibis @ 2019-11-19 12:00 UTC (permalink / raw)
  To: Evan Green
  Cc: Rob Herring, Georgi Djakov, Bjorn Andersson, Andy Gross, LKML,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-arm-msm, Mark Rutland, David Dai, Saravana Kannan,
	Viresh Kumar

Hey Evan,
Thanks for taking time to review
the series.

On 2019-11-19 04:12, Evan Green wrote:
> Hi Sibi,
> 
> On Mon, Nov 18, 2019 at 7:45 AM Sibi Sankar <sibis@codeaurora.org> 
> wrote:
>> 
>> On some Qualcomm SoCs, Operating State Manager (OSM) controls the
>> resources of scaling L3 caches. Add a driver to handle bandwidth
>> requests to OSM L3 from CPU/GPU.
>> 
>> Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
>> ---
>>  drivers/interconnect/qcom/Kconfig  |   7 +
>>  drivers/interconnect/qcom/Makefile |   2 +
>>  drivers/interconnect/qcom/osm-l3.c | 284 
>> +++++++++++++++++++++++++++++
>>  3 files changed, 293 insertions(+)
>>  create mode 100644 drivers/interconnect/qcom/osm-l3.c
>> 
>> diff --git a/drivers/interconnect/qcom/Kconfig 
>> b/drivers/interconnect/qcom/Kconfig
>> index ecf057d7e2409..17aee5b0f15b7 100644
>> --- a/drivers/interconnect/qcom/Kconfig
>> +++ b/drivers/interconnect/qcom/Kconfig
>> @@ -5,6 +5,13 @@ config INTERCONNECT_QCOM
>>         help
>>           Support for Qualcomm's Network-on-Chip interconnect 
>> hardware.
>> 
>> +config INTERCONNECT_QCOM_OSM_L3
>> +       tristate "Qualcomm OSM L3 interconnect driver"
>> +       depends on INTERCONNECT_QCOM || COMPILE_TEST
> 
> Should we depend on something sdm845 here?

not really...

maybe I can have 845 interconnect
selecting osm_l3? since sc7180 also
would be doing the same.

> 
>> +       help
>> +         Say y here to support the Operating State Manager (OSM) 
>> interconnect
>> +         driver which controls the scaling of L3 caches on Qualcomm 
>> SoCs.
>> +
>>  config INTERCONNECT_QCOM_QCS404
>>         tristate "Qualcomm QCS404 interconnect driver"
>>         depends on INTERCONNECT_QCOM
>> diff --git a/drivers/interconnect/qcom/Makefile 
>> b/drivers/interconnect/qcom/Makefile
>> index 9adf9e380545e..8d86d6515ffc9 100644
>> --- a/drivers/interconnect/qcom/Makefile
>> +++ b/drivers/interconnect/qcom/Makefile
>> @@ -1,10 +1,12 @@
>>  # SPDX-License-Identifier: GPL-2.0
>> 
>> +icc-osm-l3-objs                                := osm-l3.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_OSM_L3) += icc-osm-l3.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/osm-l3.c 
>> b/drivers/interconnect/qcom/osm-l3.c
>> new file mode 100644
>> index 0000000000000..5e9f9ce02863b
>> --- /dev/null
>> +++ b/drivers/interconnect/qcom/osm-l3.c
>> @@ -0,0 +1,284 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2019, The Linux Foundation. All rights reserved.
>> + *
>> + */
>> +
>> +#include <dt-bindings/interconnect/qcom,osm-l3.h>
>> +#include <dt-bindings/interconnect/qcom,sdm845.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/clk.h>
>> +#include <linux/interconnect-provider.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +
>> +#define LUT_MAX_ENTRIES                        40U
>> +#define LUT_SRC                                GENMASK(31, 30)
>> +#define LUT_L_VAL                      GENMASK(7, 0)
>> +#define LUT_ROW_SIZE                   32
>> +#define CLK_HW_DIV                     2
>> +
>> +/* Register offsets */
>> +#define REG_ENABLE                     0x0
>> +#define REG_FREQ_LUT                   0x110
>> +#define REG_PERF_STATE                 0x920
>> +
>> +#define OSM_L3_MAX_LINKS               1
>> +
>> +#define to_qcom_provider(_provider) \
>> +       container_of(_provider, struct qcom_osm_l3_icc_provider, 
>> provider)
>> +
>> +enum {
>> +       SDM845_MASTER_OSM_L3_APPS = SLAVE_TCU + 1,
>> +       SDM845_SLAVE_OSM_L3,
>> +};
> 
> Should these just go in qcom,sdm845.h? Seems nice to have them all in
> one place, and then they can be accessed in the DT if needed.

yeah I can do that, by doing
this I can also get rid of the
SLAVE_TCU + 1 :)

> 
>> +
>> +struct qcom_osm_l3_icc_provider {
>> +       void __iomem *base;
>> +       unsigned int max_state;
>> +       unsigned long lut_tables[LUT_MAX_ENTRIES];
>> +       struct icc_provider provider;
>> +};
>> +
>> +/**
>> + * struct qcom_icc_node - Qualcomm specific interconnect nodes
>> + * @name: the node name used in debugfs
>> + * @links: an array of nodes where we can go next while traversing
>> + * @id: a unique node identifier
>> + * @num_links: the total number of @links
>> + * @buswidth: width of the interconnect between a node and the bus
>> + */
>> +struct qcom_icc_node {
>> +       const char *name;
>> +       u16 links[OSM_L3_MAX_LINKS];
>> +       u16 id;
>> +       u16 num_links;
>> +       u16 buswidth;
>> +};
>> +
>> +struct qcom_icc_desc {
>> +       struct qcom_icc_node **nodes;
>> +       size_t num_nodes;
>> +};
>> +
>> +#define DEFINE_QNODE(_name, _id, _buswidth, ...)                      
>>  \
>> +               static struct qcom_icc_node _name = {                  
>>  \
>> +               .name = #_name,                                        
>>  \
>> +               .id = _id,                                             
>>  \
>> +               .buswidth = _buswidth,                                 
>>  \
>> +               .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })),     
>>  \
>> +               .links = { __VA_ARGS__ },                              
>>  \
>> +       }
>> +
>> +DEFINE_QNODE(osm_apps_l3, SDM845_MASTER_OSM_L3_APPS, 16, 
>> SDM845_SLAVE_OSM_L3);
>> +DEFINE_QNODE(osm_l3, SDM845_SLAVE_OSM_L3, 16);
>> +
>> +static struct qcom_icc_node *sdm845_osm_l3_nodes[] = {
> 
> const?

unfortunately we can't ...

data->nodes[i] = node;
we setup links later ^^ with
the pointer.

> 
>> +       [MASTER_OSM_L3_APPS] = &osm_apps_l3,
>> +       [SLAVE_OSM_L3] = &osm_l3,
>> +};
>> +
>> +static struct qcom_icc_desc sdm845_osm_l3 = {
>> +       .nodes = sdm845_osm_l3_nodes,
>> +       .num_nodes = ARRAY_SIZE(sdm845_osm_l3_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_t(u32, *agg_peak, peak_bw);
>> +
>> +       return 0;
>> +}
> 
> Georgi, I wonder if it's a good idea to make a small collection of
> "std" aggregate functions in the interconnect core that a driver can
> just point to if it's doing something super standard like this (ie
> driver->aggregate = icc_std_aggregate;). This is probably fine as-is
> for now, but if we see a lot more copy/pastes of this function we
> should think about it.
> 
>> +
>> +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
>> +{
>> +       struct qcom_osm_l3_icc_provider *qp;
>> +       struct icc_provider *provider;
>> +       struct qcom_icc_node *qn;
>> +       struct icc_node *n;
>> +       unsigned int index;
>> +       u32 agg_peak = 0;
>> +       u32 agg_avg = 0;
>> +       u64 rate;
>> +
>> +       qn = src->data;
>> +       provider = src->provider;
>> +       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);
>> +
>> +       rate = max(agg_avg, agg_peak);
>> +       rate = icc_units_to_bps(rate);
>> +       do_div(rate, qn->buswidth);
>> +
>> +       for (index = 0; index < qp->max_state; index++) {
> 
> If the rate is too high, you'll end up setting max_state into the
> register. That's probably bad, right? (Or maybe it's not because the
> table ends with the same value twice, but it seems like relying on an
> implementation detail). We could guard against that by only iterating
> to index < qp->max_state - 1.

yes, using max_state - 1 makes sense
here. Will change this in the next
re-spin.

> 
>> +               if (qp->lut_tables[index] >= rate)
>> +                       break;
>> +       }
>> +
>> +       writel_relaxed(index, qp->base + REG_PERF_STATE);
>> +
>> +       return 0;
>> +}
>> +
>> +static int qcom_osm_l3_remove(struct platform_device *pdev)
>> +{
>> +       struct qcom_osm_l3_icc_provider *qp = 
>> platform_get_drvdata(pdev);
>> +       struct icc_provider *provider = &qp->provider;
>> +       struct icc_node *n;
>> +
>> +       list_for_each_entry(n, &provider->nodes, node_list) {
> 
> There was a comment on one of the other threads that we've been
> copy/pasting this snippet around and it's wrong because it doesn't use
> the _safe variant of the macro. So we end up destroying the list we're
> iterating over.
> 
> Georgi, did you have a plan to refactor this, or should we just change
> this to be the _safe version?
> 
>> +               icc_node_del(n);
>> +               icc_node_destroy(n->id);
>> +       }
>> +
>> +       return icc_provider_del(provider);
>> +}
>> +
>> +static int qcom_osm_l3_probe(struct platform_device *pdev)
>> +{
>> +       u32 info, src, lval, i, prev_freq = 0, freq;
>> +       static unsigned long hw_rate, xo_rate;
>> +       struct qcom_osm_l3_icc_provider *qp;
>> +       const struct qcom_icc_desc *desc;
>> +       struct icc_onecell_data *data;
>> +       struct icc_provider *provider;
>> +       struct qcom_icc_node **qnodes;
>> +       struct icc_node *node;
>> +       size_t num_nodes;
>> +       struct clk *clk;
>> +       int ret;
>> +
>> +       clk = clk_get(&pdev->dev, "xo");
>> +       if (IS_ERR(clk))
>> +               return PTR_ERR(clk);
>> +
>> +       xo_rate = clk_get_rate(clk);
>> +       clk_put(clk);
>> +
>> +       clk = clk_get(&pdev->dev, "alternate");
>> +       if (IS_ERR(clk))
>> +               return PTR_ERR(clk);
>> +
>> +       hw_rate = clk_get_rate(clk) / CLK_HW_DIV;
> 
> It's a little weird there's a constant divide in there, though I guess
> it's in the cpufreq driver as well. I guess this is fine if it's
> likely to stay there (and the same) when this driver is generalized
> for other SoCs.

yeah I don't see this changing
on sc7180 so I'll leave this
as is.

> 
>> +       clk_put(clk);
>> +
>> +       qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
>> +       if (!qp)
>> +               return -ENOMEM;
>> +
>> +       qp->base = devm_platform_ioremap_resource(pdev, 0);
>> +       if (IS_ERR(qp->base))
>> +               return PTR_ERR(qp->base);
>> +
>> +       /* HW should be in enabled state to proceed */
>> +       if (!(readl_relaxed(qp->base + REG_ENABLE) & 0x1)) {
>> +               dev_err(&pdev->dev, "error hardware not enabled\n");
>> +               return -ENODEV;
>> +       }
>> +
>> +       for (i = 0; i < LUT_MAX_ENTRIES; i++) {
>> +               info = readl_relaxed(qp->base + REG_FREQ_LUT +
>> +                                    i * LUT_ROW_SIZE);
>> +               src = FIELD_GET(LUT_SRC, info);
>> +               lval = FIELD_GET(LUT_L_VAL, info);
>> +               if (src)
>> +                       freq = xo_rate * lval;
>> +               else
>> +                       freq = hw_rate;
>> +
>> +               /*
>> +                * Two of the same frequencies with the same core 
>> counts means
> 
> "core counts" seems like a copied comment that doesn't apply.

yes we don't use the core_cnt
field so will update the comment.

> 
> But you only look at freq and not core count, is that really
> equivalent to the table's boundary condition? Or do you need to be
> comparing info == info_prev?

no we can get by comparing
current freq with prev freq
on OSM L3.

> 
>> +                * end of table
>> +                */
>> +               if (i > 0 && prev_freq == freq)
>> +                       break;
>> +
>> +               qp->lut_tables[i] = freq;
>> +               prev_freq = freq;
>> +       }
>> +       qp->max_state = i;
> 
> Should we error out or complain if there are too few entries, or if
> the table is not in increasing order?

OSM does make sure of the
increasing order and correct
number of freq entries if the
REG_ENABLE is set. However I
don't really mind adding the
increasing order check but we
can't really detect too few
entries since the number can
vary across SKUs.

> 
>> +
>> +       desc = of_device_get_match_data(&pdev->dev);
>> +       if (!desc)
>> +               return -EINVAL;
>> +
>> +       qnodes = desc->nodes;
>> +       num_nodes = desc->num_nodes;
>> +
>> +       data = devm_kcalloc(&pdev->dev, num_nodes, sizeof(*node), 
>> GFP_KERNEL);
>> +       if (!data)
>> +               return -ENOMEM;
>> +
>> +       provider = &qp->provider;
>> +       provider->dev = &pdev->dev;
>> +       provider->set = qcom_icc_set;
>> +       provider->aggregate = qcom_icc_aggregate;
>> +       provider->xlate = of_icc_xlate_onecell;
>> +       INIT_LIST_HEAD(&provider->nodes);
>> +       provider->data = data;
>> +
>> +       ret = icc_provider_add(provider);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "error adding interconnect 
>> provider\n");
>> +               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);
>> +
>> +               dev_dbg(&pdev->dev, "registered node %p %s %d\n", 
>> node,
>> +                       qnodes[i]->name, node->id);
>> +
>> +               /* populate links */
> 
> Not a super useful comment.

lol will remove it

> 
> 
>> +               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 ret;
>> +err:
>> +       qcom_osm_l3_remove(pdev);
>> +       return ret;
>> +}
>> +
>> +static const struct of_device_id osm_l3_of_match[] = {
>> +       { .compatible = "qcom,sdm845-osm-l3", .data = &sdm845_osm_l3 
>> },
>> +       { },
>> +};
>> +MODULE_DEVICE_TABLE(of, osm_l3_of_match);
>> +
>> +static struct platform_driver osm_l3_driver = {
>> +       .probe = qcom_osm_l3_probe,
>> +       .remove = qcom_osm_l3_remove,
>> +       .driver = {
>> +               .name = "osm-l3",
>> +               .of_match_table = osm_l3_of_match,
>> +       },
>> +};
>> +module_platform_driver(osm_l3_driver);
>> +
>> +MODULE_DESCRIPTION("Qualcomm OSM L3 interconnect driver");
>> +MODULE_LICENSE("GPL v2");
>> --
>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora 
>> Forum,
>> a Linux Foundation Collaborative Project
>> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 2/2] interconnect: qcom: Add OSM L3 interconnect provider support
  2019-11-18 22:42   ` Evan Green
  2019-11-19 12:00     ` sibis
@ 2019-11-21 12:58     ` Georgi Djakov
  1 sibling, 0 replies; 9+ messages in thread
From: Georgi Djakov @ 2019-11-21 12:58 UTC (permalink / raw)
  To: Evan Green, Sibi Sankar
  Cc: Rob Herring, Bjorn Andersson, Andy Gross, LKML,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-arm-msm, Mark Rutland, David Dai, Saravana Kannan,
	Viresh Kumar

Hi Evan,

On 11/19/19 00:42, Evan Green wrote:
> Hi Sibi,
> 
> On Mon, Nov 18, 2019 at 7:45 AM Sibi Sankar <sibis@codeaurora.org> wrote:
>>
>> On some Qualcomm SoCs, Operating State Manager (OSM) controls the
>> resources of scaling L3 caches. Add a driver to handle bandwidth
>> requests to OSM L3 from CPU/GPU.
>>
>> Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
>> ---
>>  drivers/interconnect/qcom/Kconfig  |   7 +
>>  drivers/interconnect/qcom/Makefile |   2 +
>>  drivers/interconnect/qcom/osm-l3.c | 284 +++++++++++++++++++++++++++++
>>  3 files changed, 293 insertions(+)
>>  create mode 100644 drivers/interconnect/qcom/osm-l3.c
>>
[..]
>> +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_t(u32, *agg_peak, peak_bw);
>> +
>> +       return 0;
>> +}
> 
> Georgi, I wonder if it's a good idea to make a small collection of
> "std" aggregate functions in the interconnect core that a driver can
> just point to if it's doing something super standard like this (ie
> driver->aggregate = icc_std_aggregate;). This is probably fine as-is
> for now, but if we see a lot more copy/pastes of this function we
> should think about it.

Sure, thanks for the suggestion! Will submit a patch for this.

> 
>> +
>> +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
>> +{
[..]
>> +
>> +static int qcom_osm_l3_remove(struct platform_device *pdev)
>> +{
>> +       struct qcom_osm_l3_icc_provider *qp = platform_get_drvdata(pdev);
>> +       struct icc_provider *provider = &qp->provider;
>> +       struct icc_node *n;
>> +
>> +       list_for_each_entry(n, &provider->nodes, node_list) {
> 
> There was a comment on one of the other threads that we've been
> copy/pasting this snippet around and it's wrong because it doesn't use
> the _safe variant of the macro. So we end up destroying the list we're
> iterating over.
> 
> Georgi, did you have a plan to refactor this, or should we just change
> this to be the _safe version?

I have fixes that convert this to the _safe variant (for stable) and also
factor this out into icc_nodes_remove() function. Will post them.

Thanks,
Georgi

> 
>> +               icc_node_del(n);
>> +               icc_node_destroy(n->id);
>> +       }
>> +
>> +       return icc_provider_del(provider);
>> +}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 2/2] interconnect: qcom: Add OSM L3 interconnect provider support
  2019-11-19 12:00     ` sibis
@ 2019-11-27  8:42       ` Sibi Sankar
  2019-12-06 19:16         ` Evan Green
  0 siblings, 1 reply; 9+ messages in thread
From: Sibi Sankar @ 2019-11-27  8:42 UTC (permalink / raw)
  To: Evan Green
  Cc: Rob Herring, Georgi Djakov, Bjorn Andersson, Andy Gross, LKML,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-arm-msm, Mark Rutland, David Dai, Saravana Kannan,
	Viresh Kumar, linux-kernel-owner

Hey Evan/Georgi,

https://git.linaro.org/people/georgi.djakov/linux.git/commit/?h=icc-dev&id=9197da7d06e88666d1588e3c21a743e60381264d

With the "Redefine interconnect provider
DT nodes for SDM845" series, wouldn't it
make more sense to define the OSM_L3 icc
nodes in the sdm845.c icc driver and have
the common helpers in osm_l3 driver? Though
we don't plan on linking the OSM L3 nodes
to the other nodes on SDM845/SC7180, we
might have GPU needing to be linked to the
OSM L3 nodes on future SoCs. Let me know
how you want this done.

Anyway I'll re-spin the series once the
SDM845 icc re-work gets re-posted.

On 2019-11-19 17:30, sibis@codeaurora.org wrote:
> Hey Evan,
> Thanks for taking time to review
> the series.
> 
> On 2019-11-19 04:12, Evan Green wrote:
>> Hi Sibi,
>> 
>> On Mon, Nov 18, 2019 at 7:45 AM Sibi Sankar <sibis@codeaurora.org> 
>> wrote:
>>> 
>>> On some Qualcomm SoCs, Operating State Manager (OSM) controls the
>>> resources of scaling L3 caches. Add a driver to handle bandwidth
>>> requests to OSM L3 from CPU/GPU.
>>> 
>>> Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
>>> ---
>>>  drivers/interconnect/qcom/Kconfig  |   7 +
>>>  drivers/interconnect/qcom/Makefile |   2 +
>>>  drivers/interconnect/qcom/osm-l3.c | 284 
>>> +++++++++++++++++++++++++++++
>>>  3 files changed, 293 insertions(+)
>>>  create mode 100644 drivers/interconnect/qcom/osm-l3.c
>>> 
>>> diff --git a/drivers/interconnect/qcom/Kconfig 
>>> b/drivers/interconnect/qcom/Kconfig
>>> index ecf057d7e2409..17aee5b0f15b7 100644
>>> --- a/drivers/interconnect/qcom/Kconfig
>>> +++ b/drivers/interconnect/qcom/Kconfig
>>> @@ -5,6 +5,13 @@ config INTERCONNECT_QCOM
>>>         help
>>>           Support for Qualcomm's Network-on-Chip interconnect 
>>> hardware.
>>> 
>>> +config INTERCONNECT_QCOM_OSM_L3
>>> +       tristate "Qualcomm OSM L3 interconnect driver"
>>> +       depends on INTERCONNECT_QCOM || COMPILE_TEST
>> 
>> Should we depend on something sdm845 here?
> 
> not really...
> 
> maybe I can have 845 interconnect
> selecting osm_l3? since sc7180 also
> would be doing the same.
> 
>> 
>>> +       help
>>> +         Say y here to support the Operating State Manager (OSM) 
>>> interconnect
>>> +         driver which controls the scaling of L3 caches on Qualcomm 
>>> SoCs.
>>> +
>>>  config INTERCONNECT_QCOM_QCS404
>>>         tristate "Qualcomm QCS404 interconnect driver"
>>>         depends on INTERCONNECT_QCOM
>>> diff --git a/drivers/interconnect/qcom/Makefile 
>>> b/drivers/interconnect/qcom/Makefile
>>> index 9adf9e380545e..8d86d6515ffc9 100644
>>> --- a/drivers/interconnect/qcom/Makefile
>>> +++ b/drivers/interconnect/qcom/Makefile
>>> @@ -1,10 +1,12 @@
>>>  # SPDX-License-Identifier: GPL-2.0
>>> 
>>> +icc-osm-l3-objs                                := osm-l3.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_OSM_L3) += icc-osm-l3.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/osm-l3.c 
>>> b/drivers/interconnect/qcom/osm-l3.c
>>> new file mode 100644
>>> index 0000000000000..5e9f9ce02863b
>>> --- /dev/null
>>> +++ b/drivers/interconnect/qcom/osm-l3.c
>>> @@ -0,0 +1,284 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (c) 2019, The Linux Foundation. All rights reserved.
>>> + *
>>> + */
>>> +
>>> +#include <dt-bindings/interconnect/qcom,osm-l3.h>
>>> +#include <dt-bindings/interconnect/qcom,sdm845.h>
>>> +#include <linux/bitfield.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/interconnect-provider.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/platform_device.h>
>>> +
>>> +#define LUT_MAX_ENTRIES                        40U
>>> +#define LUT_SRC                                GENMASK(31, 30)
>>> +#define LUT_L_VAL                      GENMASK(7, 0)
>>> +#define LUT_ROW_SIZE                   32
>>> +#define CLK_HW_DIV                     2
>>> +
>>> +/* Register offsets */
>>> +#define REG_ENABLE                     0x0
>>> +#define REG_FREQ_LUT                   0x110
>>> +#define REG_PERF_STATE                 0x920
>>> +
>>> +#define OSM_L3_MAX_LINKS               1
>>> +
>>> +#define to_qcom_provider(_provider) \
>>> +       container_of(_provider, struct qcom_osm_l3_icc_provider, 
>>> provider)
>>> +
>>> +enum {
>>> +       SDM845_MASTER_OSM_L3_APPS = SLAVE_TCU + 1,
>>> +       SDM845_SLAVE_OSM_L3,
>>> +};
>> 
>> Should these just go in qcom,sdm845.h? Seems nice to have them all in
>> one place, and then they can be accessed in the DT if needed.
> 
> yeah I can do that, by doing
> this I can also get rid of the
> SLAVE_TCU + 1 :)
> 
>> 
>>> +
>>> +struct qcom_osm_l3_icc_provider {
>>> +       void __iomem *base;
>>> +       unsigned int max_state;
>>> +       unsigned long lut_tables[LUT_MAX_ENTRIES];
>>> +       struct icc_provider provider;
>>> +};
>>> +
>>> +/**
>>> + * struct qcom_icc_node - Qualcomm specific interconnect nodes
>>> + * @name: the node name used in debugfs
>>> + * @links: an array of nodes where we can go next while traversing
>>> + * @id: a unique node identifier
>>> + * @num_links: the total number of @links
>>> + * @buswidth: width of the interconnect between a node and the bus
>>> + */
>>> +struct qcom_icc_node {
>>> +       const char *name;
>>> +       u16 links[OSM_L3_MAX_LINKS];
>>> +       u16 id;
>>> +       u16 num_links;
>>> +       u16 buswidth;
>>> +};
>>> +
>>> +struct qcom_icc_desc {
>>> +       struct qcom_icc_node **nodes;
>>> +       size_t num_nodes;
>>> +};
>>> +
>>> +#define DEFINE_QNODE(_name, _id, _buswidth, ...)                     
>>>   \
>>> +               static struct qcom_icc_node _name = {                 
>>>   \
>>> +               .name = #_name,                                       
>>>   \
>>> +               .id = _id,                                            
>>>   \
>>> +               .buswidth = _buswidth,                                
>>>   \
>>> +               .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })),    
>>>   \
>>> +               .links = { __VA_ARGS__ },                             
>>>   \
>>> +       }
>>> +
>>> +DEFINE_QNODE(osm_apps_l3, SDM845_MASTER_OSM_L3_APPS, 16, 
>>> SDM845_SLAVE_OSM_L3);
>>> +DEFINE_QNODE(osm_l3, SDM845_SLAVE_OSM_L3, 16);
>>> +
>>> +static struct qcom_icc_node *sdm845_osm_l3_nodes[] = {
>> 
>> const?
> 
> unfortunately we can't ...
> 
> data->nodes[i] = node;
> we setup links later ^^ with
> the pointer.
> 
>> 
>>> +       [MASTER_OSM_L3_APPS] = &osm_apps_l3,
>>> +       [SLAVE_OSM_L3] = &osm_l3,
>>> +};
>>> +
>>> +static struct qcom_icc_desc sdm845_osm_l3 = {
>>> +       .nodes = sdm845_osm_l3_nodes,
>>> +       .num_nodes = ARRAY_SIZE(sdm845_osm_l3_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_t(u32, *agg_peak, peak_bw);
>>> +
>>> +       return 0;
>>> +}
>> 
>> Georgi, I wonder if it's a good idea to make a small collection of
>> "std" aggregate functions in the interconnect core that a driver can
>> just point to if it's doing something super standard like this (ie
>> driver->aggregate = icc_std_aggregate;). This is probably fine as-is
>> for now, but if we see a lot more copy/pastes of this function we
>> should think about it.
>> 
>>> +
>>> +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
>>> +{
>>> +       struct qcom_osm_l3_icc_provider *qp;
>>> +       struct icc_provider *provider;
>>> +       struct qcom_icc_node *qn;
>>> +       struct icc_node *n;
>>> +       unsigned int index;
>>> +       u32 agg_peak = 0;
>>> +       u32 agg_avg = 0;
>>> +       u64 rate;
>>> +
>>> +       qn = src->data;
>>> +       provider = src->provider;
>>> +       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);
>>> +
>>> +       rate = max(agg_avg, agg_peak);
>>> +       rate = icc_units_to_bps(rate);
>>> +       do_div(rate, qn->buswidth);
>>> +
>>> +       for (index = 0; index < qp->max_state; index++) {
>> 
>> If the rate is too high, you'll end up setting max_state into the
>> register. That's probably bad, right? (Or maybe it's not because the
>> table ends with the same value twice, but it seems like relying on an
>> implementation detail). We could guard against that by only iterating
>> to index < qp->max_state - 1.
> 
> yes, using max_state - 1 makes sense
> here. Will change this in the next
> re-spin.
> 
>> 
>>> +               if (qp->lut_tables[index] >= rate)
>>> +                       break;
>>> +       }
>>> +
>>> +       writel_relaxed(index, qp->base + REG_PERF_STATE);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int qcom_osm_l3_remove(struct platform_device *pdev)
>>> +{
>>> +       struct qcom_osm_l3_icc_provider *qp = 
>>> platform_get_drvdata(pdev);
>>> +       struct icc_provider *provider = &qp->provider;
>>> +       struct icc_node *n;
>>> +
>>> +       list_for_each_entry(n, &provider->nodes, node_list) {
>> 
>> There was a comment on one of the other threads that we've been
>> copy/pasting this snippet around and it's wrong because it doesn't use
>> the _safe variant of the macro. So we end up destroying the list we're
>> iterating over.
>> 
>> Georgi, did you have a plan to refactor this, or should we just change
>> this to be the _safe version?
>> 
>>> +               icc_node_del(n);
>>> +               icc_node_destroy(n->id);
>>> +       }
>>> +
>>> +       return icc_provider_del(provider);
>>> +}
>>> +
>>> +static int qcom_osm_l3_probe(struct platform_device *pdev)
>>> +{
>>> +       u32 info, src, lval, i, prev_freq = 0, freq;
>>> +       static unsigned long hw_rate, xo_rate;
>>> +       struct qcom_osm_l3_icc_provider *qp;
>>> +       const struct qcom_icc_desc *desc;
>>> +       struct icc_onecell_data *data;
>>> +       struct icc_provider *provider;
>>> +       struct qcom_icc_node **qnodes;
>>> +       struct icc_node *node;
>>> +       size_t num_nodes;
>>> +       struct clk *clk;
>>> +       int ret;
>>> +
>>> +       clk = clk_get(&pdev->dev, "xo");
>>> +       if (IS_ERR(clk))
>>> +               return PTR_ERR(clk);
>>> +
>>> +       xo_rate = clk_get_rate(clk);
>>> +       clk_put(clk);
>>> +
>>> +       clk = clk_get(&pdev->dev, "alternate");
>>> +       if (IS_ERR(clk))
>>> +               return PTR_ERR(clk);
>>> +
>>> +       hw_rate = clk_get_rate(clk) / CLK_HW_DIV;
>> 
>> It's a little weird there's a constant divide in there, though I guess
>> it's in the cpufreq driver as well. I guess this is fine if it's
>> likely to stay there (and the same) when this driver is generalized
>> for other SoCs.
> 
> yeah I don't see this changing
> on sc7180 so I'll leave this
> as is.
> 
>> 
>>> +       clk_put(clk);
>>> +
>>> +       qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
>>> +       if (!qp)
>>> +               return -ENOMEM;
>>> +
>>> +       qp->base = devm_platform_ioremap_resource(pdev, 0);
>>> +       if (IS_ERR(qp->base))
>>> +               return PTR_ERR(qp->base);
>>> +
>>> +       /* HW should be in enabled state to proceed */
>>> +       if (!(readl_relaxed(qp->base + REG_ENABLE) & 0x1)) {
>>> +               dev_err(&pdev->dev, "error hardware not enabled\n");
>>> +               return -ENODEV;
>>> +       }
>>> +
>>> +       for (i = 0; i < LUT_MAX_ENTRIES; i++) {
>>> +               info = readl_relaxed(qp->base + REG_FREQ_LUT +
>>> +                                    i * LUT_ROW_SIZE);
>>> +               src = FIELD_GET(LUT_SRC, info);
>>> +               lval = FIELD_GET(LUT_L_VAL, info);
>>> +               if (src)
>>> +                       freq = xo_rate * lval;
>>> +               else
>>> +                       freq = hw_rate;
>>> +
>>> +               /*
>>> +                * Two of the same frequencies with the same core 
>>> counts means
>> 
>> "core counts" seems like a copied comment that doesn't apply.
> 
> yes we don't use the core_cnt
> field so will update the comment.
> 
>> 
>> But you only look at freq and not core count, is that really
>> equivalent to the table's boundary condition? Or do you need to be
>> comparing info == info_prev?
> 
> no we can get by comparing
> current freq with prev freq
> on OSM L3.
> 
>> 
>>> +                * end of table
>>> +                */
>>> +               if (i > 0 && prev_freq == freq)
>>> +                       break;
>>> +
>>> +               qp->lut_tables[i] = freq;
>>> +               prev_freq = freq;
>>> +       }
>>> +       qp->max_state = i;
>> 
>> Should we error out or complain if there are too few entries, or if
>> the table is not in increasing order?
> 
> OSM does make sure of the
> increasing order and correct
> number of freq entries if the
> REG_ENABLE is set. However I
> don't really mind adding the
> increasing order check but we
> can't really detect too few
> entries since the number can
> vary across SKUs.
> 
>> 
>>> +
>>> +       desc = of_device_get_match_data(&pdev->dev);
>>> +       if (!desc)
>>> +               return -EINVAL;
>>> +
>>> +       qnodes = desc->nodes;
>>> +       num_nodes = desc->num_nodes;
>>> +
>>> +       data = devm_kcalloc(&pdev->dev, num_nodes, sizeof(*node), 
>>> GFP_KERNEL);
>>> +       if (!data)
>>> +               return -ENOMEM;
>>> +
>>> +       provider = &qp->provider;
>>> +       provider->dev = &pdev->dev;
>>> +       provider->set = qcom_icc_set;
>>> +       provider->aggregate = qcom_icc_aggregate;
>>> +       provider->xlate = of_icc_xlate_onecell;
>>> +       INIT_LIST_HEAD(&provider->nodes);
>>> +       provider->data = data;
>>> +
>>> +       ret = icc_provider_add(provider);
>>> +       if (ret) {
>>> +               dev_err(&pdev->dev, "error adding interconnect 
>>> provider\n");
>>> +               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);
>>> +
>>> +               dev_dbg(&pdev->dev, "registered node %p %s %d\n", 
>>> node,
>>> +                       qnodes[i]->name, node->id);
>>> +
>>> +               /* populate links */
>> 
>> Not a super useful comment.
> 
> lol will remove it
> 
>> 
>> 
>>> +               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 ret;
>>> +err:
>>> +       qcom_osm_l3_remove(pdev);
>>> +       return ret;
>>> +}
>>> +
>>> +static const struct of_device_id osm_l3_of_match[] = {
>>> +       { .compatible = "qcom,sdm845-osm-l3", .data = &sdm845_osm_l3 
>>> },
>>> +       { },
>>> +};
>>> +MODULE_DEVICE_TABLE(of, osm_l3_of_match);
>>> +
>>> +static struct platform_driver osm_l3_driver = {
>>> +       .probe = qcom_osm_l3_probe,
>>> +       .remove = qcom_osm_l3_remove,
>>> +       .driver = {
>>> +               .name = "osm-l3",
>>> +               .of_match_table = osm_l3_of_match,
>>> +       },
>>> +};
>>> +module_platform_driver(osm_l3_driver);
>>> +
>>> +MODULE_DESCRIPTION("Qualcomm OSM L3 interconnect driver");
>>> +MODULE_LICENSE("GPL v2");
>>> --
>>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora 
>>> Forum,
>>> a Linux Foundation Collaborative Project
>>> 

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 2/2] interconnect: qcom: Add OSM L3 interconnect provider support
  2019-11-27  8:42       ` Sibi Sankar
@ 2019-12-06 19:16         ` Evan Green
  0 siblings, 0 replies; 9+ messages in thread
From: Evan Green @ 2019-12-06 19:16 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Rob Herring, Georgi Djakov, Bjorn Andersson, Andy Gross, LKML,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-arm-msm, Mark Rutland, David Dai, Saravana Kannan,
	Viresh Kumar, linux-kernel-owner

On Wed, Nov 27, 2019 at 12:42 AM Sibi Sankar <sibis@codeaurora.org> wrote:
>
> Hey Evan/Georgi,
>
> https://git.linaro.org/people/georgi.djakov/linux.git/commit/?h=icc-dev&id=9197da7d06e88666d1588e3c21a743e60381264d
>
> With the "Redefine interconnect provider
> DT nodes for SDM845" series, wouldn't it
> make more sense to define the OSM_L3 icc
> nodes in the sdm845.c icc driver and have
> the common helpers in osm_l3 driver? Though
> we don't plan on linking the OSM L3 nodes
> to the other nodes on SDM845/SC7180, we
> might have GPU needing to be linked to the
> OSM L3 nodes on future SoCs. Let me know
> how you want this done.
>
> Anyway I'll re-spin the series once the
> SDM845 icc re-work gets re-posted.

I don't have a clear picture of the proposal. You'd put the couple of
extra defines in sdm845.c for the new nodes. But then you'd need to do
something in icc_set() of sdm845. Is that when you'd call out to the
osm_l3 driver?

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, back to index

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20191118154435.20357-1-sibis@codeaurora.org>
2019-11-18 15:45 ` [PATCH v3 1/2] dt-bindings: interconnect: Add OSM L3 DT bindings Sibi Sankar
2019-11-19  0:31   ` Stephen Boyd
2019-11-19 10:29     ` sibis
2019-11-18 15:45 ` [PATCH v3 2/2] interconnect: qcom: Add OSM L3 interconnect provider support Sibi Sankar
     [not found] ` <0101016e7f30ad15-18908ef0-a2b9-4a2a-bf32-6cb3aa447b01-000000@us-west-2.amazonses.com>
2019-11-18 22:42   ` Evan Green
2019-11-19 12:00     ` sibis
2019-11-27  8:42       ` Sibi Sankar
2019-12-06 19:16         ` Evan Green
2019-11-21 12:58     ` Georgi Djakov

Linux-ARM-MSM Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-arm-msm/0 linux-arm-msm/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-arm-msm linux-arm-msm/ https://lore.kernel.org/linux-arm-msm \
		linux-arm-msm@vger.kernel.org
	public-inbox-index linux-arm-msm

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-arm-msm


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git