linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v1 0/2] Add QCOM SDM845 interconnect provider driver
@ 2018-07-19  2:36 David Dai
  2018-07-19  2:36 ` [RFC PATCH v2 1/2] interconnect: qcom: Add sdm845 " David Dai
  2018-07-19  2:37 ` [RFC PATCH v2 2/2] arm64: dts: sdm845: Add interconnect provider DT nodes David Dai
  0 siblings, 2 replies; 7+ messages in thread
From: David Dai @ 2018-07-19  2:36 UTC (permalink / raw)
  To: linux-kernel, linux-pm, georgi.djakov, vincent.guittot,
	bjorn.andersson, daidavid1, amit.kucheria, ilina, seansw,
	grahamr

Hi,

This patch series adds a driver and DT binding using the interconnect (ICC)
framework [1] to describe the Qualcomm SDM845 platform's topology of its
interconnected buses and internal aggregation nodes known as
Bus Clock Managers(BCM). The SDM845 ICC provider driver would aggregate and
satisfy consumer requests accross the SoC by generating commands that
communicate with the BCM through the Resource Power Manager (RPMh) driver [2]
interface. The SDM845 ICC driver also configures QoS settings specific to each
node to ensure clients have proper priority.

The SDM845 interconnect provider has dependencies on the RPMh driver
and Command DB driver [3].

Changes in v2 [5]:
 - Updated to use the latest RPMH and CommandDB patches
 - Fixed bug in bcm aggregation
 - Updated sdm845 provider dt entry

Changes in v1 [4]:
 - Addressed review feedback from Georgi D. and Evan G.
 - Removed proposal to modify ICC aggregate callback interface
 - Moved BCM aggregation into provider set function
 - Added devicetree binding documentation
 - Fixed logic in generating TCS command list
 - Added keepalive flags for resources that are critical to platform operation
 - Added various comments to clarify intent
 - Removed unused types and struct definitions

[1]: https://lkml.org/lkml/2018/6/20/453
[2]: https://lkml.org/lkml/2018/6/20/519
[3]: https://lkml.org/lkml/2018/4/10/714
[4]: https://patchwork.kernel.org/patch/10428167/
[5]: https://lkml.org/lkml/2018/6/29/743

Summary of the patches:
Patch 1 creates the Qualcomm SDM845 Specific provider driver.
Patch 2 Adds dt binding for SDM845 provider

TODO:
  * Expand aggregation/provider to handle clients with active only requirements.
  * Add Network-on-Chip (NoC) objects to encapsulate logical nodes for QoS.
  * Add QoS configuration specific to each NoC.

David Dai (2):
  interconnect: qcom: Add sdm845 interconnect provider driver
  arm64: dts: sdm845: Add interconnect provider DT nodes

 .../bindings/interconnect/qcom-sdm845.txt          |  22 +
 arch/arm64/boot/dts/qcom/sdm845.dtsi               |   5 +
 drivers/interconnect/qcom/Kconfig                  |   8 +
 drivers/interconnect/qcom/Makefile                 |   1 +
 drivers/interconnect/qcom/qcom-icc-ids.h           | 142 ++++
 drivers/interconnect/qcom/sdm845.c                 | 826 +++++++++++++++++++++
 6 files changed, 1004 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interconnect/qcom-sdm845.txt
 create mode 100644 drivers/interconnect/qcom/qcom-icc-ids.h
 create mode 100644 drivers/interconnect/qcom/sdm845.c

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


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

* [RFC PATCH v2 1/2] interconnect: qcom: Add sdm845 interconnect provider driver
  2018-07-19  2:36 [RFC PATCH v1 0/2] Add QCOM SDM845 interconnect provider driver David Dai
@ 2018-07-19  2:36 ` David Dai
  2018-07-27 21:12   ` Evan Green
  2018-08-06 22:52   ` Bjorn Andersson
  2018-07-19  2:37 ` [RFC PATCH v2 2/2] arm64: dts: sdm845: Add interconnect provider DT nodes David Dai
  1 sibling, 2 replies; 7+ messages in thread
From: David Dai @ 2018-07-19  2:36 UTC (permalink / raw)
  To: linux-kernel, linux-pm, georgi.djakov, vincent.guittot,
	bjorn.andersson, daidavid1, amit.kucheria, ilina, seansw,
	grahamr

Introduce Qualcomm SDM845 specific provider driver using the
interconnect framework.

Signed-off-by: David Dai <daidavid1@codeaurora.org>
---
 .../bindings/interconnect/qcom-sdm845.txt          |  22 +
 drivers/interconnect/qcom/Kconfig                  |   8 +
 drivers/interconnect/qcom/Makefile                 |   1 +
 drivers/interconnect/qcom/qcom-icc-ids.h           | 142 ++++
 drivers/interconnect/qcom/sdm845.c                 | 826 +++++++++++++++++++++
 5 files changed, 999 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interconnect/qcom-sdm845.txt
 create mode 100644 drivers/interconnect/qcom/qcom-icc-ids.h
 create mode 100644 drivers/interconnect/qcom/sdm845.c

diff --git a/Documentation/devicetree/bindings/interconnect/qcom-sdm845.txt b/Documentation/devicetree/bindings/interconnect/qcom-sdm845.txt
new file mode 100644
index 0000000..6248523
--- /dev/null
+++ b/Documentation/devicetree/bindings/interconnect/qcom-sdm845.txt
@@ -0,0 +1,22 @@
+Qualcomm SDM845 Network-On-Chip interconnect driver binding
+----------------------------------------------------
+
+SDM845 interconnect providers support system bandwidth requirements through
+RPMh hardware accelerators known as Bus Clock Manager(BCM). The provider is able
+to communicate with the BCM through the Resource State Coordinator(RSC)
+associated with each execution environment. Provider nodes must reside within
+an RPMh device node pertaining to their RSC and each provider maps to
+a single RPMh resource.
+
+Required properties :
+- compatible : shall contain only one of the following:
+			"qcom,sdm845-rsc-hlos"
+
+Examples:
+
+apps_rsc: rsc {
+		qnoc: qnoc-rsc-hlos {
+			compatible = "qcom,sdm845-rsc-hlos";
+		};
+};
+
diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
index c858218..2cd3603 100644
--- a/drivers/interconnect/qcom/Kconfig
+++ b/drivers/interconnect/qcom/Kconfig
@@ -17,3 +17,11 @@ config INTERCONNECT_QCOM_MSM8916
 	help
 	  This is a driver for the Qualcomm Network-on-Chip on msm8916-based
 	  platforms.
+
+config INTERCONNECT_QCOM_SDM845
+	tristate "Qualcomm SDM845 interconnect driver"
+	depends on INTERCONNECT_QCOM
+	depends on (QCOM_RPMH && QCOM_COMMAND_DB && OF) || COMPILE_TEST
+	help
+	  This is a driver for the Qualcomm Network-on-Chip on sdm845-based
+	  platforms.
diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
index 53f3380..a0a5056 100644
--- a/drivers/interconnect/qcom/Makefile
+++ b/drivers/interconnect/qcom/Makefile
@@ -2,3 +2,4 @@
 obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += smd-rpm.o
 
 obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += msm8916.o
+obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += sdm845.o
diff --git a/drivers/interconnect/qcom/qcom-icc-ids.h b/drivers/interconnect/qcom/qcom-icc-ids.h
new file mode 100644
index 0000000..5bd0db2
--- /dev/null
+++ b/drivers/interconnect/qcom/qcom-icc-ids.h
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#ifndef	_QCOM_ICC_IDS_H
+#define	_QCOM_ICC_IDS_H
+
+#define	MASTER_APPSS_PROC	0
+#define	MASTER_TCU_0	1
+#define	MASTER_IPA_CORE	2
+#define	MASTER_LLCC	3
+#define	MASTER_GNOC_CFG	4
+#define	MASTER_A1NOC_CFG	5
+#define	MASTER_A2NOC_CFG	6
+#define	MASTER_CNOC_DC_NOC	7
+#define	MASTER_MEM_NOC_CFG	8
+#define	MASTER_CNOC_MNOC_CFG	9
+#define	MASTER_QDSS_BAM	10
+#define	MASTER_BLSP_1	11
+#define	MASTER_BLSP_2	12
+#define	MASTER_SNOC_CFG	13
+#define	MASTER_SPDM	14
+#define	MASTER_TIC	15
+#define	MASTER_TSIF	16
+#define	MASTER_A1NOC_SNOC	17
+#define	MASTER_A2NOC_SNOC	18
+#define	MASTER_GNOC_MEM_NOC	19
+#define	MASTER_CNOC_A2NOC	20
+#define	MASTER_GNOC_SNOC	21
+#define	MASTER_MEM_NOC_SNOC	22
+#define	MASTER_MNOC_HF_MEM_NOC	23
+#define	MASTER_MNOC_SF_MEM_NOC	24
+#define	MASTER_ANOC_PCIE_SNOC	25
+#define	MASTER_SNOC_CNOC	26
+#define	MASTER_SNOC_GC_MEM_NOC	27
+#define	MASTER_SNOC_SF_MEM_NOC	28
+#define	MASTER_CAMNOC_HF0	29
+#define	MASTER_CAMNOC_HF0_UNCOMP	30
+#define	MASTER_CAMNOC_HF1	31
+#define	MASTER_CAMNOC_HF1_UNCOMP	32
+#define	MASTER_CAMNOC_SF	33
+#define	MASTER_CAMNOC_SF_UNCOMP	34
+#define	MASTER_CRYPTO	35
+#define	MASTER_GFX3D	36
+#define	MASTER_IPA	37
+#define	MASTER_MDP0	38
+#define	MASTER_MDP1	39
+#define	MASTER_PIMEM	40
+#define	MASTER_ROTATOR	41
+#define	MASTER_VIDEO_P0	42
+#define	MASTER_VIDEO_P1	43
+#define	MASTER_VIDEO_PROC	44
+#define	MASTER_GIC	45
+#define	MASTER_PCIE_1	46
+#define	MASTER_PCIE_0	47
+#define	MASTER_QDSS_DAP	48
+#define	MASTER_QDSS_ETR	49
+#define	MASTER_SDCC_2	50
+#define	MASTER_SDCC_4	51
+#define	MASTER_UFS_CARD	52
+#define	MASTER_UFS_MEM	53
+#define	MASTER_USB3_0	54
+#define	MASTER_USB3_1	55
+#define	SLAVE_EBI1	512
+#define	SLAVE_IPA_CORE	513
+#define	SLAVE_A1NOC_CFG	514
+#define	SLAVE_A2NOC_CFG	515
+#define	SLAVE_AOP	516
+#define	SLAVE_AOSS	517
+#define	SLAVE_APPSS	518
+#define	SLAVE_CAMERA_CFG	519
+#define	SLAVE_CLK_CTL	520
+#define	SLAVE_CDSP_CFG	521
+#define	SLAVE_RBCPR_CX_CFG	522
+#define	SLAVE_CRYPTO_0_CFG	523
+#define	SLAVE_DCC_CFG	524
+#define	SLAVE_CNOC_DDRSS	525
+#define	SLAVE_DISPLAY_CFG	526
+#define	SLAVE_GLM	527
+#define	SLAVE_GFX3D_CFG	528
+#define	SLAVE_IMEM_CFG	529
+#define	SLAVE_IPA_CFG	530
+#define	SLAVE_LLCC_CFG	531
+#define	SLAVE_MSS_PROC_MS_MPU_CFG	532
+#define	SLAVE_MEM_NOC_CFG	533
+#define	SLAVE_CNOC_MNOC_CFG	534
+#define	SLAVE_PCIE_0_CFG	535
+#define	SLAVE_PCIE_1_CFG	536
+#define	SLAVE_PDM	537
+#define	SLAVE_SOUTH_PHY_CFG	538
+#define	SLAVE_PIMEM_CFG	539
+#define	SLAVE_PRNG	540
+#define	SLAVE_QDSS_CFG	541
+#define	SLAVE_BLSP_2	542
+#define	SLAVE_BLSP_1	543
+#define	SLAVE_SDCC_2	544
+#define	SLAVE_SDCC_4	545
+#define	SLAVE_SNOC_CFG	546
+#define	SLAVE_SPDM_WRAPPER	547
+#define	SLAVE_SPSS_CFG	548
+#define	SLAVE_TCSR	549
+#define	SLAVE_TLMM_NORTH	550
+#define	SLAVE_TLMM_SOUTH	551
+#define	SLAVE_TSIF	552
+#define	SLAVE_UFS_CARD_CFG	553
+#define	SLAVE_UFS_MEM_CFG	554
+#define	SLAVE_USB3_0	555
+#define	SLAVE_USB3_1	556
+#define	SLAVE_VENUS_CFG	557
+#define	SLAVE_VSENSE_CTRL_CFG	558
+#define	SLAVE_MNOC_SF_MEM_NOC	559
+#define	SLAVE_A1NOC_SNOC	560
+#define	SLAVE_A2NOC_SNOC	561
+#define	SLAVE_MEM_NOC_GNOC	562
+#define	SLAVE_CAMNOC_UNCOMP	563
+#define	SLAVE_SNOC_CNOC	564
+#define	SLAVE_CNOC_A2NOC	565
+#define	SLAVE_GNOC_SNOC	566
+#define	SLAVE_GNOC_MEM_NOC	567
+#define	SLAVE_LLCC	568
+#define	SLAVE_MNOC_HF_MEM_NOC	569
+#define	SLAVE_SNOC_MEM_NOC_GC	570
+#define	SLAVE_SNOC_MEM_NOC_SF	571
+#define	SLAVE_MEM_NOC_SNOC	572
+#define	SLAVE_ANOC_PCIE_A1NOC_SNOC	573
+#define	SLAVE_ANOC_PCIE_SNOC	574
+#define	SLAVE_IMEM	575
+#define	SLAVE_PCIE_0	576
+#define	SLAVE_PCIE_1	577
+#define	SLAVE_PIMEM	578
+#define	SLAVE_SERVICE_A1NOC	579
+#define	SLAVE_SERVICE_A2NOC	580
+#define	SLAVE_SERVICE_CNOC	581
+#define	SLAVE_SERVICE_GNOC	582
+#define	SLAVE_SERVICE_MEM_NOC	583
+#define	SLAVE_SERVICE_MNOC	584
+#define	SLAVE_SERVICE_SNOC	585
+#define	SLAVE_QDSS_STM	586
+#define	SLAVE_TCU	587
+#endif
diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c
new file mode 100644
index 0000000..bf13053
--- /dev/null
+++ b/drivers/interconnect/qcom/sdm845.c
@@ -0,0 +1,826 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/interconnect.h>
+#include <linux/interconnect-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/rpmh.h>
+#include <soc/qcom/tcs.h>
+
+#include "qcom-icc-ids.h"
+
+#define BCM_TCS_CMD_COMMIT_SHFT		30
+#define BCM_TCS_CMD_COMMIT_MASK		0x40000000
+#define BCM_TCS_CMD_VALID_SHFT		29
+#define BCM_TCS_CMD_VALID_MASK		0x200010000
+#define BCM_TCS_CMD_VOTE_X_SHFT		14
+#define BCM_TCS_CMD_VOTE_MASK		0x3fff
+#define BCM_TCS_CMD_VOTE_Y_SHFT		0
+#define BCM_TCS_CMD_VOTE_Y_MASK		0xfffc000
+
+#define BCM_TCS_CMD(commit, valid, vote_x, vote_y) \
+	((commit << BCM_TCS_CMD_COMMIT_SHFT) |\
+	(valid << BCM_TCS_CMD_VALID_SHFT) |\
+	((vote_x & BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_X_SHFT) |\
+	((vote_y & BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_Y_SHFT))
+
+#define to_qcom_provider(_provider) \
+	container_of(_provider, struct qcom_icc_provider, provider)
+
+#define DEFINE_QNODE(_name, _id, _channels, _buswidth,			\
+			_numlinks, ...)					\
+		static struct qcom_icc_node _name = {			\
+		.id = _id,						\
+		.name = #_name,						\
+		.channels = _channels,					\
+		.buswidth = _buswidth,					\
+		.num_links = _numlinks,					\
+		.links = { __VA_ARGS__ },				\
+	}
+
+#define DEFINE_QBCM(_name, _bcmname, _keepalive, _numnodes, ...)	\
+		static struct qcom_icc_bcm _name = {			\
+		.name = _bcmname,					\
+		.keepalive = _keepalive,				\
+		.num_nodes = _numnodes,					\
+		.nodes = { __VA_ARGS__ },				\
+	}
+
+struct qcom_icc_provider {
+	struct icc_provider	provider;
+	void __iomem		*base;
+	struct device		*dev;
+	struct qcom_icc_bcm	**bcms;
+	size_t num_bcms;
+};
+
+/**
+ * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager(BCM)
+ * @unit: bcm threshold values are in magnitudes of this
+ * @width: prototype width
+ * @vcd: virtual clock domain that this bcm belongs to
+ */
+
+struct bcm_db {
+	u32 unit;
+	u16 width;
+	u8 vcd;
+	u8 reserved;
+};
+
+#define SDM845_MAX_LINKS	43
+#define SDM845_MAX_BCMS		30
+#define SDM845_MAX_BCM_PER_NODE	2
+#define SDM845_MAX_VCD		10
+
+/**
+ * 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
+ * @channels: num of channels at this node
+ * @buswidth: width of the interconnect between a node and the bus
+ * @sum_avg: current sum aggregate value of all avg bw requests
+ * @max_peak: current max aggregate value of all peak bw requests
+ * @bcms: list of bcms associated with this logical node
+ * @num_bcm: num of @bcms
+ */
+struct qcom_icc_node {
+	const char *name;
+	u16 links[SDM845_MAX_LINKS];
+	u16 id;
+	u16 num_links;
+	u16 channels;
+	u16 buswidth;
+	u64 sum_avg;
+	u64 max_peak;
+	struct qcom_icc_bcm *bcms[SDM845_MAX_BCM_PER_NODE];
+	size_t num_bcms;
+};
+
+/**
+ * struct qcom_icc_bcm - Qualcomm specific hardware accelerator nodes
+ * known as Bus Clock Manager(BCM)
+ * @name: the bcm node name used to fetch BCM data from command db
+ * @type: latency or bandwidth bcm
+ * @addr: address offsets used when voting to RPMH
+ * @vote_x: aggregated threshold values, represents sum_bw when @type is bw bcm
+ * @vote_y: aggregated threshold values, represents peak_bw when @type is bw bcm
+ * @dirty: flag used to indicate whether or bcm needs to be committed
+ * @aux_data: auxiliary data used when calculating threshold values and
+ * communicating with RPMh
+ * @list: used to link to other bcms when compiling lists for commit
+ * @num_nodes: total number of @num_nodes
+ * @nodes: list of qcom_icc_nodes that this BCM encapsulates
+ */
+
+struct qcom_icc_bcm {
+	const char *name;
+	u32 type;
+	u32 addr;
+	u64 vote_x;
+	u64 vote_y;
+	bool dirty;
+	bool keepalive;
+	struct bcm_db aux_data;
+	struct list_head list;
+	size_t num_nodes;
+	struct qcom_icc_node *nodes[];
+};
+
+struct qcom_icc_fabric {
+	struct qcom_icc_node **nodes;
+	size_t num_nodes;
+	u32 base_offset;
+	u32 qos_offset;
+};
+
+struct qcom_icc_desc {
+	struct qcom_icc_node **nodes;
+	size_t num_nodes;
+	struct qcom_icc_bcm **bcms;
+	size_t num_bcms;
+};
+
+DEFINE_QNODE(qhm_a1noc_cfg, MASTER_A1NOC_CFG, 1, 4, 1, SLAVE_SERVICE_A1NOC);
+DEFINE_QNODE(qhm_qup1, MASTER_BLSP_1, 1, 4, 1, SLAVE_A1NOC_SNOC);
+DEFINE_QNODE(qhm_tsif, MASTER_TSIF, 1, 4, 1, SLAVE_A1NOC_SNOC);
+DEFINE_QNODE(xm_sdc2, MASTER_SDCC_2, 1, 8, 1, SLAVE_A1NOC_SNOC);
+DEFINE_QNODE(xm_sdc4, MASTER_SDCC_4, 1, 8, 1, SLAVE_A1NOC_SNOC);
+DEFINE_QNODE(xm_ufs_card, MASTER_UFS_CARD, 1, 8, 1, SLAVE_A1NOC_SNOC);
+DEFINE_QNODE(xm_ufs_mem, MASTER_UFS_MEM, 1, 8, 1, SLAVE_A1NOC_SNOC);
+DEFINE_QNODE(xm_pcie_0, MASTER_PCIE_0, 1, 8, 1, SLAVE_ANOC_PCIE_A1NOC_SNOC);
+DEFINE_QNODE(qhm_a2noc_cfg, MASTER_A2NOC_CFG, 1, 4, 1, SLAVE_SERVICE_A2NOC);
+DEFINE_QNODE(qhm_qdss_bam, MASTER_QDSS_BAM, 1, 4, 1, SLAVE_A2NOC_SNOC);
+DEFINE_QNODE(qhm_qup2, MASTER_BLSP_2, 1, 4, 1, SLAVE_A2NOC_SNOC);
+DEFINE_QNODE(qnm_cnoc, MASTER_CNOC_A2NOC, 1, 8, 1, SLAVE_A2NOC_SNOC);
+DEFINE_QNODE(qxm_crypto, MASTER_CRYPTO, 1, 8, 1, SLAVE_A2NOC_SNOC);
+DEFINE_QNODE(qxm_ipa, MASTER_IPA, 1, 8, 1, SLAVE_A2NOC_SNOC);
+DEFINE_QNODE(xm_pcie3_1, MASTER_PCIE_1, 1, 8, 1, SLAVE_ANOC_PCIE_SNOC);
+DEFINE_QNODE(xm_qdss_etr, MASTER_QDSS_ETR, 1, 8, 1, SLAVE_A2NOC_SNOC);
+DEFINE_QNODE(xm_usb3_0, MASTER_USB3_0, 1, 8, 1, SLAVE_A2NOC_SNOC);
+DEFINE_QNODE(xm_usb3_1, MASTER_USB3_1, 1, 8, 1, SLAVE_A2NOC_SNOC);
+DEFINE_QNODE(qxm_camnoc_hf0_uncomp, MASTER_CAMNOC_HF0_UNCOMP, 1, 32, 1, SLAVE_CAMNOC_UNCOMP);
+DEFINE_QNODE(qxm_camnoc_hf1_uncomp, MASTER_CAMNOC_HF1_UNCOMP, 1, 32, 1, SLAVE_CAMNOC_UNCOMP);
+DEFINE_QNODE(qxm_camnoc_sf_uncomp, MASTER_CAMNOC_SF_UNCOMP, 1, 32, 1, SLAVE_CAMNOC_UNCOMP);
+DEFINE_QNODE(qhm_spdm, MASTER_SPDM, 1, 4, 1, SLAVE_CNOC_A2NOC);
+DEFINE_QNODE(qhm_tic, MASTER_TIC, 1, 4, 43, SLAVE_A1NOC_CFG, SLAVE_A2NOC_CFG, SLAVE_AOP, SLAVE_AOSS, SLAVE_CAMERA_CFG, SLAVE_CLK_CTL, SLAVE_CDSP_CFG, SLAVE_RBCPR_CX_CFG, SLAVE_CRYPTO_0_CFG, SLAVE_DCC_CFG, SLAVE_CNOC_DDRSS, SLAVE_DISPLAY_CFG, SLAVE_GLM, SLAVE_GFX3D_CFG, SLAVE_IMEM_CFG, SLAVE_IPA_CFG, SLAVE_CNOC_MNOC_CFG, SLAVE_PCIE_0_CFG, SLAVE_PCIE_1_CFG, SLAVE_PDM, SLAVE_SOUTH_PHY_CFG, SLAVE_PIMEM_CFG, SLAVE_PRNG, SLAVE_QDSS_CFG, SLAVE_BLSP_2, SLAVE_BLSP_1, SLAVE_SDCC_2, SLAVE_SDCC_4, SLAVE_SNOC_CFG, SLAVE_SPDM_WRAPPER, SLAVE_SPSS_CFG, SLAVE_TCSR, SLAVE_TLMM_NORTH, SLAVE_TLMM_SOUTH, SLAVE_TSIF, SLAVE_UFS_CARD_CFG, SLAVE_UFS_MEM_CFG, SLAVE_USB3_0, SLAVE_USB3_1, SLAVE_VENUS_CFG, SLAVE_VSENSE_CTRL_CFG, SLAVE_CNOC_A2NOC, SLAVE_SERVICE_CNOC);
+DEFINE_QNODE(qnm_snoc, MASTER_SNOC_CNOC, 1, 8, 42, SLAVE_A1NOC_CFG, SLAVE_A2NOC_CFG, SLAVE_AOP, SLAVE_AOSS, SLAVE_CAMERA_CFG, SLAVE_CLK_CTL, SLAVE_CDSP_CFG, SLAVE_RBCPR_CX_CFG, SLAVE_CRYPTO_0_CFG, SLAVE_DCC_CFG, SLAVE_CNOC_DDRSS, SLAVE_DISPLAY_CFG, SLAVE_GLM, SLAVE_GFX3D_CFG, SLAVE_IMEM_CFG, SLAVE_IPA_CFG, SLAVE_CNOC_MNOC_CFG, SLAVE_PCIE_0_CFG, SLAVE_PCIE_1_CFG, SLAVE_PDM, SLAVE_SOUTH_PHY_CFG, SLAVE_PIMEM_CFG, SLAVE_PRNG, SLAVE_QDSS_CFG, SLAVE_BLSP_2, SLAVE_BLSP_1, SLAVE_SDCC_2, SLAVE_SDCC_4, SLAVE_SNOC_CFG, SLAVE_SPDM_WRAPPER, SLAVE_SPSS_CFG, SLAVE_TCSR, SLAVE_TLMM_NORTH, SLAVE_TLMM_SOUTH, SLAVE_TSIF, SLAVE_UFS_CARD_CFG, SLAVE_UFS_MEM_CFG, SLAVE_USB3_0, SLAVE_USB3_1, SLAVE_VENUS_CFG, SLAVE_VSENSE_CTRL_CFG, SLAVE_SERVICE_CNOC);
+DEFINE_QNODE(xm_qdss_dap, MASTER_QDSS_DAP, 1, 8, 43, SLAVE_A1NOC_CFG, SLAVE_A2NOC_CFG, SLAVE_AOP, SLAVE_AOSS, SLAVE_CAMERA_CFG, SLAVE_CLK_CTL, SLAVE_CDSP_CFG, SLAVE_RBCPR_CX_CFG, SLAVE_CRYPTO_0_CFG, SLAVE_DCC_CFG, SLAVE_CNOC_DDRSS, SLAVE_DISPLAY_CFG, SLAVE_GLM, SLAVE_GFX3D_CFG, SLAVE_IMEM_CFG, SLAVE_IPA_CFG, SLAVE_CNOC_MNOC_CFG, SLAVE_PCIE_0_CFG, SLAVE_PCIE_1_CFG, SLAVE_PDM, SLAVE_SOUTH_PHY_CFG, SLAVE_PIMEM_CFG, SLAVE_PRNG, SLAVE_QDSS_CFG, SLAVE_BLSP_2, SLAVE_BLSP_1, SLAVE_SDCC_2, SLAVE_SDCC_4, SLAVE_SNOC_CFG, SLAVE_SPDM_WRAPPER, SLAVE_SPSS_CFG, SLAVE_TCSR, SLAVE_TLMM_NORTH, SLAVE_TLMM_SOUTH, SLAVE_TSIF, SLAVE_UFS_CARD_CFG, SLAVE_UFS_MEM_CFG, SLAVE_USB3_0, SLAVE_USB3_1, SLAVE_VENUS_CFG, SLAVE_VSENSE_CTRL_CFG, SLAVE_CNOC_A2NOC, SLAVE_SERVICE_CNOC);
+DEFINE_QNODE(qhm_cnoc, MASTER_CNOC_DC_NOC, 1, 4, 2, SLAVE_LLCC_CFG, SLAVE_MEM_NOC_CFG);
+DEFINE_QNODE(acm_l3, MASTER_APPSS_PROC, 1, 16, 3, SLAVE_GNOC_SNOC, SLAVE_GNOC_MEM_NOC, SLAVE_SERVICE_GNOC);
+DEFINE_QNODE(pm_gnoc_cfg, MASTER_GNOC_CFG, 1, 4, 1, SLAVE_SERVICE_GNOC);
+DEFINE_QNODE(ipa_core_master, MASTER_IPA_CORE, 1, 8, 1, SLAVE_IPA_CORE);
+DEFINE_QNODE(llcc_mc, MASTER_LLCC, 4, 4, 1, SLAVE_EBI1);
+DEFINE_QNODE(acm_tcu, MASTER_TCU_0, 1, 8, 3, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC, SLAVE_MEM_NOC_SNOC);
+DEFINE_QNODE(qhm_memnoc_cfg, MASTER_MEM_NOC_CFG, 1, 4, 2, SLAVE_MSS_PROC_MS_MPU_CFG, SLAVE_SERVICE_MEM_NOC);
+DEFINE_QNODE(qnm_apps, MASTER_GNOC_MEM_NOC, 2, 32, 1, SLAVE_LLCC);
+DEFINE_QNODE(qnm_mnoc_hf, MASTER_MNOC_HF_MEM_NOC, 2, 32, 2, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC);
+DEFINE_QNODE(qnm_mnoc_sf, MASTER_MNOC_SF_MEM_NOC, 1, 32, 3, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC, SLAVE_MEM_NOC_SNOC);
+DEFINE_QNODE(qnm_snoc_gc, MASTER_SNOC_GC_MEM_NOC, 1, 8, 1, SLAVE_LLCC);
+DEFINE_QNODE(qnm_snoc_sf, MASTER_SNOC_SF_MEM_NOC, 1, 16, 2, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC);
+DEFINE_QNODE(qxm_gpu, MASTER_GFX3D, 2, 32, 3, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC, SLAVE_MEM_NOC_SNOC);
+DEFINE_QNODE(qhm_mnoc_cfg, MASTER_CNOC_MNOC_CFG, 1, 4, 1, SLAVE_SERVICE_MNOC);
+DEFINE_QNODE(qxm_camnoc_hf0, MASTER_CAMNOC_HF0, 1, 32, 1, SLAVE_MNOC_HF_MEM_NOC);
+DEFINE_QNODE(qxm_camnoc_hf1, MASTER_CAMNOC_HF1, 1, 32, 1, SLAVE_MNOC_HF_MEM_NOC);
+DEFINE_QNODE(qxm_camnoc_sf, MASTER_CAMNOC_SF, 1, 32, 1, SLAVE_MNOC_SF_MEM_NOC);
+DEFINE_QNODE(qxm_mdp0, MASTER_MDP0, 1, 32, 1, SLAVE_MNOC_HF_MEM_NOC);
+DEFINE_QNODE(qxm_mdp1, MASTER_MDP1, 1, 32, 1, SLAVE_MNOC_HF_MEM_NOC);
+DEFINE_QNODE(qxm_rot, MASTER_ROTATOR, 1, 32, 1, SLAVE_MNOC_SF_MEM_NOC);
+DEFINE_QNODE(qxm_venus0, MASTER_VIDEO_P0, 1, 32, 1, SLAVE_MNOC_SF_MEM_NOC);
+DEFINE_QNODE(qxm_venus1, MASTER_VIDEO_P1, 1, 32, 1, SLAVE_MNOC_SF_MEM_NOC);
+DEFINE_QNODE(qxm_venus_arm9, MASTER_VIDEO_PROC, 1, 8, 1, SLAVE_MNOC_SF_MEM_NOC);
+DEFINE_QNODE(qhm_snoc_cfg, MASTER_SNOC_CFG, 1, 4, 1, SLAVE_SERVICE_SNOC);
+DEFINE_QNODE(qnm_aggre1_noc, MASTER_A1NOC_SNOC, 1, 16, 6, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_SNOC_MEM_NOC_SF, SLAVE_IMEM, SLAVE_PIMEM, SLAVE_QDSS_STM);
+DEFINE_QNODE(qnm_aggre2_noc, MASTER_A2NOC_SNOC, 1, 16, 9, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_SNOC_MEM_NOC_SF, SLAVE_IMEM, SLAVE_PCIE_0, SLAVE_PCIE_1, SLAVE_PIMEM, SLAVE_QDSS_STM, SLAVE_TCU);
+DEFINE_QNODE(qnm_gladiator_sodv, MASTER_GNOC_SNOC, 1, 8, 8, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_IMEM, SLAVE_PCIE_0, SLAVE_PCIE_1, SLAVE_PIMEM, SLAVE_QDSS_STM, SLAVE_TCU);
+DEFINE_QNODE(qnm_memnoc, MASTER_MEM_NOC_SNOC, 1, 8, 5, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_IMEM, SLAVE_PIMEM, SLAVE_QDSS_STM);
+DEFINE_QNODE(qnm_pcie_anoc, MASTER_ANOC_PCIE_SNOC, 1, 16, 5, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_SNOC_MEM_NOC_SF, SLAVE_IMEM, SLAVE_QDSS_STM);
+DEFINE_QNODE(qxm_pimem, MASTER_PIMEM, 1, 8, 2, SLAVE_SNOC_MEM_NOC_GC, SLAVE_IMEM);
+DEFINE_QNODE(xm_gic, MASTER_GIC, 1, 8, 2, SLAVE_SNOC_MEM_NOC_GC, SLAVE_IMEM);
+DEFINE_QNODE(qns_a1noc_snoc, SLAVE_A1NOC_SNOC, 1, 16, 1, MASTER_A1NOC_SNOC);
+DEFINE_QNODE(srvc_aggre1_noc, SLAVE_SERVICE_A1NOC, 1, 4, 0);
+DEFINE_QNODE(qns_pcie_a1noc_snoc, SLAVE_ANOC_PCIE_A1NOC_SNOC, 1, 16, 1, MASTER_ANOC_PCIE_SNOC);
+DEFINE_QNODE(qns_a2noc_snoc, SLAVE_A2NOC_SNOC, 1, 16, 1, MASTER_A2NOC_SNOC);
+DEFINE_QNODE(qns_pcie_snoc, SLAVE_ANOC_PCIE_SNOC, 1, 16, 1, MASTER_ANOC_PCIE_SNOC);
+DEFINE_QNODE(srvc_aggre2_noc, SLAVE_SERVICE_A2NOC, 1, 4, 0);
+DEFINE_QNODE(qns_camnoc_uncomp, SLAVE_CAMNOC_UNCOMP, 1, 32, 0);
+DEFINE_QNODE(qhs_a1_noc_cfg, SLAVE_A1NOC_CFG, 1, 4, 1, MASTER_A1NOC_CFG);
+DEFINE_QNODE(qhs_a2_noc_cfg, SLAVE_A2NOC_CFG, 1, 4, 1, MASTER_A2NOC_CFG);
+DEFINE_QNODE(qhs_aop, SLAVE_AOP, 1, 4, 0);
+DEFINE_QNODE(qhs_aoss, SLAVE_AOSS, 1, 4, 0);
+DEFINE_QNODE(qhs_camera_cfg, SLAVE_CAMERA_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_clk_ctl, SLAVE_CLK_CTL, 1, 4, 0);
+DEFINE_QNODE(qhs_compute_dsp_cfg, SLAVE_CDSP_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_cpr_cx, SLAVE_RBCPR_CX_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_crypto0_cfg, SLAVE_CRYPTO_0_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_dcc_cfg, SLAVE_DCC_CFG, 1, 4, 1, MASTER_CNOC_DC_NOC);
+DEFINE_QNODE(qhs_ddrss_cfg, SLAVE_CNOC_DDRSS, 1, 4, 0);
+DEFINE_QNODE(qhs_display_cfg, SLAVE_DISPLAY_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_glm, SLAVE_GLM, 1, 4, 0);
+DEFINE_QNODE(qhs_gpuss_cfg, SLAVE_GFX3D_CFG, 1, 8, 0);
+DEFINE_QNODE(qhs_imem_cfg, SLAVE_IMEM_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_ipa, SLAVE_IPA_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_mnoc_cfg, SLAVE_CNOC_MNOC_CFG, 1, 4, 1, MASTER_CNOC_MNOC_CFG);
+DEFINE_QNODE(qhs_pcie0_cfg, SLAVE_PCIE_0_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_pcie_gen3_cfg, SLAVE_PCIE_1_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_pdm, SLAVE_PDM, 1, 4, 0);
+DEFINE_QNODE(qhs_phy_refgen_south, SLAVE_SOUTH_PHY_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_pimem_cfg, SLAVE_PIMEM_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_prng, SLAVE_PRNG, 1, 4, 0);
+DEFINE_QNODE(qhs_qdss_cfg, SLAVE_QDSS_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_qupv3_north, SLAVE_BLSP_2, 1, 4, 0);
+DEFINE_QNODE(qhs_qupv3_south, SLAVE_BLSP_1, 1, 4, 0);
+DEFINE_QNODE(qhs_sdc2, SLAVE_SDCC_2, 1, 4, 0);
+DEFINE_QNODE(qhs_sdc4, SLAVE_SDCC_4, 1, 4, 0);
+DEFINE_QNODE(qhs_snoc_cfg, SLAVE_SNOC_CFG, 1, 4, 1, MASTER_SNOC_CFG);
+DEFINE_QNODE(qhs_spdm, SLAVE_SPDM_WRAPPER, 1, 4, 0);
+DEFINE_QNODE(qhs_spss_cfg, SLAVE_SPSS_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_tcsr, SLAVE_TCSR, 1, 4, 0);
+DEFINE_QNODE(qhs_tlmm_north, SLAVE_TLMM_NORTH, 1, 4, 0);
+DEFINE_QNODE(qhs_tlmm_south, SLAVE_TLMM_SOUTH, 1, 4, 0);
+DEFINE_QNODE(qhs_tsif, SLAVE_TSIF, 1, 4, 0);
+DEFINE_QNODE(qhs_ufs_card_cfg, SLAVE_UFS_CARD_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_ufs_mem_cfg, SLAVE_UFS_MEM_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_usb3_0, SLAVE_USB3_0, 1, 4, 0);
+DEFINE_QNODE(qhs_usb3_1, SLAVE_USB3_1, 1, 4, 0);
+DEFINE_QNODE(qhs_venus_cfg, SLAVE_VENUS_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_vsense_ctrl_cfg, SLAVE_VSENSE_CTRL_CFG, 1, 4, 0);
+DEFINE_QNODE(qns_cnoc_a2noc, SLAVE_CNOC_A2NOC, 1, 8, 1, MASTER_CNOC_A2NOC);
+DEFINE_QNODE(srvc_cnoc, SLAVE_SERVICE_CNOC, 1, 4, 0);
+DEFINE_QNODE(qhs_llcc, SLAVE_LLCC_CFG, 1, 4, 0);
+DEFINE_QNODE(qhs_memnoc, SLAVE_MEM_NOC_CFG, 1, 4, 1, MASTER_MEM_NOC_CFG);
+DEFINE_QNODE(qns_gladiator_sodv, SLAVE_GNOC_SNOC, 1, 8, 1, MASTER_GNOC_SNOC);
+DEFINE_QNODE(qns_gnoc_memnoc, SLAVE_GNOC_MEM_NOC, 2, 32, 1, MASTER_GNOC_MEM_NOC);
+DEFINE_QNODE(srvc_gnoc, SLAVE_SERVICE_GNOC, 1, 4, 0);
+DEFINE_QNODE(ipa_core_slave, SLAVE_IPA_CORE, 1, 8, 0);
+DEFINE_QNODE(ebi, SLAVE_EBI1, 4, 4, 0);
+DEFINE_QNODE(qhs_mdsp_ms_mpu_cfg, SLAVE_MSS_PROC_MS_MPU_CFG, 1, 4, 0);
+DEFINE_QNODE(qns_apps_io, SLAVE_MEM_NOC_GNOC, 1, 32, 0);
+DEFINE_QNODE(qns_llcc, SLAVE_LLCC, 4, 16, 1, MASTER_LLCC);
+DEFINE_QNODE(qns_memnoc_snoc, SLAVE_MEM_NOC_SNOC, 1, 8, 1, MASTER_MEM_NOC_SNOC);
+DEFINE_QNODE(srvc_memnoc, SLAVE_SERVICE_MEM_NOC, 1, 4, 0);
+DEFINE_QNODE(qns2_mem_noc, SLAVE_MNOC_SF_MEM_NOC, 1, 32, 1, MASTER_MNOC_SF_MEM_NOC);
+DEFINE_QNODE(qns_mem_noc_hf, SLAVE_MNOC_HF_MEM_NOC, 2, 32, 1, MASTER_MNOC_HF_MEM_NOC);
+DEFINE_QNODE(srvc_mnoc, SLAVE_SERVICE_MNOC, 1, 4, 0);
+DEFINE_QNODE(qhs_apss, SLAVE_APPSS, 1, 8, 0);
+DEFINE_QNODE(qns_cnoc, SLAVE_SNOC_CNOC, 1, 8, 1, MASTER_SNOC_CNOC);
+DEFINE_QNODE(qns_memnoc_gc, SLAVE_SNOC_MEM_NOC_GC, 1, 8, 1, MASTER_SNOC_GC_MEM_NOC);
+DEFINE_QNODE(qns_memnoc_sf, SLAVE_SNOC_MEM_NOC_SF, 1, 16, 1, MASTER_SNOC_SF_MEM_NOC);
+DEFINE_QNODE(qxs_imem, SLAVE_IMEM, 1, 8, 0);
+DEFINE_QNODE(qxs_pcie, SLAVE_PCIE_0, 1, 8, 0);
+DEFINE_QNODE(qxs_pcie_gen3, SLAVE_PCIE_1, 1, 8, 0);
+DEFINE_QNODE(qxs_pimem, SLAVE_PIMEM, 1, 8, 0);
+DEFINE_QNODE(srvc_snoc, SLAVE_SERVICE_SNOC, 1, 4, 0);
+DEFINE_QNODE(xs_qdss_stm, SLAVE_QDSS_STM, 1, 4, 0);
+DEFINE_QNODE(xs_sys_tcu_cfg, SLAVE_TCU, 1, 8, 0);
+
+DEFINE_QBCM(bcm_acv, "ACV", false, 1, &ebi);
+DEFINE_QBCM(bcm_mc0, "MC0", true, 1, &ebi);
+DEFINE_QBCM(bcm_sh0, "SH0", true, 1, &qns_llcc);
+DEFINE_QBCM(bcm_mm0, "MM0", false, 1, &qns_mem_noc_hf);
+DEFINE_QBCM(bcm_sh1, "SH1", false, 1, &qns_apps_io);
+DEFINE_QBCM(bcm_mm1, "MM1", false, 7, &qxm_camnoc_hf0_uncomp, &qxm_camnoc_hf1_uncomp, &qxm_camnoc_sf_uncomp, &qxm_camnoc_hf0, &qxm_camnoc_hf1, &qxm_mdp0, &qxm_mdp1);
+DEFINE_QBCM(bcm_sh2, "SH2", false, 1, &qns_memnoc_snoc);
+DEFINE_QBCM(bcm_mm2, "MM2", false, 1, &qns2_mem_noc);
+DEFINE_QBCM(bcm_sh3, "SH3", false, 1, &acm_tcu);
+DEFINE_QBCM(bcm_mm3, "MM3", false, 5, &qxm_camnoc_sf, &qxm_rot, &qxm_venus0, &qxm_venus1, &qxm_venus_arm9);
+DEFINE_QBCM(bcm_sh5, "SH5", false, 1, &qnm_apps);
+DEFINE_QBCM(bcm_sn0, "SN0", true, 1, &qns_memnoc_sf);
+DEFINE_QBCM(bcm_ce0, "CE0", false, 1, &qxm_crypto);
+DEFINE_QBCM(bcm_ip0, "IP0", false, 1, &ipa_core_slave);
+DEFINE_QBCM(bcm_cn0, "CN0", false, 47, &qhm_spdm, &qhm_tic, &qnm_snoc, &xm_qdss_dap, &qhs_a1_noc_cfg, &qhs_a2_noc_cfg, &qhs_aop, &qhs_aoss, &qhs_camera_cfg, &qhs_clk_ctl, &qhs_compute_dsp_cfg, &qhs_cpr_cx, &qhs_crypto0_cfg, &qhs_dcc_cfg, &qhs_ddrss_cfg, &qhs_display_cfg, &qhs_glm, &qhs_gpuss_cfg, &qhs_imem_cfg, &qhs_ipa, &qhs_mnoc_cfg, &qhs_pcie0_cfg, &qhs_pcie_gen3_cfg, &qhs_pdm, &qhs_phy_refgen_south, &qhs_pimem_cfg, &qhs_prng, &qhs_qdss_cfg, &qhs_qupv3_north, &qhs_qupv3_south, &qhs_sdc2, &qhs_sdc4, &qhs_snoc_cfg, &qhs_spdm, &qhs_spss_cfg, &qhs_tcsr, &qhs_tlmm_north, &qhs_tlmm_south, &qhs_tsif, &qhs_ufs_card_cfg, &qhs_ufs_mem_cfg, &qhs_usb3_0, &qhs_usb3_1, &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, &qns_cnoc_a2noc, &srvc_cnoc);
+DEFINE_QBCM(bcm_qup0, "QUP0", false, 2, &qhm_qup1, &qhm_qup2);
+DEFINE_QBCM(bcm_sn1, "SN1", false, 1, &qxs_imem);
+DEFINE_QBCM(bcm_sn2, "SN2", false, 1, &qns_memnoc_gc);
+DEFINE_QBCM(bcm_sn3, "SN3", false, 1, &qns_cnoc);
+DEFINE_QBCM(bcm_sn4, "SN4", false, 1, &qxm_pimem);
+DEFINE_QBCM(bcm_sn5, "SN5", false, 1, &xs_qdss_stm);
+DEFINE_QBCM(bcm_sn6, "SN6", false, 3, &qhs_apss, &srvc_snoc, &xs_sys_tcu_cfg);
+DEFINE_QBCM(bcm_sn7, "SN7", false, 1, &qxs_pcie);
+DEFINE_QBCM(bcm_sn8, "SN8", false, 1, &qxs_pcie_gen3);
+DEFINE_QBCM(bcm_sn9, "SN9", false, 2, &srvc_aggre1_noc, &qnm_aggre1_noc);
+DEFINE_QBCM(bcm_sn11, "SN11", false, 2, &srvc_aggre2_noc, &qnm_aggre2_noc);
+DEFINE_QBCM(bcm_sn12, "SN12", false, 2, &qnm_gladiator_sodv, &xm_gic);
+DEFINE_QBCM(bcm_sn14, "SN14", false, 1, &qnm_pcie_anoc);
+DEFINE_QBCM(bcm_sn15, "SN15", false, 1, &qnm_memnoc);
+
+static struct qcom_icc_node *rsc_hlos_nodes[] = {
+	&acm_l3,
+	&acm_tcu,
+	&ipa_core_master,
+	&llcc_mc,
+	&pm_gnoc_cfg,
+	&qhm_a1noc_cfg,
+	&qhm_a2noc_cfg,
+	&qhm_cnoc,
+	&qhm_memnoc_cfg,
+	&qhm_mnoc_cfg,
+	&qhm_qdss_bam,
+	&qhm_qup1,
+	&qhm_qup2,
+	&qhm_snoc_cfg,
+	&qhm_spdm,
+	&qhm_tic,
+	&qhm_tsif,
+	&qnm_aggre1_noc,
+	&qnm_aggre2_noc,
+	&qnm_apps,
+	&qnm_cnoc,
+	&qnm_gladiator_sodv,
+	&qnm_memnoc,
+	&qnm_mnoc_hf,
+	&qnm_mnoc_sf,
+	&qnm_pcie_anoc,
+	&qnm_snoc,
+	&qnm_snoc_gc,
+	&qnm_snoc_sf,
+	&qxm_camnoc_hf0,
+	&qxm_camnoc_hf0_uncomp,
+	&qxm_camnoc_hf1,
+	&qxm_camnoc_hf1_uncomp,
+	&qxm_camnoc_sf,
+	&qxm_camnoc_sf_uncomp,
+	&qxm_crypto,
+	&qxm_gpu,
+	&qxm_ipa,
+	&qxm_mdp0,
+	&qxm_mdp1,
+	&qxm_pimem,
+	&qxm_rot,
+	&qxm_venus0,
+	&qxm_venus1,
+	&qxm_venus_arm9,
+	&xm_gic,
+	&xm_pcie3_1,
+	&xm_pcie_0,
+	&xm_qdss_dap,
+	&xm_qdss_etr,
+	&xm_sdc2,
+	&xm_sdc4,
+	&xm_ufs_card,
+	&xm_ufs_mem,
+	&xm_usb3_0,
+	&xm_usb3_1,
+	&ebi,
+	&ipa_core_slave,
+	&qhs_a1_noc_cfg,
+	&qhs_a2_noc_cfg,
+	&qhs_aop,
+	&qhs_aoss,
+	&qhs_apss,
+	&qhs_camera_cfg,
+	&qhs_clk_ctl,
+	&qhs_compute_dsp_cfg,
+	&qhs_cpr_cx,
+	&qhs_crypto0_cfg,
+	&qhs_dcc_cfg,
+	&qhs_ddrss_cfg,
+	&qhs_display_cfg,
+	&qhs_glm,
+	&qhs_gpuss_cfg,
+	&qhs_imem_cfg,
+	&qhs_ipa,
+	&qhs_llcc,
+	&qhs_mdsp_ms_mpu_cfg,
+	&qhs_memnoc,
+	&qhs_mnoc_cfg,
+	&qhs_pcie0_cfg,
+	&qhs_pcie_gen3_cfg,
+	&qhs_pdm,
+	&qhs_phy_refgen_south,
+	&qhs_pimem_cfg,
+	&qhs_prng,
+	&qhs_qdss_cfg,
+	&qhs_qupv3_north,
+	&qhs_qupv3_south,
+	&qhs_sdc2,
+	&qhs_sdc4,
+	&qhs_snoc_cfg,
+	&qhs_spdm,
+	&qhs_spss_cfg,
+	&qhs_tcsr,
+	&qhs_tlmm_north,
+	&qhs_tlmm_south,
+	&qhs_tsif,
+	&qhs_ufs_card_cfg,
+	&qhs_ufs_mem_cfg,
+	&qhs_usb3_0,
+	&qhs_usb3_1,
+	&qhs_venus_cfg,
+	&qhs_vsense_ctrl_cfg,
+	&qns2_mem_noc,
+	&qns_a1noc_snoc,
+	&qns_a2noc_snoc,
+	&qns_apps_io,
+	&qns_camnoc_uncomp,
+	&qns_cnoc,
+	&qns_cnoc_a2noc,
+	&qns_gladiator_sodv,
+	&qns_gnoc_memnoc,
+	&qns_llcc,
+	&qns_mem_noc_hf,
+	&qns_memnoc_gc,
+	&qns_memnoc_sf,
+	&qns_memnoc_snoc,
+	&qns_pcie_a1noc_snoc,
+	&qns_pcie_snoc,
+	&qxs_imem,
+	&qxs_pcie,
+	&qxs_pcie_gen3,
+	&qxs_pimem,
+	&srvc_aggre1_noc,
+	&srvc_aggre2_noc,
+	&srvc_cnoc,
+	&srvc_gnoc,
+	&srvc_memnoc,
+	&srvc_mnoc,
+	&srvc_snoc,
+	&xs_qdss_stm,
+	&xs_sys_tcu_cfg,
+};
+
+static struct qcom_icc_bcm *rsc_hlos_bcms[] = {
+	&bcm_acv,
+	&bcm_mc0,
+	&bcm_sh0,
+	&bcm_mm0,
+	&bcm_sh1,
+	&bcm_mm1,
+	&bcm_sh2,
+	&bcm_mm2,
+	&bcm_sh3,
+	&bcm_mm3,
+	&bcm_sh5,
+	&bcm_sn0,
+	&bcm_ce0,
+	&bcm_ip0,
+	&bcm_cn0,
+	&bcm_qup0,
+	&bcm_sn1,
+	&bcm_sn2,
+	&bcm_sn3,
+	&bcm_sn4,
+	&bcm_sn5,
+	&bcm_sn6,
+	&bcm_sn7,
+	&bcm_sn8,
+	&bcm_sn9,
+	&bcm_sn11,
+	&bcm_sn12,
+	&bcm_sn14,
+	&bcm_sn15,
+};
+
+static struct qcom_icc_desc sdm845_rsc_hlos = {
+	.nodes = rsc_hlos_nodes,
+	.num_nodes = ARRAY_SIZE(rsc_hlos_nodes),
+	.bcms = rsc_hlos_bcms,
+	.num_bcms = ARRAY_SIZE(rsc_hlos_bcms),
+};
+
+static int qcom_icc_init(struct icc_node *node)
+{
+	/* TODO: init qos and priority */
+
+	return 0;
+}
+
+static int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
+{
+	struct qcom_icc_node *qn;
+	int ret, i;
+
+	bcm->addr = cmd_db_read_addr(bcm->name);
+	if (!bcm->addr) {
+		dev_err(dev, "%s could not find RPMh address\n",
+			bcm->name);
+		return -EINVAL;
+	}
+
+	if (!cmd_db_read_aux_data_len(bcm->name)) {
+		dev_err(dev, "%s command db missing aux data\n",
+			bcm->name);
+		return -EINVAL;
+	}
+
+	ret = cmd_db_read_aux_data(bcm->name, (u8 *)&bcm->aux_data,
+				   sizeof(struct bcm_db));
+	if (ret < 0) {
+		dev_err(dev, "%s command db read error (%d)\n",
+			bcm->name, ret);
+		return ret;
+	}
+
+	for (i = 0; i < bcm->num_nodes; i++) {
+		qn = bcm->nodes[i];
+		qn->bcms[qn->num_bcms] = bcm;
+		qn->num_bcms++;
+	}
+
+	return 0;
+}
+
+static int qcom_tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x,
+					u64 vote_y, u32 addr, bool commit)
+{
+	int ret = 0;
+	bool valid = true;
+
+	if (!cmd)
+		return ret;
+
+	if (vote_x == 0 && vote_y == 0)
+		valid = false;
+
+	if (vote_x > BCM_TCS_CMD_VOTE_MASK)
+		vote_x = BCM_TCS_CMD_VOTE_MASK;
+
+	if (vote_y > BCM_TCS_CMD_VOTE_MASK)
+		vote_y = BCM_TCS_CMD_VOTE_MASK;
+
+	cmd->addr = addr;
+	cmd->data = BCM_TCS_CMD(commit, valid, vote_x, vote_y);
+
+	/* Set the wait for completion flag on commands that have the commit
+	 * set, in order to indicate to the RSC to not release the TCS slot
+	 * until hardware has acknowledged that this transaction has completed
+	 */
+	if (commit)
+		cmd->wait = true;
+
+	return ret;
+}
+
+static void qcom_tcs_list_gen(struct list_head *bcm_list,
+					struct tcs_cmd *tcs_list, int *n)
+{
+	struct qcom_icc_bcm *bcm;
+	bool commit;
+	size_t idx = 0, batch = 0, cur_vcd_size = 0;
+
+	memset(n, 0, sizeof(int) * SDM845_MAX_VCD);
+
+	list_for_each_entry(bcm, bcm_list, list) {
+		commit = false;
+		cur_vcd_size++;
+		if ((bcm->aux_data.vcd !=
+			list_next_entry(bcm, list)->aux_data.vcd) ||
+			list_is_last(&bcm->list, bcm_list)) {
+			commit = true;
+			cur_vcd_size = 0;
+		}
+		qcom_tcs_cmd_gen(&tcs_list[idx], bcm->vote_x, bcm->vote_y,
+				bcm->addr, commit);
+		idx++;
+		n[batch]++;
+		if (n[batch] >= MAX_RPMH_PAYLOAD) {
+			if (!commit) {
+				n[batch] -= cur_vcd_size;
+				n[batch+1] = cur_vcd_size;
+			}
+			batch++;
+		}
+	}
+}
+
+static void qcom_icc_bcm_aggregate(struct qcom_icc_bcm *bcm)
+{
+	size_t i;
+	u64 agg_avg = 0;
+	u64 agg_peak = 0;
+
+	for (i = 0; i < bcm->num_nodes; i++) {
+		agg_avg = max(agg_avg,
+			bcm->nodes[i]->sum_avg * bcm->aux_data.width /
+			(bcm->nodes[i]->buswidth * bcm->nodes[i]->channels));
+		agg_peak = max(agg_peak,
+			bcm->nodes[i]->max_peak * bcm->aux_data.width /
+			bcm->nodes[i]->buswidth);
+	}
+
+	bcm->vote_x = (u64)(agg_avg * 1000ULL / bcm->aux_data.unit);
+	bcm->vote_y = (u64)(agg_peak * 1000ULL / bcm->aux_data.unit);
+
+	if (bcm->keepalive && bcm->vote_x == 0 && bcm->vote_y == 0) {
+		bcm->vote_x = 1;
+		bcm->vote_y = 1;
+	}
+
+	bcm->dirty = false;
+}
+
+static int qcom_icc_aggregate(struct icc_node *node, u32 avg_bw,
+				u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
+{
+	size_t i;
+	struct qcom_icc_node *qn;
+
+	qn = node->data;
+
+	*agg_avg += avg_bw;
+	*agg_peak = max_t(u64, agg_peak, peak_bw);
+
+	qn->sum_avg = *agg_avg;
+	qn->max_peak = *agg_peak;
+
+	for (i = 0; i < qn->num_bcms; i++)
+		qn->bcms[i]->dirty = true;
+
+	return 0;
+}
+
+static int qcom_icc_set(struct icc_node *src, struct icc_node *dst,
+			u32 avg, u32 peak)
+{
+	struct qcom_icc_provider *qp;
+	struct qcom_icc_node *qn;
+	struct icc_node *node;
+	struct icc_provider *provider;
+	struct tcs_cmd cmds[SDM845_MAX_BCMS];
+	struct list_head commit_list;
+	int commit_idx[SDM845_MAX_VCD];
+	int ret = 0, i;
+
+	if (!src)
+		node = dst;
+	else
+		node = src;
+
+	qn = node->data;
+	provider = node->provider;
+	qp = to_qcom_provider(node->provider);
+
+	INIT_LIST_HEAD(&commit_list);
+
+	for (i = 0; i < qp->num_bcms; i++) {
+		if (qp->bcms[i]->dirty) {
+			qcom_icc_bcm_aggregate(qp->bcms[i]);
+			list_add_tail(&qp->bcms[i]->list, &commit_list);
+		}
+	}
+
+	/* Construct the command list based on a pre ordered list of BCMs
+	 * based on VCD
+	 */
+	qcom_tcs_list_gen(&commit_list, cmds, commit_idx);
+
+	if (!commit_idx[0])
+		return ret;
+
+	ret = rpmh_invalidate(qp->dev);
+	if (ret) {
+		pr_err("Error invalidating RPMH client (%d)\n", ret);
+		return ret;
+	}
+
+	ret = rpmh_write_batch(qp->dev, RPMH_ACTIVE_ONLY_STATE,
+			       cmds, commit_idx);
+	if (ret) {
+		pr_err("Error sending AMC RPMH requests (%d)\n", ret);
+		return ret;
+	}
+
+	/* TODO: collect and send wake and sleep sets */
+	return ret;
+}
+
+static int cmp_vcd(const void *_l, const void *_r)
+{
+	const struct qcom_icc_bcm **l = (const struct qcom_icc_bcm **)_l;
+	const struct qcom_icc_bcm **r = (const struct qcom_icc_bcm **)_r;
+
+	if (l[0]->aux_data.vcd < r[0]->aux_data.vcd)
+		return -1;
+	else if (l[0]->aux_data.vcd == r[0]->aux_data.vcd)
+		return 0;
+	else
+		return 1;
+}
+
+static int qnoc_probe(struct platform_device *pdev)
+{
+	const struct qcom_icc_desc *desc;
+	struct qcom_icc_node **qnodes;
+	struct qcom_icc_provider *qp;
+	struct icc_provider *provider;
+	size_t num_nodes, i;
+	int ret;
+
+	desc = of_device_get_match_data(&pdev->dev);
+	if (!desc)
+		return -EINVAL;
+
+	qnodes = desc->nodes;
+	num_nodes = desc->num_nodes;
+
+	qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
+	if (!qp)
+		return -ENOMEM;
+
+	provider = &qp->provider;
+	provider->dev = &pdev->dev;
+	provider->set = &qcom_icc_set;
+	provider->aggregate = &qcom_icc_aggregate;
+	INIT_LIST_HEAD(&provider->nodes);
+	provider->data = qp;
+
+	qp->dev = &pdev->dev;
+	qp->bcms = desc->bcms;
+	qp->num_bcms = desc->num_bcms;
+
+	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++) {
+		struct icc_node *node;
+		int ret;
+		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);
+
+		ret = qcom_icc_init(node);
+		if (ret)
+			dev_err(&pdev->dev, "%s init error (%d)\n", node->name,
+				ret);
+
+		/* populate links */
+		for (j = 0; j < qnodes[i]->num_links; j++)
+			if (qnodes[i]->links[j])
+				icc_link_create(node, qnodes[i]->links[j]);
+	}
+
+	for (i = 0; i < qp->num_bcms; i++)
+		qcom_icc_bcm_init(qp->bcms[i], &pdev->dev);
+
+	/* Pre sort the BCMs based on VCD for ease of generating a command list
+	 * that groups the BCMs with the same VCD together. VCDs are numbered
+	 * with lowest being the most expensive time wise, ensuring that
+	 * those commands are being sent the earliest in the queue.
+	 */
+	sort(qp->bcms, qp->num_bcms, sizeof(*qp->bcms), cmp_vcd, NULL);
+
+	platform_set_drvdata(pdev, provider);
+	dev_info(&pdev->dev, "Registered SDM845 ICC\n");
+
+	return ret;
+err:
+	icc_provider_del(provider);
+	return ret;
+}
+
+static int qnoc_remove(struct platform_device *pdev)
+{
+	struct icc_provider *provider = platform_get_drvdata(pdev);
+
+	icc_provider_del(provider);
+
+	return 0;
+}
+
+static const struct of_device_id qnoc_of_match[] = {
+	{ .compatible = "qcom,sdm845-rsc-hlos", .data = &sdm845_rsc_hlos },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qnoc_of_match);
+
+static struct platform_driver qnoc_driver = {
+	.probe = qnoc_probe,
+	.remove = qnoc_remove,
+	.driver = {
+		.name = "qnoc-sdm845",
+		.of_match_table = qnoc_of_match,
+	},
+};
+module_platform_driver(qnoc_driver);
+
+MODULE_AUTHOR("David Dai <daidavid1@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm sdm845 NoC 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 related	[flat|nested] 7+ messages in thread

* [RFC PATCH v2 2/2] arm64: dts: sdm845: Add interconnect provider DT nodes
  2018-07-19  2:36 [RFC PATCH v1 0/2] Add QCOM SDM845 interconnect provider driver David Dai
  2018-07-19  2:36 ` [RFC PATCH v2 1/2] interconnect: qcom: Add sdm845 " David Dai
@ 2018-07-19  2:37 ` David Dai
  1 sibling, 0 replies; 7+ messages in thread
From: David Dai @ 2018-07-19  2:37 UTC (permalink / raw)
  To: linux-kernel, linux-pm, georgi.djakov, vincent.guittot,
	bjorn.andersson, daidavid1, amit.kucheria, ilina, seansw,
	grahamr

Add RSC(Resource State Coordinator) provider
dictating network-on-chip interconnect bus performance
found on SDM845-based platforms.

Signed-off-by: David Dai <daidavid1@codeaurora.org>
---
 arch/arm64/boot/dts/qcom/sdm845.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 228d502..caffa67 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -1091,6 +1091,11 @@
 
 				pmi8998_bob: bob {};
 			};
+
+			qnoc: qnoc{
+				compatible = "qcom,sdm845-rsc-hlos";
+				#interconnect-cells = <1>;
+			};
 		};
 
 		intc: interrupt-controller@17a00000 {
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


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

* Re: [RFC PATCH v2 1/2] interconnect: qcom: Add sdm845 interconnect provider driver
  2018-07-19  2:36 ` [RFC PATCH v2 1/2] interconnect: qcom: Add sdm845 " David Dai
@ 2018-07-27 21:12   ` Evan Green
  2018-08-01  1:00     ` David Dai
  2018-08-06 22:52   ` Bjorn Andersson
  1 sibling, 1 reply; 7+ messages in thread
From: Evan Green @ 2018-07-27 21:12 UTC (permalink / raw)
  To: linux-kernel, linux-pm, georgi.djakov, Vincent Guittot,
	Bjorn Andersson, daidavid1, amit.kucheria, Lina Iyer, seansw,
	grahamr

Hi David,

On Thu, Jul 26, 2018 at 4:30 PM David Dai <daidavid1@codeaurora.org> wrote:
>
> Introduce Qualcomm SDM845 specific provider driver using the
> interconnect framework.
>
> Signed-off-by: David Dai <daidavid1@codeaurora.org>
> ---
>  .../bindings/interconnect/qcom-sdm845.txt          |  22 +
>  drivers/interconnect/qcom/Kconfig                  |   8 +
>  drivers/interconnect/qcom/Makefile                 |   1 +
>  drivers/interconnect/qcom/qcom-icc-ids.h           | 142 ++++
>  drivers/interconnect/qcom/sdm845.c                 | 826 +++++++++++++++++++++
>  5 files changed, 999 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interconnect/qcom-sdm845.txt
>  create mode 100644 drivers/interconnect/qcom/qcom-icc-ids.h
>  create mode 100644 drivers/interconnect/qcom/sdm845.c
>
...
> diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c
> new file mode 100644
> index 0000000..bf13053
> --- /dev/null
> +++ b/drivers/interconnect/qcom/sdm845.c
> @@ -0,0 +1,826 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, The Linux Foundation. All rights reserved.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/interconnect.h>
> +#include <linux/interconnect-provider.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/sort.h>
> +
> +#include <soc/qcom/cmd-db.h>
> +#include <soc/qcom/rpmh.h>
> +#include <soc/qcom/tcs.h>
> +
> +#include "qcom-icc-ids.h"
> +
> +#define BCM_TCS_CMD_COMMIT_SHFT                30
> +#define BCM_TCS_CMD_COMMIT_MASK                0x40000000
> +#define BCM_TCS_CMD_VALID_SHFT         29
> +#define BCM_TCS_CMD_VALID_MASK         0x200010000
> +#define BCM_TCS_CMD_VOTE_X_SHFT                14
> +#define BCM_TCS_CMD_VOTE_MASK          0x3fff
> +#define BCM_TCS_CMD_VOTE_Y_SHFT                0
> +#define BCM_TCS_CMD_VOTE_Y_MASK                0xfffc000
> +
> +#define BCM_TCS_CMD(commit, valid, vote_x, vote_y) \
> +       ((commit << BCM_TCS_CMD_COMMIT_SHFT) |\
> +       (valid << BCM_TCS_CMD_VALID_SHFT) |\
> +       ((vote_x & BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_X_SHFT) |\
> +       ((vote_y & BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_Y_SHFT))

These values that are >8 bits (vote_x and vote_y) should be converted
from cpu to little endian.

> +
> +#define to_qcom_provider(_provider) \
> +       container_of(_provider, struct qcom_icc_provider, provider)
> +
> +#define DEFINE_QNODE(_name, _id, _channels, _buswidth,                 \
> +                       _numlinks, ...)                                 \
> +               static struct qcom_icc_node _name = {                   \
> +               .id = _id,                                              \
> +               .name = #_name,                                         \
> +               .channels = _channels,                                  \
> +               .buswidth = _buswidth,                                  \
> +               .num_links = _numlinks,                                 \
> +               .links = { __VA_ARGS__ },                               \
> +       }
> +
> +#define DEFINE_QBCM(_name, _bcmname, _keepalive, _numnodes, ...)       \
> +               static struct qcom_icc_bcm _name = {                    \
> +               .name = _bcmname,                                       \
> +               .keepalive = _keepalive,                                \
> +               .num_nodes = _numnodes,                                 \
> +               .nodes = { __VA_ARGS__ },                               \
> +       }
> +
> +struct qcom_icc_provider {
> +       struct icc_provider     provider;
> +       void __iomem            *base;
> +       struct device           *dev;
> +       struct qcom_icc_bcm     **bcms;
> +       size_t num_bcms;
> +};
> +
> +/**
> + * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager(BCM)
> + * @unit: bcm threshold values are in magnitudes of this
> + * @width: prototype width
> + * @vcd: virtual clock domain that this bcm belongs to
> + */
> +
> +struct bcm_db {
> +       u32 unit;
> +       u16 width;
> +       u8 vcd;
> +       u8 reserved;
> +};
> +
> +#define SDM845_MAX_LINKS       43
> +#define SDM845_MAX_BCMS                30
> +#define SDM845_MAX_BCM_PER_NODE        2
> +#define SDM845_MAX_VCD         10
> +
> +/**
> + * 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
> + * @channels: num of channels at this node
> + * @buswidth: width of the interconnect between a node and the bus
> + * @sum_avg: current sum aggregate value of all avg bw requests
> + * @max_peak: current max aggregate value of all peak bw requests
> + * @bcms: list of bcms associated with this logical node
> + * @num_bcm: num of @bcms
> + */
> +struct qcom_icc_node {
> +       const char *name;
> +       u16 links[SDM845_MAX_LINKS];
> +       u16 id;
> +       u16 num_links;
> +       u16 channels;
> +       u16 buswidth;
> +       u64 sum_avg;
> +       u64 max_peak;
> +       struct qcom_icc_bcm *bcms[SDM845_MAX_BCM_PER_NODE];
> +       size_t num_bcms;
> +};
> +
> +/**
> + * struct qcom_icc_bcm - Qualcomm specific hardware accelerator nodes
> + * known as Bus Clock Manager(BCM)
> + * @name: the bcm node name used to fetch BCM data from command db
> + * @type: latency or bandwidth bcm
> + * @addr: address offsets used when voting to RPMH
> + * @vote_x: aggregated threshold values, represents sum_bw when @type is bw bcm
> + * @vote_y: aggregated threshold values, represents peak_bw when @type is bw bcm
> + * @dirty: flag used to indicate whether or bcm needs to be committed
> + * @aux_data: auxiliary data used when calculating threshold values and
> + * communicating with RPMh
> + * @list: used to link to other bcms when compiling lists for commit
> + * @num_nodes: total number of @num_nodes
> + * @nodes: list of qcom_icc_nodes that this BCM encapsulates
> + */
> +
> +struct qcom_icc_bcm {
> +       const char *name;
> +       u32 type;
> +       u32 addr;
> +       u64 vote_x;
> +       u64 vote_y;
> +       bool dirty;
> +       bool keepalive;
> +       struct bcm_db aux_data;
> +       struct list_head list;
> +       size_t num_nodes;
> +       struct qcom_icc_node *nodes[];
> +};
> +
> +struct qcom_icc_fabric {
> +       struct qcom_icc_node **nodes;
> +       size_t num_nodes;
> +       u32 base_offset;
> +       u32 qos_offset;
> +};
> +
> +struct qcom_icc_desc {
> +       struct qcom_icc_node **nodes;
> +       size_t num_nodes;
> +       struct qcom_icc_bcm **bcms;
> +       size_t num_bcms;
> +};
> +
...
> +
> +static int qcom_icc_init(struct icc_node *node)
> +{
> +       /* TODO: init qos and priority */
> +
> +       return 0;
> +}
> +
> +static int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
> +{
> +       struct qcom_icc_node *qn;
> +       int ret, i;
> +
> +       bcm->addr = cmd_db_read_addr(bcm->name);
> +       if (!bcm->addr) {
> +               dev_err(dev, "%s could not find RPMh address\n",
> +                       bcm->name);
> +               return -EINVAL;
> +       }
> +
> +       if (!cmd_db_read_aux_data_len(bcm->name)) {

I think you should validate that the aux data length being returned is
the length you expect it to be. Or maybe "at least" the length you
expect it to be, if this structure is meant to be backwards
compatible?

> +               dev_err(dev, "%s command db missing aux data\n",
> +                       bcm->name);
> +               return -EINVAL;
> +       }
> +
> +       ret = cmd_db_read_aux_data(bcm->name, (u8 *)&bcm->aux_data,
> +                                  sizeof(struct bcm_db));

This still doesn't work for big endian, after being read the
multi-byte members of this structure need to be converted from little
endian to cpu format.

> +       if (ret < 0) {
> +               dev_err(dev, "%s command db read error (%d)\n",
> +                       bcm->name, ret);
> +               return ret;
> +       }
> +
> +       for (i = 0; i < bcm->num_nodes; i++) {

This loop could use a comment describing what it's doing, which I
understand to be: Add links back to the BCM for each node in the BCM.

> +               qn = bcm->nodes[i];
> +               qn->bcms[qn->num_bcms] = bcm;
> +               qn->num_bcms++;
> +       }
> +
> +       return 0;
> +}
> +
> +static int qcom_tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x,
> +                                       u64 vote_y, u32 addr, bool commit)
> +{
> +       int ret = 0;
> +       bool valid = true;
> +
> +       if (!cmd)
> +               return ret;
> +
> +       if (vote_x == 0 && vote_y == 0)
> +               valid = false;
> +
> +       if (vote_x > BCM_TCS_CMD_VOTE_MASK)
> +               vote_x = BCM_TCS_CMD_VOTE_MASK;
> +
> +       if (vote_y > BCM_TCS_CMD_VOTE_MASK)
> +               vote_y = BCM_TCS_CMD_VOTE_MASK;
> +
> +       cmd->addr = addr;
> +       cmd->data = BCM_TCS_CMD(commit, valid, vote_x, vote_y);
> +
> +       /* Set the wait for completion flag on commands that have the commit

I think the preferred style of multiline commenting has /* on its own line.

> +        * set, in order to indicate to the RSC to not release the TCS slot
> +        * until hardware has acknowledged that this transaction has completed
> +        */
> +       if (commit)
> +               cmd->wait = true;
> +
> +       return ret;
> +}
> +
> +static void qcom_tcs_list_gen(struct list_head *bcm_list,
> +                                       struct tcs_cmd *tcs_list, int *n)
> +{
> +       struct qcom_icc_bcm *bcm;
> +       bool commit;
> +       size_t idx = 0, batch = 0, cur_vcd_size = 0;
> +
> +       memset(n, 0, sizeof(int) * SDM845_MAX_VCD);
> +
> +       list_for_each_entry(bcm, bcm_list, list) {
> +               commit = false;
> +               cur_vcd_size++;
> +               if ((bcm->aux_data.vcd !=
> +                       list_next_entry(bcm, list)->aux_data.vcd) ||
> +                       list_is_last(&bcm->list, bcm_list)) {

You should switch the order of these conditions. Otherwise for the
last node you end up reaching through the list head object to get
aux_data.vcd, which is a wild memory access.

> +                       commit = true;
> +                       cur_vcd_size = 0;
> +               }
> +               qcom_tcs_cmd_gen(&tcs_list[idx], bcm->vote_x, bcm->vote_y,
> +                               bcm->addr, commit);
> +               idx++;
> +               n[batch]++;
> +               if (n[batch] >= MAX_RPMH_PAYLOAD) {
> +                       if (!commit) {
> +                               n[batch] -= cur_vcd_size;
> +                               n[batch+1] = cur_vcd_size;
> +                       }
> +                       batch++;

I think I have my head around this, but it at least needs a comment
describing what it's doing, because it's still mind boggling. My
understanding is: "Avoid splitting requests for the same BCM across
multiple RPMh payloads. If the current set of BCM requests would be
split, move it to the next batch."

I would also include a warning if cur_vcd_size is > MAX_RPMH_PAYLOAD,
or if n[batch] <= cur_vcd_size. This would help with readability, and
help catch any sort of crazy situation not anticipated by this logic.

> +               }
> +       }
> +}
> +
> +static void qcom_icc_bcm_aggregate(struct qcom_icc_bcm *bcm)
> +{
> +       size_t i;
> +       u64 agg_avg = 0;
> +       u64 agg_peak = 0;
> +
> +       for (i = 0; i < bcm->num_nodes; i++) {
> +               agg_avg = max(agg_avg,
> +                       bcm->nodes[i]->sum_avg * bcm->aux_data.width /
> +                       (bcm->nodes[i]->buswidth * bcm->nodes[i]->channels));
> +               agg_peak = max(agg_peak,
> +                       bcm->nodes[i]->max_peak * bcm->aux_data.width /
> +                       bcm->nodes[i]->buswidth);
> +       }
> +
> +       bcm->vote_x = (u64)(agg_avg * 1000ULL / bcm->aux_data.unit);
> +       bcm->vote_y = (u64)(agg_peak * 1000ULL / bcm->aux_data.unit);
> +
> +       if (bcm->keepalive && bcm->vote_x == 0 && bcm->vote_y == 0) {
> +               bcm->vote_x = 1;
> +               bcm->vote_y = 1;
> +       }
> +
> +       bcm->dirty = false;
> +}
> +
> +static int qcom_icc_aggregate(struct icc_node *node, u32 avg_bw,
> +                               u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
> +{
> +       size_t i;
> +       struct qcom_icc_node *qn;
> +
> +       qn = node->data;
> +
> +       *agg_avg += avg_bw;
> +       *agg_peak = max_t(u64, agg_peak, peak_bw);
> +
> +       qn->sum_avg = *agg_avg;
> +       qn->max_peak = *agg_peak;
> +
> +       for (i = 0; i < qn->num_bcms; i++)
> +               qn->bcms[i]->dirty = true;
> +
> +       return 0;
> +}
> +
> +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst,
> +                       u32 avg, u32 peak)
> +{
> +       struct qcom_icc_provider *qp;
> +       struct qcom_icc_node *qn;
> +       struct icc_node *node;
> +       struct icc_provider *provider;
> +       struct tcs_cmd cmds[SDM845_MAX_BCMS];
> +       struct list_head commit_list;
> +       int commit_idx[SDM845_MAX_VCD];
> +       int ret = 0, i;
> +
> +       if (!src)
> +               node = dst;
> +       else
> +               node = src;
> +
> +       qn = node->data;
> +       provider = node->provider;
> +       qp = to_qcom_provider(node->provider);
> +
> +       INIT_LIST_HEAD(&commit_list);
> +
> +       for (i = 0; i < qp->num_bcms; i++) {
> +               if (qp->bcms[i]->dirty) {
> +                       qcom_icc_bcm_aggregate(qp->bcms[i]);
> +                       list_add_tail(&qp->bcms[i]->list, &commit_list);
> +               }
> +       }
> +
> +       /* Construct the command list based on a pre ordered list of BCMs

Another networking-style comment.

> +        * based on VCD

period?

> +        */
> +       qcom_tcs_list_gen(&commit_list, cmds, commit_idx);
> +
> +       if (!commit_idx[0])
> +               return ret;
> +
> +       ret = rpmh_invalidate(qp->dev);
> +       if (ret) {
> +               pr_err("Error invalidating RPMH client (%d)\n", ret);
> +               return ret;
> +       }
> +
> +       ret = rpmh_write_batch(qp->dev, RPMH_ACTIVE_ONLY_STATE,
> +                              cmds, commit_idx);
> +       if (ret) {
> +               pr_err("Error sending AMC RPMH requests (%d)\n", ret);
> +               return ret;
> +       }
> +
> +       /* TODO: collect and send wake and sleep sets */
> +       return ret;
> +}
> +
> +static int cmp_vcd(const void *_l, const void *_r)
> +{
> +       const struct qcom_icc_bcm **l = (const struct qcom_icc_bcm **)_l;
> +       const struct qcom_icc_bcm **r = (const struct qcom_icc_bcm **)_r;
> +
> +       if (l[0]->aux_data.vcd < r[0]->aux_data.vcd)
> +               return -1;
> +       else if (l[0]->aux_data.vcd == r[0]->aux_data.vcd)
> +               return 0;
> +       else
> +               return 1;
> +}
> +
> +static int qnoc_probe(struct platform_device *pdev)
> +{
> +       const struct qcom_icc_desc *desc;
> +       struct qcom_icc_node **qnodes;
> +       struct qcom_icc_provider *qp;
> +       struct icc_provider *provider;
> +       size_t num_nodes, i;
> +       int ret;
> +
> +       desc = of_device_get_match_data(&pdev->dev);
> +       if (!desc)
> +               return -EINVAL;
> +
> +       qnodes = desc->nodes;
> +       num_nodes = desc->num_nodes;
> +
> +       qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
> +       if (!qp)
> +               return -ENOMEM;
> +
> +       provider = &qp->provider;
> +       provider->dev = &pdev->dev;
> +       provider->set = &qcom_icc_set;
> +       provider->aggregate = &qcom_icc_aggregate;
> +       INIT_LIST_HEAD(&provider->nodes);
> +       provider->data = qp;
> +
> +       qp->dev = &pdev->dev;
> +       qp->bcms = desc->bcms;
> +       qp->num_bcms = desc->num_bcms;
> +
> +       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++) {
> +               struct icc_node *node;
> +               int ret;
> +               size_t j;
> +
> +               node = icc_node_create(qnodes[i]->id);
> +               if (IS_ERR(node)) {
> +                       ret = PTR_ERR(node);
> +                       goto err;

I think you need to clean up the nodes you've already created and
added, otherwise provider_del will end up printing a warning and
failing.

> +               }
> +
> +               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);
> +
> +               ret = qcom_icc_init(node);
> +               if (ret)
> +                       dev_err(&pdev->dev, "%s init error (%d)\n", node->name,
> +                               ret);
> +
> +               /* populate links */
> +               for (j = 0; j < qnodes[i]->num_links; j++)
> +                       if (qnodes[i]->links[j])
> +                               icc_link_create(node, qnodes[i]->links[j]);
> +       }
> +
> +       for (i = 0; i < qp->num_bcms; i++)
> +               qcom_icc_bcm_init(qp->bcms[i], &pdev->dev);
> +
> +       /* Pre sort the BCMs based on VCD for ease of generating a command list

Another comment that needs the /* line on its own. It turns out I do
this a lot too :)

> +        * that groups the BCMs with the same VCD together. VCDs are numbered
> +        * with lowest being the most expensive time wise, ensuring that
> +        * those commands are being sent the earliest in the queue.
> +        */
> +       sort(qp->bcms, qp->num_bcms, sizeof(*qp->bcms), cmp_vcd, NULL);
> +
> +       platform_set_drvdata(pdev, provider);
> +       dev_info(&pdev->dev, "Registered SDM845 ICC\n");
> +
> +       return ret;
> +err:
> +       icc_provider_del(provider);
> +       return ret;
> +}
> +
> +static int qnoc_remove(struct platform_device *pdev)
> +{
> +       struct icc_provider *provider = platform_get_drvdata(pdev);
> +
> +       icc_provider_del(provider);

Don't you need to tear all the nodes down too? Otherwise this function
will fail. Should you check the return value as well and complain at
least, or maybe fail qnoc_remove entirely?

> +
> +       return 0;
> +}
> +
> +static const struct of_device_id qnoc_of_match[] = {
> +       { .compatible = "qcom,sdm845-rsc-hlos", .data = &sdm845_rsc_hlos },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, qnoc_of_match);
> +
> +static struct platform_driver qnoc_driver = {
> +       .probe = qnoc_probe,
> +       .remove = qnoc_remove,
> +       .driver = {
> +               .name = "qnoc-sdm845",
> +               .of_match_table = qnoc_of_match,
> +       },
> +};
> +module_platform_driver(qnoc_driver);
> +
> +MODULE_AUTHOR("David Dai <daidavid1@codeaurora.org>");
> +MODULE_DESCRIPTION("Qualcomm sdm845 NoC driver");
> +MODULE_LICENSE("GPL v2");

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

* Re: [RFC PATCH v2 1/2] interconnect: qcom: Add sdm845 interconnect provider driver
  2018-07-27 21:12   ` Evan Green
@ 2018-08-01  1:00     ` David Dai
  0 siblings, 0 replies; 7+ messages in thread
From: David Dai @ 2018-08-01  1:00 UTC (permalink / raw)
  To: Evan Green, linux-kernel, linux-pm, georgi.djakov,
	Vincent Guittot, Bjorn Andersson, amit.kucheria, Lina Iyer,
	seansw, grahamr

Hi Evan,

Thanks for taking the time to review and feedback!


On 7/27/2018 2:12 PM, Evan Green wrote:
> Hi David,
>
> On Thu, Jul 26, 2018 at 4:30 PM David Dai <daidavid1@codeaurora.org> wrote:
>> Introduce Qualcomm SDM845 specific provider driver using the
>> interconnect framework.
>>
>> Signed-off-by: David Dai <daidavid1@codeaurora.org>
>> ---
>>   .../bindings/interconnect/qcom-sdm845.txt          |  22 +
>>   drivers/interconnect/qcom/Kconfig                  |   8 +
>>   drivers/interconnect/qcom/Makefile                 |   1 +
>>   drivers/interconnect/qcom/qcom-icc-ids.h           | 142 ++++
>>   drivers/interconnect/qcom/sdm845.c                 | 826 +++++++++++++++++++++
>>   5 files changed, 999 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/interconnect/qcom-sdm845.txt
>>   create mode 100644 drivers/interconnect/qcom/qcom-icc-ids.h
>>   create mode 100644 drivers/interconnect/qcom/sdm845.c
>>
> ...
>> diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c
>> new file mode 100644
>> index 0000000..bf13053
>> --- /dev/null
>> +++ b/drivers/interconnect/qcom/sdm845.c
>> @@ -0,0 +1,826 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2018, The Linux Foundation. All rights reserved.
>> + *
>> + */
>> +
>> +#include <linux/device.h>
>> +#include <linux/io.h>
>> +#include <linux/interconnect.h>
>> +#include <linux/interconnect-provider.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/sort.h>
>> +
>> +#include <soc/qcom/cmd-db.h>
>> +#include <soc/qcom/rpmh.h>
>> +#include <soc/qcom/tcs.h>
>> +
>> +#include "qcom-icc-ids.h"
>> +
>> +#define BCM_TCS_CMD_COMMIT_SHFT                30
>> +#define BCM_TCS_CMD_COMMIT_MASK                0x40000000
>> +#define BCM_TCS_CMD_VALID_SHFT         29
>> +#define BCM_TCS_CMD_VALID_MASK         0x200010000
>> +#define BCM_TCS_CMD_VOTE_X_SHFT                14
>> +#define BCM_TCS_CMD_VOTE_MASK          0x3fff
>> +#define BCM_TCS_CMD_VOTE_Y_SHFT                0
>> +#define BCM_TCS_CMD_VOTE_Y_MASK                0xfffc000
>> +
>> +#define BCM_TCS_CMD(commit, valid, vote_x, vote_y) \
>> +       ((commit << BCM_TCS_CMD_COMMIT_SHFT) |\
>> +       (valid << BCM_TCS_CMD_VALID_SHFT) |\
>> +       ((vote_x & BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_X_SHFT) |\
>> +       ((vote_y & BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_Y_SHFT))
> These values that are >8 bits (vote_x and vote_y) should be converted
> from cpu to little endian.
Done.
>
>> +
>> +#define to_qcom_provider(_provider) \
>> +       container_of(_provider, struct qcom_icc_provider, provider)
>> +
>> +#define DEFINE_QNODE(_name, _id, _channels, _buswidth,                 \
>> +                       _numlinks, ...)                                 \
>> +               static struct qcom_icc_node _name = {                   \
>> +               .id = _id,                                              \
>> +               .name = #_name,                                         \
>> +               .channels = _channels,                                  \
>> +               .buswidth = _buswidth,                                  \
>> +               .num_links = _numlinks,                                 \
>> +               .links = { __VA_ARGS__ },                               \
>> +       }
>> +
>> +#define DEFINE_QBCM(_name, _bcmname, _keepalive, _numnodes, ...)       \
>> +               static struct qcom_icc_bcm _name = {                    \
>> +               .name = _bcmname,                                       \
>> +               .keepalive = _keepalive,                                \
>> +               .num_nodes = _numnodes,                                 \
>> +               .nodes = { __VA_ARGS__ },                               \
>> +       }
>> +
>> +struct qcom_icc_provider {
>> +       struct icc_provider     provider;
>> +       void __iomem            *base;
>> +       struct device           *dev;
>> +       struct qcom_icc_bcm     **bcms;
>> +       size_t num_bcms;
>> +};
>> +
>> +/**
>> + * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager(BCM)
>> + * @unit: bcm threshold values are in magnitudes of this
>> + * @width: prototype width
>> + * @vcd: virtual clock domain that this bcm belongs to
>> + */
>> +
>> +struct bcm_db {
>> +       u32 unit;
>> +       u16 width;
>> +       u8 vcd;
>> +       u8 reserved;
>> +};
>> +
>> +#define SDM845_MAX_LINKS       43
>> +#define SDM845_MAX_BCMS                30
>> +#define SDM845_MAX_BCM_PER_NODE        2
>> +#define SDM845_MAX_VCD         10
>> +
>> +/**
>> + * 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
>> + * @channels: num of channels at this node
>> + * @buswidth: width of the interconnect between a node and the bus
>> + * @sum_avg: current sum aggregate value of all avg bw requests
>> + * @max_peak: current max aggregate value of all peak bw requests
>> + * @bcms: list of bcms associated with this logical node
>> + * @num_bcm: num of @bcms
>> + */
>> +struct qcom_icc_node {
>> +       const char *name;
>> +       u16 links[SDM845_MAX_LINKS];
>> +       u16 id;
>> +       u16 num_links;
>> +       u16 channels;
>> +       u16 buswidth;
>> +       u64 sum_avg;
>> +       u64 max_peak;
>> +       struct qcom_icc_bcm *bcms[SDM845_MAX_BCM_PER_NODE];
>> +       size_t num_bcms;
>> +};
>> +
>> +/**
>> + * struct qcom_icc_bcm - Qualcomm specific hardware accelerator nodes
>> + * known as Bus Clock Manager(BCM)
>> + * @name: the bcm node name used to fetch BCM data from command db
>> + * @type: latency or bandwidth bcm
>> + * @addr: address offsets used when voting to RPMH
>> + * @vote_x: aggregated threshold values, represents sum_bw when @type is bw bcm
>> + * @vote_y: aggregated threshold values, represents peak_bw when @type is bw bcm
>> + * @dirty: flag used to indicate whether or bcm needs to be committed
>> + * @aux_data: auxiliary data used when calculating threshold values and
>> + * communicating with RPMh
>> + * @list: used to link to other bcms when compiling lists for commit
>> + * @num_nodes: total number of @num_nodes
>> + * @nodes: list of qcom_icc_nodes that this BCM encapsulates
>> + */
>> +
>> +struct qcom_icc_bcm {
>> +       const char *name;
>> +       u32 type;
>> +       u32 addr;
>> +       u64 vote_x;
>> +       u64 vote_y;
>> +       bool dirty;
>> +       bool keepalive;
>> +       struct bcm_db aux_data;
>> +       struct list_head list;
>> +       size_t num_nodes;
>> +       struct qcom_icc_node *nodes[];
>> +};
>> +
>> +struct qcom_icc_fabric {
>> +       struct qcom_icc_node **nodes;
>> +       size_t num_nodes;
>> +       u32 base_offset;
>> +       u32 qos_offset;
>> +};
>> +
>> +struct qcom_icc_desc {
>> +       struct qcom_icc_node **nodes;
>> +       size_t num_nodes;
>> +       struct qcom_icc_bcm **bcms;
>> +       size_t num_bcms;
>> +};
>> +
> ...
>> +
>> +static int qcom_icc_init(struct icc_node *node)
>> +{
>> +       /* TODO: init qos and priority */
>> +
>> +       return 0;
>> +}
>> +
>> +static int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
>> +{
>> +       struct qcom_icc_node *qn;
>> +       int ret, i;
>> +
>> +       bcm->addr = cmd_db_read_addr(bcm->name);
>> +       if (!bcm->addr) {
>> +               dev_err(dev, "%s could not find RPMh address\n",
>> +                       bcm->name);
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (!cmd_db_read_aux_data_len(bcm->name)) {
> I think you should validate that the aux data length being returned is
> the length you expect it to be. Or maybe "at least" the length you
> expect it to be, if this structure is meant to be backwards
> compatible?
Done.
>> +               dev_err(dev, "%s command db missing aux data\n",
>> +                       bcm->name);
>> +               return -EINVAL;
>> +       }
>> +
>> +       ret = cmd_db_read_aux_data(bcm->name, (u8 *)&bcm->aux_data,
>> +                                  sizeof(struct bcm_db));
> This still doesn't work for big endian, after being read the
> multi-byte members of this structure need to be converted from little
> endian to cpu format.
Done. I'll convert the values to cpu format.
>> +       if (ret < 0) {
>> +               dev_err(dev, "%s command db read error (%d)\n",
>> +                       bcm->name, ret);
>> +               return ret;
>> +       }
>> +
>> +       for (i = 0; i < bcm->num_nodes; i++) {
> This loop could use a comment describing what it's doing, which I
> understand to be: Add links back to the BCM for each node in the BCM.
Done.
>> +               qn = bcm->nodes[i];
>> +               qn->bcms[qn->num_bcms] = bcm;
>> +               qn->num_bcms++;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int qcom_tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x,
>> +                                       u64 vote_y, u32 addr, bool commit)
>> +{
>> +       int ret = 0;
>> +       bool valid = true;
>> +
>> +       if (!cmd)
>> +               return ret;
>> +
>> +       if (vote_x == 0 && vote_y == 0)
>> +               valid = false;
>> +
>> +       if (vote_x > BCM_TCS_CMD_VOTE_MASK)
>> +               vote_x = BCM_TCS_CMD_VOTE_MASK;
>> +
>> +       if (vote_y > BCM_TCS_CMD_VOTE_MASK)
>> +               vote_y = BCM_TCS_CMD_VOTE_MASK;
>> +
>> +       cmd->addr = addr;
>> +       cmd->data = BCM_TCS_CMD(commit, valid, vote_x, vote_y);
>> +
>> +       /* Set the wait for completion flag on commands that have the commit
> I think the preferred style of multiline commenting has /* on its own line.
Done.
>> +        * set, in order to indicate to the RSC to not release the TCS slot
>> +        * until hardware has acknowledged that this transaction has completed
>> +        */
>> +       if (commit)
>> +               cmd->wait = true;
>> +
>> +       return ret;
>> +}
>> +
>> +static void qcom_tcs_list_gen(struct list_head *bcm_list,
>> +                                       struct tcs_cmd *tcs_list, int *n)
>> +{
>> +       struct qcom_icc_bcm *bcm;
>> +       bool commit;
>> +       size_t idx = 0, batch = 0, cur_vcd_size = 0;
>> +
>> +       memset(n, 0, sizeof(int) * SDM845_MAX_VCD);
>> +
>> +       list_for_each_entry(bcm, bcm_list, list) {
>> +               commit = false;
>> +               cur_vcd_size++;
>> +               if ((bcm->aux_data.vcd !=
>> +                       list_next_entry(bcm, list)->aux_data.vcd) ||
>> +                       list_is_last(&bcm->list, bcm_list)) {
> You should switch the order of these conditions. Otherwise for the
> last node you end up reaching through the list head object to get
> aux_data.vcd, which is a wild memory access.
In my mind at the time, it didn't really matter that the last member 
compared itself against head, since it would have a different VCD 
anyways unless there's only a single VCD in the entire commit list, 
which is what the list_is_list check takes care of. Though now that you 
mention it, it shouldn't need to compare at all if it sees that it's the 
last entry. I'll swap them.
>> +                       commit = true;
>> +                       cur_vcd_size = 0;
>> +               }
>> +               qcom_tcs_cmd_gen(&tcs_list[idx], bcm->vote_x, bcm->vote_y,
>> +                               bcm->addr, commit);
>> +               idx++;
>> +               n[batch]++;
>> +               if (n[batch] >= MAX_RPMH_PAYLOAD) {
>> +                       if (!commit) {
>> +                               n[batch] -= cur_vcd_size;
>> +                               n[batch+1] = cur_vcd_size;
>> +                       }
>> +                       batch++;
> I think I have my head around this, but it at least needs a comment
> describing what it's doing, because it's still mind boggling. My
> understanding is: "Avoid splitting requests for the same BCM across
> multiple RPMh payloads. If the current set of BCM requests would be
> split, move it to the next batch."
I agree that the logic here gets a bit busy, will add comments to clarify.
>
> I would also include a warning if cur_vcd_size is > MAX_RPMH_PAYLOAD,
> or if n[batch] <= cur_vcd_size. This would help with readability, and
> help catch any sort of crazy situation not anticipated by this logic.
hm, I think cur_vcd_size is > MAX_RPMH_PAYLOAD is a useful check if 
somehow we got bad data to begin with, n[batch] <= cur_vcd_size is only 
a useful check when cur_vcd_size is > MAX_RPMH_PAYLOAD, since n[batch] 
== cur_vcd_size can be valid if cur_vcd_size == MAX_RPMH_PAYLOAD.
>> +               }
>> +       }
>> +}
>> +
>> +static void qcom_icc_bcm_aggregate(struct qcom_icc_bcm *bcm)
>> +{
>> +       size_t i;
>> +       u64 agg_avg = 0;
>> +       u64 agg_peak = 0;
>> +
>> +       for (i = 0; i < bcm->num_nodes; i++) {
>> +               agg_avg = max(agg_avg,
>> +                       bcm->nodes[i]->sum_avg * bcm->aux_data.width /
>> +                       (bcm->nodes[i]->buswidth * bcm->nodes[i]->channels));
>> +               agg_peak = max(agg_peak,
>> +                       bcm->nodes[i]->max_peak * bcm->aux_data.width /
>> +                       bcm->nodes[i]->buswidth);
>> +       }
>> +
>> +       bcm->vote_x = (u64)(agg_avg * 1000ULL / bcm->aux_data.unit);
>> +       bcm->vote_y = (u64)(agg_peak * 1000ULL / bcm->aux_data.unit);
>> +
>> +       if (bcm->keepalive && bcm->vote_x == 0 && bcm->vote_y == 0) {
>> +               bcm->vote_x = 1;
>> +               bcm->vote_y = 1;
>> +       }
>> +
>> +       bcm->dirty = false;
>> +}
>> +
>> +static int qcom_icc_aggregate(struct icc_node *node, u32 avg_bw,
>> +                               u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
>> +{
>> +       size_t i;
>> +       struct qcom_icc_node *qn;
>> +
>> +       qn = node->data;
>> +
>> +       *agg_avg += avg_bw;
>> +       *agg_peak = max_t(u64, agg_peak, peak_bw);
>> +
>> +       qn->sum_avg = *agg_avg;
>> +       qn->max_peak = *agg_peak;
>> +
>> +       for (i = 0; i < qn->num_bcms; i++)
>> +               qn->bcms[i]->dirty = true;
>> +
>> +       return 0;
>> +}
>> +
>> +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst,
>> +                       u32 avg, u32 peak)
>> +{
>> +       struct qcom_icc_provider *qp;
>> +       struct qcom_icc_node *qn;
>> +       struct icc_node *node;
>> +       struct icc_provider *provider;
>> +       struct tcs_cmd cmds[SDM845_MAX_BCMS];
>> +       struct list_head commit_list;
>> +       int commit_idx[SDM845_MAX_VCD];
>> +       int ret = 0, i;
>> +
>> +       if (!src)
>> +               node = dst;
>> +       else
>> +               node = src;
>> +
>> +       qn = node->data;
>> +       provider = node->provider;
>> +       qp = to_qcom_provider(node->provider);
>> +
>> +       INIT_LIST_HEAD(&commit_list);
>> +
>> +       for (i = 0; i < qp->num_bcms; i++) {
>> +               if (qp->bcms[i]->dirty) {
>> +                       qcom_icc_bcm_aggregate(qp->bcms[i]);
>> +                       list_add_tail(&qp->bcms[i]->list, &commit_list);
>> +               }
>> +       }
>> +
>> +       /* Construct the command list based on a pre ordered list of BCMs
> Another networking-style comment.
Done.
>
>> +        * based on VCD
> period?
Done.
>
>> +        */
>> +       qcom_tcs_list_gen(&commit_list, cmds, commit_idx);
>> +
>> +       if (!commit_idx[0])
>> +               return ret;
>> +
>> +       ret = rpmh_invalidate(qp->dev);
>> +       if (ret) {
>> +               pr_err("Error invalidating RPMH client (%d)\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       ret = rpmh_write_batch(qp->dev, RPMH_ACTIVE_ONLY_STATE,
>> +                              cmds, commit_idx);
>> +       if (ret) {
>> +               pr_err("Error sending AMC RPMH requests (%d)\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       /* TODO: collect and send wake and sleep sets */
>> +       return ret;
>> +}
>> +
>> +static int cmp_vcd(const void *_l, const void *_r)
>> +{
>> +       const struct qcom_icc_bcm **l = (const struct qcom_icc_bcm **)_l;
>> +       const struct qcom_icc_bcm **r = (const struct qcom_icc_bcm **)_r;
>> +
>> +       if (l[0]->aux_data.vcd < r[0]->aux_data.vcd)
>> +               return -1;
>> +       else if (l[0]->aux_data.vcd == r[0]->aux_data.vcd)
>> +               return 0;
>> +       else
>> +               return 1;
>> +}
>> +
>> +static int qnoc_probe(struct platform_device *pdev)
>> +{
>> +       const struct qcom_icc_desc *desc;
>> +       struct qcom_icc_node **qnodes;
>> +       struct qcom_icc_provider *qp;
>> +       struct icc_provider *provider;
>> +       size_t num_nodes, i;
>> +       int ret;
>> +
>> +       desc = of_device_get_match_data(&pdev->dev);
>> +       if (!desc)
>> +               return -EINVAL;
>> +
>> +       qnodes = desc->nodes;
>> +       num_nodes = desc->num_nodes;
>> +
>> +       qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
>> +       if (!qp)
>> +               return -ENOMEM;
>> +
>> +       provider = &qp->provider;
>> +       provider->dev = &pdev->dev;
>> +       provider->set = &qcom_icc_set;
>> +       provider->aggregate = &qcom_icc_aggregate;
>> +       INIT_LIST_HEAD(&provider->nodes);
>> +       provider->data = qp;
>> +
>> +       qp->dev = &pdev->dev;
>> +       qp->bcms = desc->bcms;
>> +       qp->num_bcms = desc->num_bcms;
>> +
>> +       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++) {
>> +               struct icc_node *node;
>> +               int ret;
>> +               size_t j;
>> +
>> +               node = icc_node_create(qnodes[i]->id);
>> +               if (IS_ERR(node)) {
>> +                       ret = PTR_ERR(node);
>> +                       goto err;
> I think you need to clean up the nodes you've already created and
> added, otherwise provider_del will end up printing a warning and
> failing.
Done.
>> +               }
>> +
>> +               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);
>> +
>> +               ret = qcom_icc_init(node);
>> +               if (ret)
>> +                       dev_err(&pdev->dev, "%s init error (%d)\n", node->name,
>> +                               ret);
>> +
>> +               /* populate links */
>> +               for (j = 0; j < qnodes[i]->num_links; j++)
>> +                       if (qnodes[i]->links[j])
>> +                               icc_link_create(node, qnodes[i]->links[j]);
>> +       }
>> +
>> +       for (i = 0; i < qp->num_bcms; i++)
>> +               qcom_icc_bcm_init(qp->bcms[i], &pdev->dev);
>> +
>> +       /* Pre sort the BCMs based on VCD for ease of generating a command list
> Another comment that needs the /* line on its own. It turns out I do
> this a lot too :)
Done.
>> +        * that groups the BCMs with the same VCD together. VCDs are numbered
>> +        * with lowest being the most expensive time wise, ensuring that
>> +        * those commands are being sent the earliest in the queue.
>> +        */
>> +       sort(qp->bcms, qp->num_bcms, sizeof(*qp->bcms), cmp_vcd, NULL);
>> +
>> +       platform_set_drvdata(pdev, provider);
>> +       dev_info(&pdev->dev, "Registered SDM845 ICC\n");
>> +
>> +       return ret;
>> +err:
>> +       icc_provider_del(provider);
>> +       return ret;
>> +}
>> +
>> +static int qnoc_remove(struct platform_device *pdev)
>> +{
>> +       struct icc_provider *provider = platform_get_drvdata(pdev);
>> +
>> +       icc_provider_del(provider);
> Don't you need to tear all the nodes down too? Otherwise this function
> will fail. Should you check the return value as well and complain at
> least, or maybe fail qnoc_remove entirely?
Done.
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id qnoc_of_match[] = {
>> +       { .compatible = "qcom,sdm845-rsc-hlos", .data = &sdm845_rsc_hlos },
>> +       { },
>> +};
>> +MODULE_DEVICE_TABLE(of, qnoc_of_match);
>> +
>> +static struct platform_driver qnoc_driver = {
>> +       .probe = qnoc_probe,
>> +       .remove = qnoc_remove,
>> +       .driver = {
>> +               .name = "qnoc-sdm845",
>> +               .of_match_table = qnoc_of_match,
>> +       },
>> +};
>> +module_platform_driver(qnoc_driver);
>> +
>> +MODULE_AUTHOR("David Dai <daidavid1@codeaurora.org>");
>> +MODULE_DESCRIPTION("Qualcomm sdm845 NoC 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] 7+ messages in thread

* Re: [RFC PATCH v2 1/2] interconnect: qcom: Add sdm845 interconnect provider driver
  2018-07-19  2:36 ` [RFC PATCH v2 1/2] interconnect: qcom: Add sdm845 " David Dai
  2018-07-27 21:12   ` Evan Green
@ 2018-08-06 22:52   ` Bjorn Andersson
  1 sibling, 0 replies; 7+ messages in thread
From: Bjorn Andersson @ 2018-08-06 22:52 UTC (permalink / raw)
  To: David Dai
  Cc: linux-kernel, linux-pm, georgi.djakov, vincent.guittot,
	amit.kucheria, ilina, seansw, grahamr

On Wed 18 Jul 19:36 PDT 2018, David Dai wrote:
> diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c
[..]
> +DEFINE_QNODE(ipa_core_master, MASTER_IPA_CORE, 1, 8, 1, SLAVE_IPA_CORE);
[..]
> +DEFINE_QNODE(ipa_core_slave, SLAVE_IPA_CORE, 1, 8, 0);

As discussed before; while the two sides of IPA_CORE are controlled
through the "bus mechanism", they do represent the clock of the IPA
block.

I think it would make sense to make this interconnect provider also
register a clock provider and expose this as a clock, for the IPA driver
to consume.

Regards,
Bjorn

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

* [RFC PATCH v1 0/2] Add QCOM SDM845 interconnect provider driver
@ 2018-06-29 21:08 David Dai
  0 siblings, 0 replies; 7+ messages in thread
From: David Dai @ 2018-06-29 21:08 UTC (permalink / raw)
  To: linux-kernel, linux-pm, georgi.djakov, vincent.guittot,
	bjorn.andersson, daidavid1, amit.kucheria, ilina, seansw

Hi,

This patch series adds a driver and DT binding using the interconnect (ICC)
framework [1] to describe the Qualcomm SDM845 platform's topology of its
interconnected buses and internal aggregation nodes known as
Bus Clock Managers(BCM). The SDM845 ICC provider driver would aggregate and
satisfy consumer requests accross the SoC by generating commands that
communicate with the BCM through the Resource Power Manager (RPMh) driver [2]
interface. The SDM845 ICC driver also configures QoS settings specific to each
node to ensure clients have proper priority.

The SDM845 interconnect provider has dependencies on the RPMh driver
and Command DB driver [3] which are under review.

Changes since v0 [4]:
 - Addressed review feedback from Georgi D. and Evan G.
 - Removed proposal to modify ICC aggregate callback interface
 - Moved BCM aggregation into provider set function
 - Added devicetree binding documentation
 - Fixed logic in generating TCS command list
 - Added keepalive flags for resources that are critical to platform operation
 - Added various comments to clarify intent
 - Removed unused types and struct definitions

[1]: https://lkml.org/lkml/2018/6/20/453
[2]: https://lkml.org/lkml/2018/5/9/729
[3]: https://lkml.org/lkml/2018/3/14/787
[4]: https://patchwork.kernel.org/patch/10428167/

Summary of the patches:
Patch 1 creates the Qualcomm SDM845 Specific provider driver.
Patch 2 Adds dt binding for SDM845 provider

TODO:
  * Expand aggregation/provider to handle clients with active only requirements.
  * Add Network-on-Chip (NoC) objects to encapsulate logical nodes for QoS.
  * Add QoS configuration specific to each NoC.

David Dai (2):
  interconnect: qcom: Add sdm845 interconnect provider driver
  arm64: dts: sdm845: Add interconnect provider DT nodes

 .../bindings/interconnect/qcom-sdm845.txt          |  22 +
 arch/arm64/boot/dts/qcom/sdm845.dtsi               |   5 +
 drivers/interconnect/qcom/Kconfig                  |   8 +
 drivers/interconnect/qcom/Makefile                 |   1 +
 drivers/interconnect/qcom/qcom-icc-ids.h           | 142 ++++
 drivers/interconnect/qcom/sdm845.c                 | 826 +++++++++++++++++++++
 6 files changed, 1004 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interconnect/qcom-sdm845.txt
 create mode 100644 drivers/interconnect/qcom/qcom-icc-ids.h
 create mode 100644 drivers/interconnect/qcom/sdm845.c

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


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

end of thread, other threads:[~2018-08-06 22:54 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-19  2:36 [RFC PATCH v1 0/2] Add QCOM SDM845 interconnect provider driver David Dai
2018-07-19  2:36 ` [RFC PATCH v2 1/2] interconnect: qcom: Add sdm845 " David Dai
2018-07-27 21:12   ` Evan Green
2018-08-01  1:00     ` David Dai
2018-08-06 22:52   ` Bjorn Andersson
2018-07-19  2:37 ` [RFC PATCH v2 2/2] arm64: dts: sdm845: Add interconnect provider DT nodes David Dai
  -- strict thread matches above, loose matches on Subject: below --
2018-06-29 21:08 [RFC PATCH v1 0/2] Add QCOM SDM845 interconnect provider driver David Dai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).