Skip to contentSkip to conversation

drivers/qcom: add RPMH communication support

PATCH In review Lina Iyer 5 participants 39 messages May 9, 2018 – May 30, 2018

Subsystems (3)
lore
Share & tools
lore

Lina Iyer

author
#0 lore raw

v8 cover drivers/qcom: add RPMH communication support

Changes in v8:
- Bounds check for cmd_cache
- Describe interrupts to other DRVs in DT bindings
- Rebase on top of 4.17-rc3

Changes in v7:
- Rename 'm' and 'n' and use tcs_id and cmd_id instead
- Bug fix in find_match() and other review comments from Matthias
- Spinlock around get_rpmh_ctrlr()
- DT documentation example fixes
- Rebase on top of 4.16-rc2

Changes in v6:
- Remove tasklet in rpmh-rsc.c
- Remove rpmh_client and use struct device * instead
- Variable changes and bug fixes
- DT binding changes to describe all DRVs in the RSC
- Documentation and comment fixes

Changes in v5:
- Add Reviewed-by tags
- Rebase on top of 4.16

Changes in v4:
- Rename variables as suggested by Stephen and Evan
- Lot of minor syntax and style fixes
- Fix FTRACE compilation error
- Improve doc comments and DT description

Changes in v3:
- Address Steven's comments in FTRACE
- Fix DT documentation as suggested by Rob H
- Fix error handling in IRQ handler as suggested by Evan
- Remove locks in rpmh_flush()
- Improve comments

Changes in v2:
- Added sleep/wake, async and batch requests support
- Addressed Bjorn's comments
- Private FTRACE for drivers/soc/qcom as suggested by Steven
- Sparse checked on these patches
- Use SPDX license commenting sytle

This set of patches add the ability for platform drivers to make use of shared resources in newer Qualcomm SoCs like SDM845. Resources that are shared between multiple processors in a SoC are generally controlled by a dedicated remote processor. The remote processor (Resource Power Manager or RPM in previous QCOM SoCs) receives requests for resource state from other processors using the shared resource, aggregates the request and applies the result on the shared resource. SDM845 advances this concept and uses h/w (hardened I/P) blocks for aggregating requests and applying the result on the resource. The resources could be clocks, regulators or bandwidth requests for buses. This new architecture is called RPM-hardened or RPMH in short.

Since this communication mechanism is completely hardware driven without a processor intervention on the remote end, existing mechanisms like RPM-SMD are no longer useful. Also, there is no serialization of data or is data is written to a shared memory in this new format. The data used is different, unsigned 32 bits are used for representing an address, data and header. Each resource's property is a unique u32 address and have pre-defined set of property specific valid values. A request that comprises of <header, addr, data> is sent by writing to a set of registers from Linux and transmitted to the remote slave through an internal bus. The remote end aggregates this request along with requests from other processors for the <addr> and applies the result.

The hardware block that houses this functionality is called Resource State Coordinator or RSC. Inside the RSC are set of slots for sending RPMH requests called Trigger Commands Sets (TCS). The set of patches are for writing the requests into these TCSes and sending them to hardened IP blocks.

The driver design is split into two components. The RSC driver housed in rpmh-rsc.c and the set of library functions in rpmh.c that frame the request and transmit it using the controller. This first set of patches allow a simple synchronous request to be made by the platform drivers. Future patches will add more functionality that cater to complex drivers and use cases.

Please consider reviewing this patchset.

v1: https://www.spinics.net/lists/devicetree/msg210980.html
v2: https://lkml.org/lkml/2018/2/15/852
v3: https://lkml.org/lkml/2018/3/2/801
v4: https://lkml.org/lkml/2018/3/9/979
v5: https://lkml.org/lkml/2018/4/5/480
v6: https://lkml.org/lkml/2018/4/19/914
v7: https://lkml.org/lkml/2018/5/2/779

Lina Iyer (10):
drivers: qcom: rpmh-rsc: add RPMH controller for QCOM SoCs
dt-bindings: introduce RPMH RSC bindings for Qualcomm SoCs
drivers: qcom: rpmh-rsc: log RPMH requests in FTRACE
drivers: qcom: rpmh: add RPMH helper functions
drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
drivers: qcom: rpmh-rsc: allow invalidation of sleep/wake TCS
drivers: qcom: rpmh: cache sleep/wake state requests
drivers: qcom: rpmh: allow requests to be sent asynchronously
drivers: qcom: rpmh: add support for batch RPMH request
drivers: qcom: rpmh-rsc: allow active requests from wake TCS

 .../devicetree/bindings/soc/qcom/rpmh-rsc.txt | 137 ++++
 drivers/soc/qcom/Kconfig                      |  10 +
 drivers/soc/qcom/Makefile                     |   4 +
 drivers/soc/qcom/rpmh-internal.h              |  83 +++
 drivers/soc/qcom/rpmh-rsc.c                   | 683 ++++++++++++++++++
 drivers/soc/qcom/rpmh.c                       | 585 +++++++++++++++
 drivers/soc/qcom/trace-rpmh.h                 |  82 +++
 include/dt-bindings/soc/qcom,rpmh-rsc.h       |  14 +
 include/soc/qcom/rpmh.h                       |  51 ++
 include/soc/qcom/tcs.h                        |  56 ++
 10 files changed, 1705 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt
 create mode 100644 drivers/soc/qcom/rpmh-internal.h
 create mode 100644 drivers/soc/qcom/rpmh-rsc.c
 create mode 100644 drivers/soc/qcom/rpmh.c
 create mode 100644 drivers/soc/qcom/trace-rpmh.h
 create mode 100644 include/dt-bindings/soc/qcom,rpmh-rsc.h
 create mode 100644 include/soc/qcom/rpmh.h
 create mode 100644 include/soc/qcom/tcs.h

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

author
#1 lore raw

PATCH v8 1/10 drivers: qcom: rpmh-rsc: add RPMH controller for QCOM SoCs

Add controller driver for QCOM SoCs that have hardware based shared resource management. The hardware IP known as RSC (Resource State Coordinator) houses multiple Direct Resource Voter (DRV) for different execution levels. A DRV is a unique voter on the state of a shared resource. A Trigger Control Set (TCS) is a bunch of slots that can house multiple resource state requests, that when triggered will issue those requests through an internal bus to the Resource Power Manager Hardened (RPMH) blocks. These hardware blocks are capable of adjusting clocks, voltages, etc. The resource state request from a DRV are aggregated along with state requests from other processors in the SoC and the aggregate value is applied on the resource.

Some important aspects of the RPMH communication -
- Requests are <addr, value> with some header information
- Multiple requests (upto 16) may be sent through a TCS, at a time
- Requests in a TCS are sent in sequence
- Requests may be fire-n-forget or completion (response expected)
- Multiple TCS from the same DRV may be triggered simultaneously
- Cannot send a request if another request for the same addr is in progress from the same DRV
- When all the requests from a TCS are complete, an IRQ is raised
- The IRQ handler needs to clear the TCS before it is available for reuse
- TCS configuration is specific to a DRV
- Platform drivers may use DRV from different RSCs to make requests

Resource state requests made when CPUs are active are called 'active' state requests. Requests made when all the CPUs are powered down (idle state) are called 'sleep' state requests. They are matched by a corresponding 'wake' state requests which puts the resources back in to previously requested active state before resuming any CPU. TCSes are dedicated for each type of requests. Active mode TCSes (AMC) are used to send requests immediately to the resource, while control TCS are used to provide specific information to the controller. Sleep and Wake TCS send sleep and wake requests, after and before the system halt respectively.

---

Changes in v8:
- introduce interrupt description in DT for all DRvs

Changes in v7:
- rename 'm' and 'n' variable names

Changes in v6:
- Remove tasklet and response object
- introduce rpm_write_tcs_cmd() function
- rename tcs_irq_handler()
- avoid bool in structures. Fix tcs.h.
Changes in v4:
- lots of variable name changes as suggested by Stephen B
- use of const for data pointers
- fix comments and other code syntax
- use of bitmap for tcs_in_use instead of atomic

---
 drivers/soc/qcom/Kconfig                |  10 +
 drivers/soc/qcom/Makefile               |   1 +
 drivers/soc/qcom/rpmh-internal.h        |  69 ++++
 drivers/soc/qcom/rpmh-rsc.c             | 483 ++++++++++++++++++++++++
 include/dt-bindings/soc/qcom,rpmh-rsc.h |  14 +
 include/soc/qcom/tcs.h                  |  56 +++
 6 files changed, 633 insertions(+)
 create mode 100644 drivers/soc/qcom/rpmh-internal.h
 create mode 100644 drivers/soc/qcom/rpmh-rsc.c
 create mode 100644 include/dt-bindings/soc/qcom,rpmh-rsc.h
 create mode 100644 include/soc/qcom/tcs.h
drivers/soc/qcom/Kconfig +10 -0
index 5c4535b545cc..1887e1b1f43b 100644
@@ -56,6 +56,16 @@ config QCOM_RMTFS_MEM
 
 	  Say y here if you intend to boot the modem remoteproc.
 
+config QCOM_RPMH
+	bool "Qualcomm RPM-Hardened (RPMH) Communication"
+	depends on ARCH_QCOM && ARM64 && OF || COMPILE_TEST
+	help
+	  Support for communication with the hardened-RPM blocks in
+	  Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
+	  internal bus to transmit state requests for shared resources. A set
+	  of hardware components aggregate requests for these resources and
+	  help apply the aggregated state on the resource.
+
 config QCOM_SMEM
 	tristate "Qualcomm Shared Memory Manager (SMEM)"
 	depends on ARCH_QCOM
drivers/soc/qcom/Makefile +1 -0
index dcebf2814e6d..39d3a059ee50 100644
@@ -6,6 +6,7 @@ obj-$(CONFIG_QCOM_PM)	+=	spm.o
 obj-$(CONFIG_QCOM_QMI_HELPERS)	+= qmi_helpers.o
 qmi_helpers-y	+= qmi_encdec.o qmi_interface.o
 obj-$(CONFIG_QCOM_RMTFS_MEM)	+= rmtfs_mem.o
+obj-$(CONFIG_QCOM_RPMH)	+=	rpmh-rsc.o
 obj-$(CONFIG_QCOM_SMD_RPM)	+= smd-rpm.o
 obj-$(CONFIG_QCOM_SMEM) +=	smem.o
 obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
drivers/soc/qcom/rpmh-internal.h new file +69 -0

69 lines not on this page · raw message

drivers/soc/qcom/rpmh-rsc.c new file +483 -0

483 lines not on this page · raw message

include/dt-bindings/soc/qcom,rpmh-rsc.h new file +14 -0
new file mode 100644
index 000000000000..868f998ea998
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __DT_QCOM_RPMH_RSC_H__
+#define __DT_QCOM_RPMH_RSC_H__
+
+#define SLEEP_TCS	0
+#define WAKE_TCS	1
+#define ACTIVE_TCS	2
+#define CONTROL_TCS	3
+
+#endif /* __DT_QCOM_RPMH_RSC_H__ */
include/soc/qcom/tcs.h new file +56 -0

56 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#2 lore raw

Re: [PATCH v8 01/10] drivers: qcom: rpmh-rsc: add RPMH controller for QCOM SoCs

Hi,

Quoted 12 lines · Lina Iyer
drivers/soc/qcom/rpmh-rsc.c new file +12 -0
+int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg)
+{
+       int ret;
+
+       if (!msg || !msg->cmds || !msg->num_cmds ||
+           msg->num_cmds > MAX_RPMH_PAYLOAD) {
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       do {
+               ret = tcs_write(drv, msg);

drivers/soc/qcom/rpmh-rsc.c line 341

Quoted 10 lines
drivers/soc/qcom/rpmh-rsc.c new file +10 -0
+               if (ret == -EBUSY) {
+                       pr_info_ratelimited("TCS Busy, retrying RPMH message send: addr=%#x\n",
+                                           msg->cmds[0].addr);
+                       udelay(10);
+               }
+       } while (ret == -EBUSY);
+
+       return ret;
+}
+EXPORT_SYMBOL(rpmh_rsc_send_data);

Here and elsewhere in this series: why EXPORT_SYMBOL in this case? This is only exported to rpmh.c, right? You don't need EXPORT_SYMBOL for that. The Makefile puts rpmh.c and rpmh-rsc.c together in the same "qcom_rpmh.o", and then even further the KConfig lists this as bool so both are builtin to the kernel.

-Doug

#3 lore raw

Re: [PATCH v8 01/10] drivers: qcom: rpmh-rsc: add RPMH controller for QCOM SoCs

Hi,

Quoted 32 lines · Doug Anderson

Hi,

Quoted 12 lines · Lina Iyer
drivers/soc/qcom/rpmh-rsc.c new file +12 -0
+int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg)
+{
+       int ret;
+
+       if (!msg || !msg->cmds || !msg->num_cmds ||
+           msg->num_cmds > MAX_RPMH_PAYLOAD) {
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       do {
+               ret = tcs_write(drv, msg);

drivers/soc/qcom/rpmh-rsc.c line 341

Quoted 10 lines
drivers/soc/qcom/rpmh-rsc.c new file +10 -0
+               if (ret == -EBUSY) {
+                       pr_info_ratelimited("TCS Busy, retrying RPMH message send: addr=%#x\n",
+                                           msg->cmds[0].addr);
+                       udelay(10);
+               }
+       } while (ret == -EBUSY);
+
+       return ret;
+}
+EXPORT_SYMBOL(rpmh_rsc_send_data);

Here and elsewhere in this series: why EXPORT_SYMBOL in this case? This is only exported to rpmh.c, right? You don't need EXPORT_SYMBOL for that. The Makefile puts rpmh.c and rpmh-rsc.c together in the same "qcom_rpmh.o", and then even further the KConfig lists this as bool so both are builtin to the kernel.

Quoted 1 line

-Doug

Sure. I Will change in v9.

Thanks for your review Doug,
Raju

Quoted 5 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

author
#4 lore raw

PATCH v8 2/10 dt-bindings: introduce RPMH RSC bindings for Qualcomm SoCs

In reply to Lina Iyer

Add device binding documentation for Qualcomm Technology Inc's RPMH RSC driver. The driver is used for communicating resource state requests for shared resources.

---
Changes in v8:
- Describe IRQ for all DRVs

Changes in v7:
- Fix example

Changes in v6:
- Address comments from Stephen Boyd

Changes in v3:
- Move to soc/qcom
- Amend text per Stephen's suggestions

Changes in v2:
- Amend text to describe the registers in reg property
- Add reg-names for the registers
- Update examples to use GIC_SPI in interrupts instead of 0
- Rephrase incorrect description

Changes in v3:
- Fix unwanted capitalization
- Remove clients from the examples, this doc does not describe them
- Rephrase introductory paragraph
- Remove hardware specifics from DT bindings

---
 .../devicetree/bindings/soc/qcom/rpmh-rsc.txt | 137 ++++++++++++++++++
 1 file changed, 137 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt new file +137 -0
new file mode 100644
index 000000000000..e15c100f5c92
@@ -0,0 +1,137 @@
+RPMH RSC:
+------------
+
+Resource Power Manager Hardened (RPMH) is the mechanism for communicating with
+the hardened resource accelerators on Qualcomm SoCs. Requests to the resources
+can be written to the Trigger Command Set (TCS)  registers and using a (addr,
+val) pair and triggered. Messages in the TCS are then sent in sequence over an
+internal bus.
+
+The hardware block (Direct Resource Voter or DRV) is a part of the h/w entity
+(Resource State Coordinator a.k.a RSC) that can handle multiple sleep and
+active/wake resource requests. Multiple such DRVs can exist in a SoC and can
+be written to from Linux. The structure of each DRV follows the same template
+with a few variations that are captured by the properties here.
+
+A TCS may be triggered from Linux or triggered by the F/W after all the CPUs
+have powered off to facilitate idle power saving. TCS could be classified as -
+
+	SLEEP   /* Triggered by F/W */
+	WAKE    /* Triggered by F/W */
+	ACTIVE  /* Triggered by Linux */
+	CONTROL /* Triggered by F/W */
+
+The order in which they are described in the DT, should match the hardware
+configuration.
+
+Requests can be made for the state of a resource, when the subsystem is active
+or idle. When all subsystems like Modem, GPU, CPU are idle, the resource state
+will be an aggregate of the sleep votes from each of those subsystems. Clients
+may request a sleep value for their shared resources in addition to the active
+mode requests.
+
+Properties:
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: Should be "qcom,rpmh-rsc".
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: The first register specifies the base address of the
+		    DRV(s). The number of DRVs in the dependent on the RSC.
+	            The tcs-offset specifies the start address of the
+	            TCS in the DRVs.
+
+- reg-names:
+	Usage: required
+	Value type: <string>
+	Definition: Maps the register specified in the reg property. Must be
+	            "drv-0", "drv-1", "drv-2" etc and "tcs-offset". The
+
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-interrupt>
+	Definition: The interrupt that trips when a message complete/response
+	           is received for this DRV from the accelerators.
+
+- qcom,drv-id:
+	Usage: required
+	Value type: <u32>
+	Definition: The id of the DRV in the RSC block that will be used by
+		    this controller.
+
+- qcom,tcs-config:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: The tuple defining the configuration of TCS.
+	            Must have 2 cells which describe each TCS type.
+	            <type number_of_tcs>.
+	            The order of the TCS must match the hardware
+	            configuration.
+	- Cell #1 (TCS Type): TCS types to be specified -
+	            SLEEP_TCS
+	            WAKE_TCS
+	            ACTIVE_TCS
+	            CONTROL_TCS
+	- Cell #2 (Number of TCS): <u32>
+
+- label:
+	Usage: optional
+	Value type: <string>
+	Definition: Name for the RSC. The name would be used in trace logs.
+
+Drivers that want to use the RSC to communicate with RPMH must specify their
+bindings as child nodes of the RSC controllers they wish to communicate with.
+
+Example 1:
+
+For a TCS whose RSC base address is is 0x179C0000 and is at a DRV id of 2, the
+register offsets for DRV2 start at 0D00, the register calculations are like
+this -
+DRV0: 0x179C0000
+DRV2: 0x179C0000 + 0x10000 = 0x179D0000
+DRV2: 0x179C0000 + 0x10000 * 2 = 0x179E0000
+TCS-OFFSET: 0xD00
+
+	apps_rsc: rsc@179c0000 {
+		label = "apps_rsc";
+		compatible = "qcom,rpmh-rsc";
+		reg = <0x179c0000 0x10000>,
+		      <0x179d0000 0x10000>,
+		      <0x179e0000 0x10000>;
+		reg-names = "drv-0", "drv-1", "drv-2";
+		interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
+		qcom,tcs-offset = <0xd00>;
+		qcom,drv-id = <2>;
+		qcom,tcs-config = <SLEEP_TCS   3>,
+				  <WAKE_TCS    3>,
+				  <ACTIVE_TCS  2>,
+				  <CONTROL_TCS 1>;
+	};
+
+Example 2:
+
+For a TCS whose RSC base address is 0xAF20000 and is at DRV id of 0, the
+register offsets for DRV0 start at 01C00, the register calculations are like
+this -
+DRV0: 0xAF20000
+TCS-OFFSET: 0x1C00
+
+	disp_rsc: rsc@af20000 {
+		label = "disp_rsc";
+		compatible = "qcom,rpmh-rsc";
+		reg = <0xaf20000 0x10000>;
+		reg-names = "drv-0";
+		interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
+		qcom,tcs-offset = <0x1c00>;
+		qcom,drv-id = <0>;
+		qcom,tcs-config = <SLEEP_TCS   1>,
+				  <WAKE_TCS    1>,
+				  <ACTIVE_TCS  0>,
+				  <CONTROL_TCS 0>;
+	};
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
author
#5 lore raw

PATCH v8 3/10 drivers: qcom: rpmh-rsc: log RPMH requests in FTRACE

In reply to Lina Iyer

Log sent RPMH requests and interrupt responses in FTRACE.

Cc Steven Rostedt <[email protected]>

---

Changes in v7:
- varible name changes and white space

Changes in v6:
- struct tcs_response was removed. Fix in trace as well.
Changes in v4:
- fix compilation issues, use __assign_str
- use %#x instead of 0x%08x
Changes in v3:
- Use __string() instead of char *
- fix TRACE_INCLUDE_PATH

---
 drivers/soc/qcom/Makefile     |  1 +
 drivers/soc/qcom/rpmh-rsc.c   | 11 ++++-
 drivers/soc/qcom/trace-rpmh.h | 82 +++++++++++++++++++++++++++++++++++
 3 files changed, 93 insertions(+), 1 deletion(-)
 create mode 100644 drivers/soc/qcom/trace-rpmh.h
drivers/soc/qcom/Makefile +1 -0
index 39d3a059ee50..cb6300f6a8e9 100644
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
+CFLAGS_rpmh-rsc.o := -I$(src)
 obj-$(CONFIG_QCOM_GLINK_SSR) +=	glink_ssr.o
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
 obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
drivers/soc/qcom/rpmh-rsc.c +10 -1
index 4eeccf75cc15..0a8cec9d1651 100644
@@ -24,6 +24,9 @@
 
 #include "rpmh-internal.h"
 
+#define CREATE_TRACE_POINTS
+#include "trace-rpmh.h"
+
 #define RSC_DRV_TCS_OFFSET		672
 #define RSC_DRV_CMD_OFFSET		20
 
@@ -136,7 +139,7 @@ static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv,
 static irqreturn_t tcs_tx_done(int irq, void *p)
 {
 	struct rsc_drv *drv = p;
-	int i, j;
+	int i, j, err;
 	unsigned long irq_status;
 	const struct tcs_request *req;
 	struct tcs_cmd *cmd;
@@ -150,6 +153,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p)
 			goto skip;
 		}
 
+		err = 0;
 		for (j = 0; j < req->num_cmds; j++) {
 			u32 sts;
 
@@ -160,8 +164,11 @@ static irqreturn_t tcs_tx_done(int irq, void *p)
 			   !(sts & CMD_STATUS_COMPL))) {
 				pr_err("Incomplete request: %s: addr=%#x data=%#x",
 				       drv->name, cmd->addr, cmd->data);
+				err = -EIO;
 			}
 		}
+
+		trace_rpmh_tx_done(drv, i, req, err);
 skip:
 		/* Reclaim the TCS */
 		write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, i, 0);
@@ -195,9 +202,11 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id,
 		cmd_complete |= cmd->wait << j;
 		msgid = cmd_msgid;
 		msgid |= cmd->wait ? CMD_MSGID_RESP_REQ : 0;
+
 		write_tcs_cmd(drv, RSC_DRV_CMD_MSGID, tcs_id, j, msgid);
 		write_tcs_cmd(drv, RSC_DRV_CMD_ADDR, tcs_id, j, cmd->addr);
 		write_tcs_cmd(drv, RSC_DRV_CMD_DATA, tcs_id, j, cmd->data);
+		trace_rpmh_send_msg(drv, tcs_id, j, msgid, cmd);
 	}
 
 	write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, cmd_complete);
drivers/soc/qcom/trace-rpmh.h new file +82 -0

82 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#6 lore raw

Re: [PATCH v8 03/10] drivers: qcom: rpmh-rsc: log RPMH requests in FTRACE

Quoted 1 line · On Wed, 9 May 2018 11:01:52 -0600 Lina Iyer <[email protected]> wrote:

Log sent RPMH requests and interrupt responses in FTRACE.

Has this changed since the last time I reviewed it? If not, please add
the: Reviewed-by: Steven Rostedt (VMware) <[email protected]> to you patch queue.

-- Steve

Quoted 17 lines
Cc Steven Rostedt <[email protected]>

---

Changes in v7:
- varible name changes and white space

Changes in v6:
- struct tcs_response was removed. Fix in trace as well.
Changes in v4:
- fix compilation issues, use __assign_str
- use %#x instead of 0x%08x
Changes in v3:
- Use __string() instead of char *
- fix TRACE_INCLUDE_PATH
---

author
#7 lore raw

Re: [PATCH v8 03/10] drivers: qcom: rpmh-rsc: log RPMH requests in FTRACE

Quoted 9 lines · Steven Rostedt
Quoted 1 line · On Wed, 9 May 2018 11:01:52 -0600 Lina Iyer <[email protected]> wrote:

Log sent RPMH requests and interrupt responses in FTRACE.

Has this changed since the last time I reviewed it? If not, please add
the: Reviewed-by: Steven Rostedt (VMware) <[email protected]> to you patch queue.

Sorry, no it did not change in v8.
I will add your reviewed-by tag. Thanks Steve.

-- Lina

Quoted 19 lines

-- Steve

Quoted 17 lines
Cc Steven Rostedt <[email protected]>

---

Changes in v7:
- varible name changes and white space

Changes in v6:
- struct tcs_response was removed. Fix in trace as well.
Changes in v4:
- fix compilation issues, use __assign_str
- use %#x instead of 0x%08x
Changes in v3:
- Use __string() instead of char *
- fix TRACE_INCLUDE_PATH
---

author
#8 lore raw

PATCH v8 4/10 drivers: qcom: rpmh: add RPMH helper functions

In reply to Lina Iyer

Sending RPMH requests and waiting for response from the controller through a callback is common functionality across all platform drivers. To simplify drivers, add a library functions to create RPMH client and send resource state requests.

rpmh_write() is a synchronous blocking call that can be used to send active state requests.

---

Changes in v7:
- Optimization and locking fixes

Changes in v6:
- replace rpmh_client with device
- inline wait_for_tx_done()

Changes in v4:
- use const struct tcs_cmd in API
- remove wait count from this patch
- changed -EFAULT to -EINVAL

---
 drivers/soc/qcom/Makefile        |   4 +-
 drivers/soc/qcom/rpmh-internal.h |   6 ++
 drivers/soc/qcom/rpmh-rsc.c      |   8 ++
 drivers/soc/qcom/rpmh.c          | 176 +++++++++++++++++++++++++++++++
 include/soc/qcom/rpmh.h          |  25 +++++
 5 files changed, 218 insertions(+), 1 deletion(-)
 create mode 100644 drivers/soc/qcom/rpmh.c
 create mode 100644 include/soc/qcom/rpmh.h
drivers/soc/qcom/Makefile +3 -1
index cb6300f6a8e9..bb395c3202ca 100644
@@ -7,7 +7,9 @@ obj-$(CONFIG_QCOM_PM)	+=	spm.o
 obj-$(CONFIG_QCOM_QMI_HELPERS)	+= qmi_helpers.o
 qmi_helpers-y	+= qmi_encdec.o qmi_interface.o
 obj-$(CONFIG_QCOM_RMTFS_MEM)	+= rmtfs_mem.o
-obj-$(CONFIG_QCOM_RPMH)	+=	rpmh-rsc.o
+obj-$(CONFIG_QCOM_RPMH)		+= qcom_rpmh.o
+qcom_rpmh-y			+= rpmh-rsc.o
+qcom_rpmh-y			+= rpmh.o
 obj-$(CONFIG_QCOM_SMD_RPM)	+= smd-rpm.o
 obj-$(CONFIG_QCOM_SMEM) +=	smem.o
 obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
drivers/soc/qcom/rpmh-internal.h +6 -0
index cc29176f1303..d9a21726e568 100644
@@ -14,6 +14,7 @@
 #define MAX_CMDS_PER_TCS		16
 #define MAX_TCS_PER_TYPE		3
 #define MAX_TCS_NR			(MAX_TCS_PER_TYPE * TCS_TYPE_NR)
+#define RPMH_MAX_CTRLR			2
 
 struct rsc_drv;
 
@@ -52,6 +53,7 @@ struct tcs_group {
  * @tcs:        TCS groups
  * @tcs_in_use: s/w state of the TCS
  * @lock:       synchronize state of the controller
+ * @list:       element in list of drv
  */
 struct rsc_drv {
 	const char *name;
@@ -61,9 +63,13 @@ struct rsc_drv {
 	struct tcs_group tcs[TCS_TYPE_NR];
 	DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR);
 	spinlock_t lock;
+	struct list_head list;
 };
 
+extern struct list_head rsc_drv_list;
 
 int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg);
 
+void rpmh_tx_done(const struct tcs_request *msg, int r);
+
 #endif /* __RPM_INTERNAL_H__ */
drivers/soc/qcom/rpmh-rsc.c +8 -0
index 0a8cec9d1651..c0edf3850147 100644
@@ -61,6 +61,8 @@
 #define CMD_STATUS_ISSUED		BIT(8)
 #define CMD_STATUS_COMPL		BIT(16)
 
+LIST_HEAD(rsc_drv_list);
+
 static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id)
 {
 	return readl_relaxed(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id +
@@ -176,6 +178,8 @@ static irqreturn_t tcs_tx_done(int irq, void *p)
 		spin_lock(&drv->lock);
 		clear_bit(i, drv->tcs_in_use);
 		spin_unlock(&drv->lock);
+		if (req)
+			rpmh_tx_done(req, err);
 	}
 
 	return IRQ_HANDLED;
@@ -469,6 +473,10 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
 	/* Enable the active TCS to send requests immediately */
 	write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, drv->tcs[ACTIVE_TCS].mask);
 
+	INIT_LIST_HEAD(&drv->list);
+	list_add(&drv->list, &rsc_drv_list);
+	dev_set_drvdata(&pdev->dev, drv);
+
 	return devm_of_platform_populate(&pdev->dev);
 }
 
drivers/soc/qcom/rpmh.c new file +176 -0

176 lines not on this page · raw message

include/soc/qcom/rpmh.h new file +25 -0
new file mode 100644
index 000000000000..c1d0f902bd71
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __SOC_QCOM_RPMH_H__
+#define __SOC_QCOM_RPMH_H__
+
+#include <soc/qcom/tcs.h>
+#include <linux/platform_device.h>
+
+
+#if IS_ENABLED(CONFIG_QCOM_RPMH)
+int rpmh_write(const struct device *dev, enum rpmh_state state,
+	       const struct tcs_cmd *cmd, u32 n);
+
+#else
+
+static inline int rpmh_write(const struct device *dev, enum rpmh_state state,
+			     const struct tcs_cmd *cmd, u32 n)
+{ return -ENODEV; }
+
+#endif /* CONFIG_QCOM_RPMH */
+
+#endif /* __SOC_QCOM_RPMH_H__ */
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#9 lore raw

Re: [PATCH v8 04/10] drivers: qcom: rpmh: add RPMH helper functions

Hi,

Quoted 8 lines · Lina Iyer
drivers/soc/qcom/rpmh.c new file +8 -0
+int rpmh_write(const struct device *dev, enum rpmh_state state,
+              const struct tcs_cmd *cmd, u32 n)
+{
+       DECLARE_COMPLETION_ONSTACK(compl);
+       DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg);
+       int ret;
+
+       if (!cmd || !n || n > MAX_RPMH_PAYLOAD)

drivers/soc/qcom/rpmh.c line 173

Quoted 10 lines
drivers/soc/qcom/rpmh.c new file +10 -0
+               return -EINVAL;
+
+       memcpy(rpm_msg.cmd, cmd, n * sizeof(*cmd));
+       rpm_msg.msg.num_cmds = n;
+
+       ret = __rpmh_write(dev, state, &rpm_msg);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);

IMO it's almost never a good idea to use wait_for_completion_timeout() together with a completion that's declared on the stack. If you somehow insist that this is a good idea then I need to see incredibly clear and obvious code/comments that say why it's impossible that the process might somehow try to signal the completion _after_ RPMH_TIMEOUT_MS has expired.

Specifically if the timeout happens but the process could still signal a completion later then they will access random data on the stack of a function that has already returned. This causes ridiculously difficult-to-debug crashes.

NOTE: You've got timeout set to 10 seconds here. Is that really even useful? IMO just call wait_for_completion() without a timeout. It's much better to have a nice clean hang than a random stack corruption.

-Doug

author
#10 lore raw

Re: [PATCH v8 04/10] drivers: qcom: rpmh: add RPMH helper functions

Quoted 35 lines · Doug Anderson

Hi,

Quoted 8 lines · Lina Iyer
drivers/soc/qcom/rpmh.c new file +8 -0
+int rpmh_write(const struct device *dev, enum rpmh_state state,
+              const struct tcs_cmd *cmd, u32 n)
+{
+       DECLARE_COMPLETION_ONSTACK(compl);
+       DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg);
+       int ret;
+
+       if (!cmd || !n || n > MAX_RPMH_PAYLOAD)

drivers/soc/qcom/rpmh.c line 173

Quoted 10 lines
drivers/soc/qcom/rpmh.c new file +10 -0
+               return -EINVAL;
+
+       memcpy(rpm_msg.cmd, cmd, n * sizeof(*cmd));
+       rpm_msg.msg.num_cmds = n;
+
+       ret = __rpmh_write(dev, state, &rpm_msg);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);

IMO it's almost never a good idea to use wait_for_completion_timeout() together with a completion that's declared on the stack. If you somehow insist that this is a good idea then I need to see incredibly clear and obvious code/comments that say why it's impossible that the process might somehow try to signal the completion _after_ RPMH_TIMEOUT_MS has expired.

Specifically if the timeout happens but the process could still signal a completion later then they will access random data on the stack of a function that has already returned. This causes ridiculously difficult-to-debug crashes.

Quoted 5 lines

NOTE: You've got timeout set to 10 seconds here. Is that really even useful? IMO just call wait_for_completion() without a timeout. It's much better to have a nice clean hang than a random stack corruption.

The 10 sec timeout will guarantee that we will not get a response at all anymore for the request. Usually requests can be considered failed if there is no response in a few tens of microseconds. 10 sec is just an arbitarily large number.

The reason we use timeout is that once the timeout happens, we know we have failed, we could trigger a watchdog or crash the system. This is very important for our productization in debugging RPMH failures. A hang would not always trigger a watchdog and the failure would be silent and possibly fatal but hard to debug.

-- Lina

#11 lore raw

Re: [PATCH v8 04/10] drivers: qcom: rpmh: add RPMH helper functions

Hi,

Quoted 49 lines · Lina Iyer
Quoted 38 lines · Doug Anderson

Hi,

Quoted 9 lines · Lina Iyer
drivers/soc/qcom/rpmh.c new file +8 -0
+int rpmh_write(const struct device *dev, enum rpmh_state state,
+              const struct tcs_cmd *cmd, u32 n)
+{
+       DECLARE_COMPLETION_ONSTACK(compl);
+       DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg);
+       int ret;
+
+       if (!cmd || !n || n > MAX_RPMH_PAYLOAD)

drivers/soc/qcom/rpmh.c line 173

Quoted 10 lines
drivers/soc/qcom/rpmh.c new file +10 -0
+               return -EINVAL;
+
+       memcpy(rpm_msg.cmd, cmd, n * sizeof(*cmd));
+       rpm_msg.msg.num_cmds = n;
+
+       ret = __rpmh_write(dev, state, &rpm_msg);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);

IMO it's almost never a good idea to use wait_for_completion_timeout() together with a completion that's declared on the stack. If you somehow insist that this is a good idea then I need to see incredibly clear and obvious code/comments that say why it's impossible that the process might somehow try to signal the completion _after_ RPMH_TIMEOUT_MS has expired.

Specifically if the timeout happens but the process could still signal a completion later then they will access random data on the stack of a function that has already returned. This causes ridiculously difficult-to-debug crashes.

Quoted 5 lines

NOTE: You've got timeout set to 10 seconds here. Is that really even useful? IMO just call wait_for_completion() without a timeout. It's much better to have a nice clean hang than a random stack corruption.

The 10 sec timeout will guarantee that we will not get a response at all anymore for the request. Usually requests can be considered failed if there is no response in a few tens of microseconds. 10 sec is just an arbitarily large number.

Quoted 5 lines

The reason we use timeout is that once the timeout happens, we know we have failed, we could trigger a watchdog or crash the system. This is very important for our productization in debugging RPMH failures. A hang would not always trigger a watchdog and the failure would be silent and possibly fatal but hard to debug.

If you intend the system to crash when this timeout happens then IMHO add a BUG_ON. Then I won't worry about something coming around later and clobbering the stack.

-Doug

#12 lore raw

Re: [PATCH v8 04/10] drivers: qcom: rpmh: add RPMH helper functions

Hi,

Quoted 58 lines · Doug Anderson

Hi,

Quoted 49 lines · Lina Iyer
Quoted 38 lines · Doug Anderson

Hi,

Quoted 9 lines · Lina Iyer
drivers/soc/qcom/rpmh.c new file +8 -0
+int rpmh_write(const struct device *dev, enum rpmh_state state,
+              const struct tcs_cmd *cmd, u32 n)
+{
+       DECLARE_COMPLETION_ONSTACK(compl);
+       DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg);
+       int ret;
+
+       if (!cmd || !n || n > MAX_RPMH_PAYLOAD)

drivers/soc/qcom/rpmh.c line 173

Quoted 10 lines
drivers/soc/qcom/rpmh.c new file +10 -0
+               return -EINVAL;
+
+       memcpy(rpm_msg.cmd, cmd, n * sizeof(*cmd));
+       rpm_msg.msg.num_cmds = n;
+
+       ret = __rpmh_write(dev, state, &rpm_msg);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);

IMO it's almost never a good idea to use wait_for_completion_timeout() together with a completion that's declared on the stack. If you somehow insist that this is a good idea then I need to see incredibly clear and obvious code/comments that say why it's impossible that the process might somehow try to signal the completion _after_ RPMH_TIMEOUT_MS has expired.

Specifically if the timeout happens but the process could still signal a completion later then they will access random data on the stack of a function that has already returned. This causes ridiculously difficult-to-debug crashes.

Quoted 5 lines

NOTE: You've got timeout set to 10 seconds here. Is that really even useful? IMO just call wait_for_completion() without a timeout. It's much better to have a nice clean hang than a random stack corruption.

The 10 sec timeout will guarantee that we will not get a response at all anymore for the request. Usually requests can be considered failed if there is no response in a few tens of microseconds. 10 sec is just an arbitarily large number.

Quoted 5 lines

The reason we use timeout is that once the timeout happens, we know we have failed, we could trigger a watchdog or crash the system. This is very important for our productization in debugging RPMH failures. A hang would not always trigger a watchdog and the failure would be silent and possibly fatal but hard to debug.

Quoted 6 lines

If you intend the system to crash when this timeout happens then IMHO add a BUG_ON. Then I won't worry about something coming around later and clobbering the stack.

-Doug

Sure. Will add BUG_ON in next patch.

Thanks,
Raju.

author
#13 lore raw

PATCH v8 5/10 drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS

In reply to Lina Iyer

Sleep and wake requests are sent when the application processor subsystem of the SoC is entering deep sleep states like in suspend. These requests help lower the system power requirements when the resources are not in use.

Sleep and wake requests are written to the TCS slots but are not triggered at the time of writing. The TCS are triggered by the firmware after the last of the CPUs has executed its WFI. Since these requests may come in different batches of requests, it is the job of this controller driver to find and arrange the requests into the available TCSes.

---
Changes in v8:
- Bounds check in find_match()

Changes in v7:
- Bug fix in find_match()

---
 drivers/soc/qcom/rpmh-internal.h |   8 ++
 drivers/soc/qcom/rpmh-rsc.c      | 122 +++++++++++++++++++++++++++++++
 2 files changed, 130 insertions(+)
drivers/soc/qcom/rpmh-internal.h +8 -0
index d9a21726e568..6e19fe458c31 100644
@@ -14,6 +14,7 @@
 #define MAX_CMDS_PER_TCS		16
 #define MAX_TCS_PER_TYPE		3
 #define MAX_TCS_NR			(MAX_TCS_PER_TYPE * TCS_TYPE_NR)
+#define MAX_TCS_SLOTS			(MAX_CMDS_PER_TCS * MAX_TCS_PER_TYPE)
 #define RPMH_MAX_CTRLR			2
 
 struct rsc_drv;
@@ -30,6 +31,8 @@ struct rsc_drv;
  * @ncpt:      number of commands in each TCS
  * @lock:      lock for synchronizing this TCS writes
  * @req:       requests that are sent from the TCS
+ * @cmd_cache: flattened cache of cmds in sleep/wake TCS
+ * @slots:     indicates which of @cmd_addr are occupied
  */
 struct tcs_group {
 	struct rsc_drv *drv;
@@ -40,6 +43,8 @@ struct tcs_group {
 	int ncpt;
 	spinlock_t lock;
 	const struct tcs_request *req[MAX_TCS_PER_TYPE];
+	u32 *cmd_cache;
+	DECLARE_BITMAP(slots, MAX_TCS_SLOTS);
 };
 
 /**
@@ -69,6 +74,9 @@ struct rsc_drv {
 extern struct list_head rsc_drv_list;
 
 int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg);
+int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv,
+			     const struct tcs_request *msg);
+int rpmh_rsc_invalidate(struct rsc_drv *drv);
 
 void rpmh_tx_done(const struct tcs_request *msg, int r);
 
drivers/soc/qcom/rpmh-rsc.c +122 -0

140 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#14 lore raw

Re: [PATCH v8 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS

Hi Lina,

Quoted 25 lines · Lina Iyer

Sleep and wake requests are sent when the application processor subsystem of the SoC is entering deep sleep states like in suspend. These requests help lower the system power requirements when the resources are not in use.

Sleep and wake requests are written to the TCS slots but are not triggered at the time of writing. The TCS are triggered by the firmware after the last of the CPUs has executed its WFI. Since these requests may come in different batches of requests, it is the job of this controller driver to find and arrange the requests into the available TCSes.

---

drivers/soc/qcom/rpmh-rsc.c +1 -0
index c0edf3850147..b5894b001ae1 100644
 
<snip>
 
+static int find_match(const struct tcs_group *tcs, const struct tcs_cmd *cmd,

drivers/soc/qcom/rpmh-rsc.c int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) · line 372

Quoted 10 lines
drivers/soc/qcom/rpmh-rsc.c +10 -0
+		      int len)
+{
+	int i, j;
+
+	/* Check for already cached commands */
+	for_each_set_bit(i, tcs->slots, MAX_TCS_SLOTS) {
+		if (tcs->cmd_cache[i] != cmd[0].addr)
+			continue;
+		if (i + len >= MAX_TCS_SLOTS)
+			goto seq_err;

The command cache can have less than MAX_TCS_SLOTS slot:

static int rpmh_probe_tcs_config(struct platform_device *pdev,
struct rsc_drv *drv)

{
	...
	tcs->cmd_cache = devm_kcalloc(&pdev->dev,
				      tcs->num_tcs * ncpt, sizeof(u32),
				      GFP_KERNEL);
	...
}

So the condition needs to be:

if (i + len >= tcs->num_tcs * tcs->ncpt)

Quoted 7 lines
drivers/soc/qcom/rpmh-rsc.c +7 -0
+static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg,
+		      int *tcs_id, int *cmd_id)
+{
+	int slot, offset;
+	int i = 0;
+
+	/* Find if we already have the msg in our TCS */

drivers/soc/qcom/rpmh-rsc.c int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) · line 403

Quoted 10 lines
drivers/soc/qcom/rpmh-rsc.c +10 -0
+	slot = find_match(tcs, msg->cmds, msg->num_cmds);
+	if (slot >= 0)
+		goto copy_data;
+
+	/* Do over, until we can fit the full payload in a TCS */
+	do {
+		slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS,
+						  i, msg->num_cmds, 0);
+		if (slot == MAX_TCS_SLOTS)
+			return -ENOMEM;

Like above, use 'tcs->num_tcs * tcs->ncpt' as maximum instead of MAX_TCS_SLOTS.

drivers/soc/qcom/rpmh-rsc.c int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) · line 432

Quoted 13 lines
drivers/soc/qcom/rpmh-rsc.c +13 -0
+static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg)
+{
+	struct tcs_group *tcs;
+	int tcs_id = 0, cmd_id = 0;
+	unsigned long flags;
+	int ret;
+
+	tcs = get_tcs_for_msg(drv, msg);
+	if (IS_ERR(tcs))
+		return PTR_ERR(tcs);
+
+	spin_lock_irqsave(&tcs->lock, flags);
+	/* find the m-th TCS and the n-th position in the TCS to write to */

The comment still refers to the old names 'm' and 'n'.

Thanks

Matthias

author
#15 lore raw

Re: [PATCH v8 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS

Quoted 27 lines · Matthias Kaehlcke

Hi Lina,

Quoted 24 lines · Lina Iyer

Sleep and wake requests are sent when the application processor subsystem of the SoC is entering deep sleep states like in suspend. These requests help lower the system power requirements when the resources are not in use.

Sleep and wake requests are written to the TCS slots but are not triggered at the time of writing. The TCS are triggered by the firmware after the last of the CPUs has executed its WFI. Since these requests may come in different batches of requests, it is the job of this controller driver to find and arrange the requests into the available TCSes.

---

drivers/soc/qcom/rpmh-rsc.c +0 -0
index c0edf3850147..b5894b001ae1 100644
 
<snip>
Quoted 14 lines

drivers/soc/qcom/rpmh-rsc.c int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) · line 372

Quoted 11 lines
drivers/soc/qcom/rpmh-rsc.c +11 -0
+static int find_match(const struct tcs_group *tcs, const struct tcs_cmd *cmd,
+		      int len)
+{
+	int i, j;
+
+	/* Check for already cached commands */
+	for_each_set_bit(i, tcs->slots, MAX_TCS_SLOTS) {
+		if (tcs->cmd_cache[i] != cmd[0].addr)
+			continue;
+		if (i + len >= MAX_TCS_SLOTS)
+			goto seq_err;

The command cache can have less than MAX_TCS_SLOTS slot:

That's true. I forgot that I had optimized the cache slots. Thanks for pointing out.

Quoted 35 lines

static int rpmh_probe_tcs_config(struct platform_device *pdev,
struct rsc_drv *drv)

{
	...
	tcs->cmd_cache = devm_kcalloc(&pdev->dev,
				      tcs->num_tcs * ncpt, sizeof(u32),
				      GFP_KERNEL);
	...
}

So the condition needs to be:

if (i + len >= tcs->num_tcs * tcs->ncpt)

Quoted 7 lines
drivers/soc/qcom/rpmh-rsc.c +7 -0
+static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg,
+		      int *tcs_id, int *cmd_id)
+{
+	int slot, offset;
+	int i = 0;
+
+	/* Find if we already have the msg in our TCS */

drivers/soc/qcom/rpmh-rsc.c int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) · line 403

Quoted 10 lines
drivers/soc/qcom/rpmh-rsc.c +10 -0
+	slot = find_match(tcs, msg->cmds, msg->num_cmds);
+	if (slot >= 0)
+		goto copy_data;
+
+	/* Do over, until we can fit the full payload in a TCS */
+	do {
+		slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS,
+						  i, msg->num_cmds, 0);
+		if (slot == MAX_TCS_SLOTS)
+			return -ENOMEM;

Like above, use 'tcs->num_tcs * tcs->ncpt' as maximum instead of MAX_TCS_SLOTS.

Quoted 16 lines

drivers/soc/qcom/rpmh-rsc.c int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) · line 432

Quoted 13 lines
drivers/soc/qcom/rpmh-rsc.c +13 -0
+static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg)
+{
+	struct tcs_group *tcs;
+	int tcs_id = 0, cmd_id = 0;
+	unsigned long flags;
+	int ret;
+
+	tcs = get_tcs_for_msg(drv, msg);
+	if (IS_ERR(tcs))
+		return PTR_ERR(tcs);
+
+	spin_lock_irqsave(&tcs->lock, flags);
+	/* find the m-th TCS and the n-th position in the TCS to write to */

The comment still refers to the old names 'm' and 'n'.

Really? :)
Will fix.

Thanks for your review,
Lina

author
#16 lore raw

PATCH v8 6/10 drivers: qcom: rpmh-rsc: allow invalidation of sleep/wake TCS

In reply to Lina Iyer

Allow sleep and wake commands to be cleared from the respective TCSes, so that they can be re-populated.

---

Changes in v7:
- Move bitmap_zero() outside the loop

Changes in v6:
- remove unnecessary locks around __tcs_invalidate
- rename function to tcs_invaldiate

Changes in v4:
- refactored the rphm_rsc_invalidate()

---
 drivers/soc/qcom/rpmh-rsc.c | 45 +++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
drivers/soc/qcom/rpmh-rsc.c +45 -0
index b5894b001ae1..68c25ebbbe09 100644
@@ -104,6 +104,51 @@ static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type)
 	return &drv->tcs[type];
 }
 
+static int tcs_invalidate(struct rsc_drv *drv, int type)
+{
+	int m;
+	struct tcs_group *tcs;
+
+	tcs = get_tcs_of_type(drv, type);
+	if (IS_ERR(tcs))
+		return PTR_ERR(tcs);
+
+	spin_lock(&tcs->lock);
+	if (bitmap_empty(tcs->slots, MAX_TCS_SLOTS)) {
+		spin_unlock(&tcs->lock);
+		return 0;
+	}
+
+	for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) {
+		if (!tcs_is_free(drv, m)) {
+			spin_unlock(&tcs->lock);
+			return -EAGAIN;
+		}
+		write_tcs_reg_sync(drv, RSC_DRV_CMD_ENABLE, m, 0);
+	}
+	bitmap_zero(tcs->slots, MAX_TCS_SLOTS);
+	spin_unlock(&tcs->lock);
+
+	return 0;
+}
+
+/**
+ * rpmh_rsc_invalidate - Invalidate sleep and wake TCSes
+ *
+ * @drv: the RSC controller
+ */
+int rpmh_rsc_invalidate(struct rsc_drv *drv)
+{
+	int ret;
+
+	ret = tcs_invalidate(drv, SLEEP_TCS);
+	if (!ret)
+		ret = tcs_invalidate(drv, WAKE_TCS);
+
+	return ret;
+}
+EXPORT_SYMBOL(rpmh_rsc_invalidate);
+
 static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
 					 const struct tcs_request *msg)
 {
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
author
#17 lore raw

PATCH v8 7/10 drivers: qcom: rpmh: cache sleep/wake state requests

In reply to Lina Iyer

Active state requests are sent immediately to the RSC controller, while sleep and wake state requests are cached in this driver to avoid taxing the RSC controller repeatedly. The cached values will be sent to the controller when the rpmh_flush() is called.

Generally, flushing is a system PM activity and may be called from the system PM drivers when the system is entering suspend or deeper sleep modes during cpuidle.

Also allow invalidating the cached requests, so they may be re-populated again.

---

Changes in v6:
- replace rpmh_client with device *

Changes in v4:
- remove locking for ->dirty in invalidate
- fix send_single
Changes in v3:
- Remove locking for flush function
- Improve comments

---
 drivers/soc/qcom/rpmh.c | 217 +++++++++++++++++++++++++++++++++++++++-
 include/soc/qcom/rpmh.h |  11 ++
 2 files changed, 223 insertions(+), 5 deletions(-)
drivers/soc/qcom/rpmh.c +212 -5
index 74bb82339b01..23fcbd9487cd 100644
@@ -7,10 +7,12 @@
 #include <linux/interrupt.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
+#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/wait.h>
 
@@ -33,6 +35,21 @@
 		.dev = dev,				\
 	}
 
+/**
+ * struct cache_req: the request object for caching
+ *
+ * @addr: the address of the resource
+ * @sleep_val: the sleep vote
+ * @wake_val: the wake vote
+ * @list: linked list obj
+ */
+struct cache_req {
+	u32 addr;
+	u32 sleep_val;
+	u32 wake_val;
+	struct list_head list;
+};
+
 /**
  * struct rpmh_request: the message to be sent to rpmh-rsc
  *
@@ -54,9 +71,15 @@ struct rpmh_request {
  * struct rpmh_ctrlr: our representation of the controller
  *
  * @drv: the controller instance
+ * @cache: the list of cached requests
+ * @lock: synchronize access to the controller data
+ * @dirty: was the cache updated since flush
  */
 struct rpmh_ctrlr {
 	struct rsc_drv *drv;
+	struct list_head cache;
+	spinlock_t lock;
+	bool dirty;
 };
 
 static struct rpmh_ctrlr rpmh_rsc[RPMH_MAX_CTRLR];
@@ -91,6 +114,8 @@ static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev)
 				break;
 			}
 			rpmh_rsc[i].drv = drv;
+			spin_lock_init(&rpmh_rsc[i].lock);
+			INIT_LIST_HEAD(&rpmh_rsc[i].cache);
 			ctrlr = &rpmh_rsc[i];
 			break;
 		}
@@ -118,29 +143,109 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
 }
 EXPORT_SYMBOL(rpmh_tx_done);
 
+static struct cache_req *__find_req(struct rpmh_ctrlr *ctrlr, u32 addr)
+{
+	struct cache_req *p, *req = NULL;
+
+	list_for_each_entry(p, &ctrlr->cache, list) {
+		if (p->addr == addr) {
+			req = p;
+			break;
+		}
+	}
+
+	return req;
+}
+
+static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr,
+					   enum rpmh_state state,
+					   struct tcs_cmd *cmd)
+{
+	struct cache_req *req;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ctrlr->lock, flags);
+	req = __find_req(ctrlr, cmd->addr);
+	if (req)
+		goto existing;
+
+	req = kzalloc(sizeof(*req), GFP_ATOMIC);
+	if (!req) {
+		req = ERR_PTR(-ENOMEM);
+		goto unlock;
+	}
+
+	req->addr = cmd->addr;
+	req->sleep_val = req->wake_val = UINT_MAX;
+	INIT_LIST_HEAD(&req->list);
+	list_add_tail(&req->list, &ctrlr->cache);
+
+existing:
+	switch (state) {
+	case RPMH_ACTIVE_ONLY_STATE:
+		if (req->sleep_val != UINT_MAX)
+			req->wake_val = cmd->data;
+		break;
+	case RPMH_WAKE_ONLY_STATE:
+		req->wake_val = cmd->data;
+		break;
+	case RPMH_SLEEP_STATE:
+		req->sleep_val = cmd->data;
+		break;
+	default:
+		break;
+	};
+
+	ctrlr->dirty = true;
+unlock:
+	spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+	return req;
+}
+
 /**
- * __rpmh_write: send the RPMH request
+ * __rpmh_write: Cache and send the RPMH request
  *
  * @dev: The device making the request
  * @state: Active/Sleep request type
  * @rpm_msg: The data that needs to be sent (cmds).
+ *
+ * Cache the RPMH request and send if the state is ACTIVE_ONLY.
+ * SLEEP/WAKE_ONLY requests are not sent to the controller at
+ * this time. Use rpmh_flush() to send them to the controller.
  */
 static int __rpmh_write(const struct device *dev, enum rpmh_state state,
 			struct rpmh_request *rpm_msg)
 {
 	struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+	int ret = -EINVAL;
+	struct cache_req *req;
+	int i;
 
 	if (IS_ERR(ctrlr))
 		return PTR_ERR(ctrlr);
 
 	rpm_msg->msg.state = state;
 
-	if (state != RPMH_ACTIVE_ONLY_STATE)
-		return -EINVAL;
+	/* Cache the request in our store and link the payload */
+	for (i = 0; i < rpm_msg->msg.num_cmds; i++) {
+		req = cache_rpm_request(ctrlr, state, &rpm_msg->msg.cmds[i]);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+	}
+
+	rpm_msg->msg.state = state;
 
-	WARN_ON(irqs_disabled());
+	if (state == RPMH_ACTIVE_ONLY_STATE) {
+		WARN_ON(irqs_disabled());
+		ret = rpmh_rsc_send_data(ctrlr->drv, &rpm_msg->msg);
+	} else {
+		ret = rpmh_rsc_write_ctrl_data(ctrlr->drv, &rpm_msg->msg);
+		/* Clean up our call by spoofing tx_done */
+		rpmh_tx_done(&rpm_msg->msg, ret);
+	}
 
-	return rpmh_rsc_send_data(ctrlr->drv, &rpm_msg->msg);
+	return ret;
 }
 
 /**
@@ -174,3 +279,105 @@ int rpmh_write(const struct device *dev, enum rpmh_state state,
 	return (ret > 0) ? 0 : -ETIMEDOUT;
 }
 EXPORT_SYMBOL(rpmh_write);
+
+static int is_req_valid(struct cache_req *req)
+{
+	return (req->sleep_val != UINT_MAX &&
+		req->wake_val != UINT_MAX &&
+		req->sleep_val != req->wake_val);
+}
+
+static int send_single(const struct device *dev, enum rpmh_state state,
+		       u32 addr, u32 data)
+{
+	DEFINE_RPMH_MSG_ONSTACK(dev, state, NULL, rpm_msg);
+	struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+
+	if (IS_ERR(ctrlr))
+		return PTR_ERR(ctrlr);
+
+	/* Wake sets are always complete and sleep sets are not */
+	rpm_msg.msg.wait_for_compl = (state == RPMH_WAKE_ONLY_STATE);
+	rpm_msg.cmd[0].addr = addr;
+	rpm_msg.cmd[0].data = data;
+	rpm_msg.msg.num_cmds = 1;
+
+	return rpmh_rsc_write_ctrl_data(ctrlr->drv, &rpm_msg.msg);
+}
+
+/**
+ * rpmh_flush: Flushes the buffered active and sleep sets to TCS
+ *
+ * @dev: The device making the request
+ *
+ * Return: -EBUSY if the controller is busy, probably waiting on a response
+ * to a RPMH request sent earlier.
+ *
+ * This function is generally called from the sleep code from the last CPU
+ * that is powering down the entire system. Since no other RPMH API would be
+ * executing at this time, it is safe to run lockless.
+ */
+int rpmh_flush(const struct device *dev)
+{
+	struct cache_req *p;
+	struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+	int ret;
+
+	if (IS_ERR(ctrlr))
+		return PTR_ERR(ctrlr);
+
+	if (!ctrlr->dirty) {
+		pr_debug("Skipping flush, TCS has latest data.\n");
+		return 0;
+	}
+
+	/*
+	 * Nobody else should be calling this function other than system PM,
+	 * hence we can run without locks.
+	 */
+	list_for_each_entry(p, &ctrlr->cache, list) {
+		if (!is_req_valid(p)) {
+			pr_debug("%s: skipping RPMH req: a:%#x s:%#x w:%#x",
+				 __func__, p->addr, p->sleep_val, p->wake_val);
+			continue;
+		}
+		ret = send_single(dev, RPMH_SLEEP_STATE, p->addr, p->sleep_val);
+		if (ret)
+			return ret;
+		ret = send_single(dev, RPMH_WAKE_ONLY_STATE,
+				  p->addr, p->wake_val);
+		if (ret)
+			return ret;
+	}
+
+	ctrlr->dirty = false;
+
+	return 0;
+}
+EXPORT_SYMBOL(rpmh_flush);
+
+/**
+ * rpmh_invalidate: Invalidate all sleep and active sets
+ * sets.
+ *
+ * @dev: The device making the request
+ *
+ * Invalidate the sleep and active values in the TCS blocks.
+ */
+int rpmh_invalidate(const struct device *dev)
+{
+	struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+	int ret;
+
+	if (IS_ERR(ctrlr))
+		return PTR_ERR(ctrlr);
+
+	ctrlr->dirty = true;
+
+	do {
+		ret = rpmh_rsc_invalidate(ctrlr->drv);
+	} while (ret == -EAGAIN);
+
+	return ret;
+}
+EXPORT_SYMBOL(rpmh_invalidate);
include/soc/qcom/rpmh.h +11 -0
index c1d0f902bd71..42e62a0d26d8 100644
@@ -14,12 +14,23 @@
 int rpmh_write(const struct device *dev, enum rpmh_state state,
 	       const struct tcs_cmd *cmd, u32 n);
 
+int rpmh_flush(const struct device *dev);
+
+int rpmh_invalidate(const struct device *dev);
+
 #else
 
 static inline int rpmh_write(const struct device *dev, enum rpmh_state state,
 			     const struct tcs_cmd *cmd, u32 n)
 { return -ENODEV; }
 
+
+static inline int rpmh_flush(const struct device *dev)
+{ return -ENODEV; }
+
+static inline int rpmh_invalidate(const struct device *dev)
+{ return -ENODEV; }
+
 #endif /* CONFIG_QCOM_RPMH */
 
 #endif /* __SOC_QCOM_RPMH_H__ */
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#18 lore raw

Re: [PATCH v8 07/10] drivers: qcom: rpmh: cache sleep/wake state requests

Hi,

drivers/soc/qcom/rpmh.c struct rpmh_request { · line 75

Quoted 9 lines · Lina Iyer

/**
* struct rpmh_request: the message to be sent to rpmh-rsc *

drivers/soc/qcom/rpmh.c +2 -0
@@ -54,9 +71,15 @@ struct rpmh_request {
  * struct rpmh_ctrlr: our representation of the controller
  *
  * @drv: the controller instance
+ * @cache: the list of cached requests
+ * @lock: synchronize access to the controller data

nit: this makes it sound like this lock will be grabbed for all calls into rpmh-rsc. In fact, it only protects access to the cache. Ideally name it cache_lock and document that it's for protecting the cache.

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 316

Quoted 9 lines
drivers/soc/qcom/rpmh.c +9 -0
+/**
+ * rpmh_flush: Flushes the buffered active and sleep sets to TCS
+ *
+ * @dev: The device making the request
+ *
+ * Return: -EBUSY if the controller is busy, probably waiting on a response
+ * to a RPMH request sent earlier.
+ *
+ * This function is generally called from the sleep code from the last CPU

"is generally" implies that sometimes it's not called from the sleep code. Change to "is always". If "is generally" is more correct, you can't run lockless right?

-Doug

#19 lore raw

Re: [PATCH v8 07/10] drivers: qcom: rpmh: cache sleep/wake state requests

Hi,

Quoted 29 lines · Doug Anderson

Hi,

drivers/soc/qcom/rpmh.c struct rpmh_request { · line 75

Quoted 9 lines · Lina Iyer

/**
* struct rpmh_request: the message to be sent to rpmh-rsc *

drivers/soc/qcom/rpmh.c +2 -0
@@ -54,9 +71,15 @@ struct rpmh_request {
   * struct rpmh_ctrlr: our representation of the controller
   *
   * @drv: the controller instance
+ * @cache: the list of cached requests
+ * @lock: synchronize access to the controller data

nit: this makes it sound like this lock will be grabbed for all calls into rpmh-rsc. In fact, it only protects access to the cache. Ideally name it cache_lock and document that it's for protecting the cache.

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 316

Quoted 9 lines
drivers/soc/qcom/rpmh.c +9 -0
+/**
+ * rpmh_flush: Flushes the buffered active and sleep sets to TCS
+ *
+ * @dev: The device making the request
+ *
+ * Return: -EBUSY if the controller is busy, probably waiting on a response
+ * to a RPMH request sent earlier.
+ *
+ * This function is generally called from the sleep code from the last CPU
Quoted 7 lines

"is generally" implies that sometimes it's not called from the sleep code. Change to "is always". If "is generally" is more correct, you can't run lockless right?

-Doug

Yes. Will change in next patch.

Thanks,
Raju

author
#20 lore raw

PATCH v8 8/10 drivers: qcom: rpmh: allow requests to be sent asynchronously

In reply to Lina Iyer

Platform drivers that want to send a request but do not want to block until the RPMH request completes have now a new API - rpmh_write_async().

The API allocates memory and send the requests and returns the control back to the platform driver. The tx_done callback from the controller is handled in the context of the controller's thread and frees the allocated memory. This API allows RPMH requests from atomic contexts as well.

---

Changes in v6:
- replace rpmh_client with device *

---
 drivers/soc/qcom/rpmh.c | 52 +++++++++++++++++++++++++++++++++++++++++
 include/soc/qcom/rpmh.h |  7 ++++++
 2 files changed, 59 insertions(+)
drivers/soc/qcom/rpmh.c +52 -0
index 23fcbd9487cd..1bb62876795c 100644
@@ -33,6 +33,7 @@
 		.cmd = { { 0 } },			\
 		.completion = q,			\
 		.dev = dev,				\
+		.free = NULL,				\
 	}
 
 /**
@@ -58,6 +59,7 @@ struct cache_req {
  * @completion: triggered when request is done
  * @dev: the device making the request
  * @err: err return from the controller
+ * @free: the request object to be freed at tx_done
  */
 struct rpmh_request {
 	struct tcs_request msg;
@@ -65,6 +67,7 @@ struct rpmh_request {
 	struct completion *completion;
 	const struct device *dev;
 	int err;
+	struct rpmh_request *free;
 };
 
 /**
@@ -137,6 +140,8 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
 		dev_err(rpm_msg->dev, "RPMH TX fail in msg addr=%#x, err=%d\n",
 			rpm_msg->msg.cmds[0].addr, r);
 
+	kfree(rpm_msg->free);
+
 	/* Signal the blocking thread we are done */
 	if (compl)
 		complete(compl);
@@ -248,6 +253,53 @@ static int __rpmh_write(const struct device *dev, enum rpmh_state state,
 	return ret;
 }
 
+static struct rpmh_request *__get_rpmh_msg_async(enum rpmh_state state,
+						 const struct tcs_cmd *cmd,
+						 u32 n)
+{
+	struct rpmh_request *req;
+
+	if (!cmd || !n || n > MAX_RPMH_PAYLOAD)
+		return ERR_PTR(-EINVAL);
+
+	req = kzalloc(sizeof(*req), GFP_ATOMIC);
+	if (!req)
+		return ERR_PTR(-ENOMEM);
+
+	memcpy(req->cmd, cmd, n * sizeof(*cmd));
+
+	req->msg.state = state;
+	req->msg.cmds = req->cmd;
+	req->msg.num_cmds = n;
+	req->free = req;
+
+	return req;
+}
+
+/**
+ * rpmh_write_async: Write a set of RPMH commands
+ *
+ * @dev: The device making the request
+ * @state: Active/sleep set
+ * @cmd: The payload data
+ * @n: The number of elements in payload
+ *
+ * Write a set of RPMH commands, the order of commands is maintained
+ * and will be sent as a single shot.
+ */
+int rpmh_write_async(const struct device *dev, enum rpmh_state state,
+		     const struct tcs_cmd *cmd, u32 n)
+{
+	struct rpmh_request *rpm_msg;
+
+	rpm_msg = __get_rpmh_msg_async(state, cmd, n);
+	if (IS_ERR(rpm_msg))
+		return PTR_ERR(rpm_msg);
+
+	return __rpmh_write(dev, state, rpm_msg);
+}
+EXPORT_SYMBOL(rpmh_write_async);
+
 /**
  * rpmh_write: Write a set of RPMH commands and block until response
  *
include/soc/qcom/rpmh.h +7 -0
index 42e62a0d26d8..1161a5c77e75 100644
@@ -14,6 +14,9 @@
 int rpmh_write(const struct device *dev, enum rpmh_state state,
 	       const struct tcs_cmd *cmd, u32 n);
 
+int rpmh_write_async(const struct device *dev, enum rpmh_state state,
+		     const struct tcs_cmd *cmd, u32 n);
+
 int rpmh_flush(const struct device *dev);
 
 int rpmh_invalidate(const struct device *dev);
@@ -24,6 +27,10 @@ static inline int rpmh_write(const struct device *dev, enum rpmh_state state,
 			     const struct tcs_cmd *cmd, u32 n)
 { return -ENODEV; }
 
+static inline int rpmh_write_async(const struct device *dev,
+				   enum rpmh_state state,
+				   const struct tcs_cmd *cmd, u32 n)
+{ return -ENODEV; }
 
 static inline int rpmh_flush(const struct device *dev)
 { return -ENODEV; }
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Reviewed-by on v8 8/10
#21 lore raw

Re: [PATCH v8 08/10] drivers: qcom: rpmh: allow requests to be sent asynchronously

Quoted 11 lines · Lina Iyer

Platform drivers that want to send a request but do not want to block until the RPMH request completes have now a new API - rpmh_write_async().

The API allocates memory and send the requests and returns the control back to the platform driver. The tx_done callback from the controller is handled in the context of the controller's thread and frees the allocated memory. This API allows RPMH requests from atomic contexts as well.

#22 lore raw

Re: [PATCH v8 08/10] drivers: qcom: rpmh: allow requests to be sent asynchronously

In reply to Lina Iyer

Hi,

drivers/soc/qcom/rpmh.c void rpmh_tx_done(const struct tcs_request *msg, int r) · line 144

Quoted 7 lines · Lina Iyer

/**

drivers/soc/qcom/rpmh.c +2 -0
@@ -137,6 +140,8 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
                dev_err(rpm_msg->dev, "RPMH TX fail in msg addr=%#x, err=%d\n",
                        rpm_msg->msg.cmds[0].addr, r);
 
+       kfree(rpm_msg->free);
+

The way the code is written makes it seem like you could free memory _and_ have a completion but you can't. Specifically:

* The only plausible thing that "rpm_msg->free" could point to is "rpm_msg".

* The complete(compl) would then be accessing freed memory.

I believe the kfree() should be at the end of the function. Personally I'd make it more obvious that this is just a boolean value and change to:

if (rpm_msg->needs_free)
kgree(rpm_msg)

...then the reader of the code doesn't need to go figure out what you're trying to free.

-Doug

#23 lore raw

Re: [PATCH v8 08/10] drivers: qcom: rpmh: allow requests to be sent asynchronously

Hi,

Quoted 14 lines · Doug Anderson

Hi,

drivers/soc/qcom/rpmh.c void rpmh_tx_done(const struct tcs_request *msg, int r) · line 144

Quoted 7 lines · Lina Iyer

/**

drivers/soc/qcom/rpmh.c +2 -0
@@ -137,6 +140,8 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
                 dev_err(rpm_msg->dev, "RPMH TX fail in msg addr=%#x, err=%d\n",
                         rpm_msg->msg.cmds[0].addr, r);
 
+       kfree(rpm_msg->free);
+

The way the code is written makes it seem like you could free memory _and_ have a completion but you can't. Specifically:

Quoted 3 lines

* The only plausible thing that "rpm_msg->free" could point to is "rpm_msg".

* The complete(compl) would then be accessing freed memory.

As the completions are declared on stack, it will not access freed memory.

Quoted 5 lines

I believe the kfree() should be at the end of the function. Personally I'd make it more obvious that this is just a boolean value and change to:

Quoted 9 lines

if (rpm_msg->needs_free)
kgree(rpm_msg)

...then the reader of the code doesn't need to go figure out what you're trying to free.

-Doug

Yes. Will change it this way in next patch set.

Thanks,
Raju

author
#24 lore raw

PATCH v8 9/10 drivers: qcom: rpmh: add support for batch RPMH request

In reply to Lina Iyer

Platform drivers need make a lot of resource state requests at the same time, say, at the start or end of an usecase. It can be quite inefficient to send each request separately. Instead they can give the RPMH library a batch of requests to be sent and wait on the whole transaction to be complete.

rpmh_write_batch() is a blocking call that can be used to send multiple RPMH command sets. Each RPMH command set is set asynchronously and the API blocks until all the command sets are complete and receive their tx_done callbacks.

---

Changes in v7:
- Check for loop out of bounds

Changes in v6:
- replace rpmh_client with device *
Changes in v4:
- reorganize rpmh_write_batch()
- introduce wait_count here, instead of patch#4

---
 drivers/soc/qcom/rpmh.c | 154 +++++++++++++++++++++++++++++++++++++++-
 include/soc/qcom/rpmh.h |   8 +++
 2 files changed, 160 insertions(+), 2 deletions(-)
drivers/soc/qcom/rpmh.c +152 -2
index 1bb62876795c..a0e277b4b846 100644
@@ -21,6 +21,8 @@
 #include "rpmh-internal.h"
 
 #define RPMH_TIMEOUT_MS			msecs_to_jiffies(10000)
+#define RPMH_MAX_REQ_IN_BATCH		10
+#define RPMH_MAX_BATCH_CACHE		(2 * RPMH_MAX_REQ_IN_BATCH)
 
 #define DEFINE_RPMH_MSG_ONSTACK(dev, s, q, name)	\
 	struct rpmh_request name = {			\
@@ -34,6 +36,7 @@
 		.completion = q,			\
 		.dev = dev,				\
 		.free = NULL,				\
+		.wait_count = NULL,			\
 	}
 
 /**
@@ -60,6 +63,7 @@ struct cache_req {
  * @dev: the device making the request
  * @err: err return from the controller
  * @free: the request object to be freed at tx_done
+ * @wait_count: count of waiters for this completion
  */
 struct rpmh_request {
 	struct tcs_request msg;
@@ -68,6 +72,7 @@ struct rpmh_request {
 	const struct device *dev;
 	int err;
 	struct rpmh_request *free;
+	atomic_t *wait_count;
 };
 
 /**
@@ -77,12 +82,14 @@ struct rpmh_request {
  * @cache: the list of cached requests
  * @lock: synchronize access to the controller data
  * @dirty: was the cache updated since flush
+ * @batch_cache: Cache sleep and wake requests sent as batch
  */
 struct rpmh_ctrlr {
 	struct rsc_drv *drv;
 	struct list_head cache;
 	spinlock_t lock;
 	bool dirty;
+	const struct rpmh_request *batch_cache[RPMH_MAX_BATCH_CACHE];
 };
 
 static struct rpmh_ctrlr rpmh_rsc[RPMH_MAX_CTRLR];
@@ -133,6 +140,7 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
 	struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request,
 						    msg);
 	struct completion *compl = rpm_msg->completion;
+	atomic_t *wc = rpm_msg->wait_count;
 
 	rpm_msg->err = r;
 
@@ -143,8 +151,13 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
 	kfree(rpm_msg->free);
 
 	/* Signal the blocking thread we are done */
-	if (compl)
-		complete(compl);
+	if (!compl)
+		return;
+
+	if (wc && !atomic_dec_and_test(wc))
+		return;
+
+	complete(compl);
 }
 EXPORT_SYMBOL(rpmh_tx_done);
 
@@ -332,6 +345,137 @@ int rpmh_write(const struct device *dev, enum rpmh_state state,
 }
 EXPORT_SYMBOL(rpmh_write);
 
+static int cache_batch(struct rpmh_ctrlr *ctrlr,
+		       struct rpmh_request **rpm_msg, int count)
+{
+	unsigned long flags;
+	int ret = 0;
+	int index = 0;
+	int i;
+
+	spin_lock_irqsave(&ctrlr->lock, flags);
+	while (index < RPMH_MAX_BATCH_CACHE && ctrlr->batch_cache[index])
+		index++;
+	if (index + count >= RPMH_MAX_BATCH_CACHE) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	for (i = 0; i < count; i++)
+		ctrlr->batch_cache[index + i] = rpm_msg[i];
+fail:
+	spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+	return ret;
+}
+
+static int flush_batch(struct rpmh_ctrlr *ctrlr)
+{
+	const struct rpmh_request *rpm_msg;
+	unsigned long flags;
+	int ret = 0;
+	int i;
+
+	/* Send Sleep/Wake requests to the controller, expect no response */
+	spin_lock_irqsave(&ctrlr->lock, flags);
+	for (i = 0; ctrlr->batch_cache[i]; i++) {
+		rpm_msg = ctrlr->batch_cache[i];
+		ret = rpmh_rsc_write_ctrl_data(ctrlr->drv, &rpm_msg->msg);
+		if (ret)
+			break;
+	}
+	spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+	return ret;
+}
+
+static void invalidate_batch(struct rpmh_ctrlr *ctrlr)
+{
+	unsigned long flags;
+	int index = 0;
+
+	spin_lock_irqsave(&ctrlr->lock, flags);
+	while (ctrlr->batch_cache[index]) {
+		kfree(ctrlr->batch_cache[index]->free);
+		ctrlr->batch_cache[index] = NULL;
+		index++;
+	}
+	spin_unlock_irqrestore(&ctrlr->lock, flags);
+}
+
+/**
+ * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the
+ * batch to finish.
+ *
+ * @dev: the device making the request
+ * @state: Active/sleep set
+ * @cmd: The payload data
+ * @n: The array of count of elements in each batch, 0 terminated.
+ *
+ * Write a request to the RSC controller without caching. If the request
+ * state is ACTIVE, then the requests are treated as completion request
+ * and sent to the controller immediately. The function waits until all the
+ * commands are complete. If the request was to SLEEP or WAKE_ONLY, then the
+ * request is sent as fire-n-forget and no ack is expected.
+ *
+ * May sleep. Do not call from atomic contexts for ACTIVE_ONLY requests.
+ */
+int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
+		     const struct tcs_cmd *cmd, u32 *n)
+{
+	struct rpmh_request *rpm_msg[RPMH_MAX_REQ_IN_BATCH] = { NULL };
+	DECLARE_COMPLETION_ONSTACK(compl);
+	atomic_t wait_count = ATOMIC_INIT(0);
+	struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+	int count = 0;
+	int ret, i;
+
+	if (IS_ERR(ctrlr) || !cmd || !n)
+		return -EINVAL;
+
+	while (n[count++] > 0)
+		;
+	count--;
+	if (!count || count > RPMH_MAX_REQ_IN_BATCH)
+		return -EINVAL;
+
+	for (i = 0; i < count; i++) {
+		rpm_msg[i] = __get_rpmh_msg_async(state, cmd, n[i]);
+		if (IS_ERR_OR_NULL(rpm_msg[i])) {
+			ret = PTR_ERR(rpm_msg[i]);
+			for (; i >= 0; i--)
+				kfree(rpm_msg[i]->free);
+			return ret;
+		}
+		cmd += n[i];
+	}
+
+	if (state != RPMH_ACTIVE_ONLY_STATE)
+		return cache_batch(ctrlr, rpm_msg, count);
+
+	atomic_set(&wait_count, count);
+
+	for (i = 0; i < count; i++) {
+		rpm_msg[i]->completion = &compl;
+		rpm_msg[i]->wait_count = &wait_count;
+		ret = rpmh_rsc_send_data(ctrlr->drv, &rpm_msg[i]->msg);
+		if (ret) {
+			int j;
+
+			pr_err("Error(%d) sending RPMH message addr=%#x\n",
+			       ret, rpm_msg[i]->msg.cmds[0].addr);
+			for (j = i; j < count; j++)
+				rpmh_tx_done(&rpm_msg[j]->msg, ret);
+			break;
+		}
+	}
+
+	ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);
+	return (ret > 0) ? 0 : -ETIMEDOUT;
+
+}
+EXPORT_SYMBOL(rpmh_write_batch);
+
 static int is_req_valid(struct cache_req *req)
 {
 	return (req->sleep_val != UINT_MAX &&
@@ -383,6 +527,11 @@ int rpmh_flush(const struct device *dev)
 		return 0;
 	}
 
+	/* First flush the cached batch requests */
+	ret = flush_batch(ctrlr);
+	if (ret)
+		return ret;
+
 	/*
 	 * Nobody else should be calling this function other than system PM,
 	 * hence we can run without locks.
@@ -424,6 +573,7 @@ int rpmh_invalidate(const struct device *dev)
 	if (IS_ERR(ctrlr))
 		return PTR_ERR(ctrlr);
 
+	invalidate_batch(ctrlr);
 	ctrlr->dirty = true;
 
 	do {
include/soc/qcom/rpmh.h +8 -0
index 1161a5c77e75..619e07c75da9 100644
@@ -17,6 +17,9 @@ int rpmh_write(const struct device *dev, enum rpmh_state state,
 int rpmh_write_async(const struct device *dev, enum rpmh_state state,
 		     const struct tcs_cmd *cmd, u32 n);
 
+int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
+		     const struct tcs_cmd *cmd, u32 *n);
+
 int rpmh_flush(const struct device *dev);
 
 int rpmh_invalidate(const struct device *dev);
@@ -32,6 +35,11 @@ static inline int rpmh_write_async(const struct device *dev,
 				   const struct tcs_cmd *cmd, u32 n)
 { return -ENODEV; }
 
+static inline int rpmh_write_batch(const struct device *dev,
+				   enum rpmh_state state,
+				   const struct tcs_cmd *cmd, u32 *n)
+{ return -ENODEV; }
+
 static inline int rpmh_flush(const struct device *dev)
 { return -ENODEV; }
 
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#25 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

Quoted 34 lines · Lina Iyer

Platform drivers need make a lot of resource state requests at the same time, say, at the start or end of an usecase. It can be quite inefficient to send each request separately. Instead they can give the RPMH library a batch of requests to be sent and wait on the whole transaction to be complete.

rpmh_write_batch() is a blocking call that can be used to send multiple RPMH command sets. Each RPMH command set is set asynchronously and the API blocks until all the command sets are complete and receive their tx_done callbacks.

---

Changes in v7:
- Check for loop out of bounds

Changes in v6:
- replace rpmh_client with device *
Changes in v4:
- reorganize rpmh_write_batch()
- introduce wait_count here, instead of patch#4

---
 drivers/soc/qcom/rpmh.c | 154 +++++++++++++++++++++++++++++++++++++++-
 include/soc/qcom/rpmh.h |   8 +++
 2 files changed, 160 insertions(+), 2 deletions(-)
drivers/soc/qcom/rpmh.c +0 -0
index 1bb62876795c..a0e277b4b846 100644
 
...
 

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 381

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+static int flush_batch(struct rpmh_ctrlr *ctrlr)
+{
+	const struct rpmh_request *rpm_msg;
+	unsigned long flags;
+	int ret = 0;
+	int i;
+
+	/* Send Sleep/Wake requests to the controller, expect no response */
+	spin_lock_irqsave(&ctrlr->lock, flags);
+	for (i = 0; ctrlr->batch_cache[i]; i++) {

I missed this earlier: the loop goes beyond ctrlr->batch_cache when the batch cache is full.

Quoted 7 lines
drivers/soc/qcom/rpmh.c +7 -0
+		rpm_msg = ctrlr->batch_cache[i];
+		ret = rpmh_rsc_write_ctrl_data(ctrlr->drv, &rpm_msg->msg);
+		if (ret)
+			break;
+	}
+	spin_unlock_irqrestore(&ctrlr->lock, flags);
+

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 398

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+	return ret;
+}
+
+static void invalidate_batch(struct rpmh_ctrlr *ctrlr)
+{
+	unsigned long flags;
+	int index = 0;
+
+	spin_lock_irqsave(&ctrlr->lock, flags);
+	while (ctrlr->batch_cache[index]) {

This still goes beyond ctrlr->batch_cache when the batch cache is full.

author
#26 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

Quoted 61 lines · Matthias Kaehlcke
Quoted 34 lines · Lina Iyer

Platform drivers need make a lot of resource state requests at the same time, say, at the start or end of an usecase. It can be quite inefficient to send each request separately. Instead they can give the RPMH library a batch of requests to be sent and wait on the whole transaction to be complete.

rpmh_write_batch() is a blocking call that can be used to send multiple RPMH command sets. Each RPMH command set is set asynchronously and the API blocks until all the command sets are complete and receive their tx_done callbacks.

---

Changes in v7:
- Check for loop out of bounds

Changes in v6:
- replace rpmh_client with device *
Changes in v4:
- reorganize rpmh_write_batch()
- introduce wait_count here, instead of patch#4

---
 drivers/soc/qcom/rpmh.c | 154 +++++++++++++++++++++++++++++++++++++++-
 include/soc/qcom/rpmh.h |   8 +++
 2 files changed, 160 insertions(+), 2 deletions(-)
drivers/soc/qcom/rpmh.c +0 -0
index 1bb62876795c..a0e277b4b846 100644
 
...
 

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 381

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+static int flush_batch(struct rpmh_ctrlr *ctrlr)
+{
+	const struct rpmh_request *rpm_msg;
+	unsigned long flags;
+	int ret = 0;
+	int i;
+
+	/* Send Sleep/Wake requests to the controller, expect no response */
+	spin_lock_irqsave(&ctrlr->lock, flags);
+	for (i = 0; ctrlr->batch_cache[i]; i++) {

I missed this earlier: the loop goes beyond ctrlr->batch_cache when the batch cache is full.

Quoted 12 lines
drivers/soc/qcom/rpmh.c +12 -0
+		rpm_msg = ctrlr->batch_cache[i];
+		ret = rpmh_rsc_write_ctrl_data(ctrlr->drv, &rpm_msg->msg);
+		if (ret)
+			break;
+	}
+	spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+	return ret;
+}
+
+static void invalidate_batch(struct rpmh_ctrlr *ctrlr)
+{
Quoted 8 lines

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 398

Quoted 5 lines
drivers/soc/qcom/rpmh.c +5 -0
+	unsigned long flags;
+	int index = 0;
+
+	spin_lock_irqsave(&ctrlr->lock, flags);
+	while (ctrlr->batch_cache[index]) {

This still goes beyond ctrlr->batch_cache when the batch cache is full.

I will check through the code for all out-of-bounds. Seems like I have not worried about it too much. Well the space here assigned is beyond what we see on a production device. Neverthless, it needs to be checked.

Thanks for your review Matthias.

-- Lina

#27 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

In reply to Lina Iyer

Hi,

drivers/soc/qcom/rpmh.c struct rpmh_request { · line 92

Quoted 13 lines · Lina Iyer

/**

drivers/soc/qcom/rpmh.c +2 -0
@@ -77,12 +82,14 @@ struct rpmh_request {
  * @cache: the list of cached requests
  * @lock: synchronize access to the controller data
  * @dirty: was the cache updated since flush
+ * @batch_cache: Cache sleep and wake requests sent as batch
  */
 struct rpmh_ctrlr {
        struct rsc_drv *drv;
        struct list_head cache;
        spinlock_t lock;
        bool dirty;
+       const struct rpmh_request *batch_cache[RPMH_MAX_BATCH_CACHE];

I'm pretty confused about why the "batch_cache" is separate from the normal cache. As far as I can tell the purpose of the two is the same but you have two totally separate code paths and data structures.

Quoted 12 lines
drivers/soc/qcom/rpmh.c +1 -0
 };
 
 static struct rpmh_ctrlr rpmh_rsc[RPMH_MAX_CTRLR];
@@ -133,6 +140,7 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
        struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request,
                                                    msg);
        struct completion *compl = rpm_msg->completion;
+       atomic_t *wc = rpm_msg->wait_count;
 
        rpm_msg->err = r;
 

drivers/soc/qcom/rpmh.c void rpmh_tx_done(const struct tcs_request *msg, int r) · line 155

Quoted 7 lines
drivers/soc/qcom/rpmh.c +2 -2
@@ -143,8 +151,13 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
        kfree(rpm_msg->free);
 
        /* Signal the blocking thread we are done */
-       if (compl)
-               complete(compl);
+       if (!compl)
+               return;

The comment above this "if" block no longer applies to the line next to it after your patch. ...but below I suggest you get rid of "wait_count", so maybe this part of the patch will go away.

Quoted 9 lines
drivers/soc/qcom/rpmh.c +9 -0
+static int cache_batch(struct rpmh_ctrlr *ctrlr,
+                      struct rpmh_request **rpm_msg, int count)
+{
+       unsigned long flags;
+       int ret = 0;
+       int index = 0;
+       int i;
+
+       spin_lock_irqsave(&ctrlr->lock, flags);

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 366

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+       while (index < RPMH_MAX_BATCH_CACHE && ctrlr->batch_cache[index])
+               index++;
+       if (index + count >= RPMH_MAX_BATCH_CACHE) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       for (i = 0; i < count; i++)
+               ctrlr->batch_cache[index + i] = rpm_msg[i];
+fail:

Nit: this label is for both failure and normal exit, so call it "exit".

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 370

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+       return ret;
+}

As part of my overall confusion about why the batch cache is different than the normal one: for the normal use case you still call rpmh_rsc_write_ctrl_data() for things you put in your cache, but you don't for the batch cache. I still haven't totally figured out what rpmh_rsc_write_ctrl_data() does, but it seems strange that you don't do it for the batch cache but you do for the other one.

Quoted 29 lines
drivers/soc/qcom/rpmh.c +29 -0
+/**
+ * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the
+ * batch to finish.
+ *
+ * @dev: the device making the request
+ * @state: Active/sleep set
+ * @cmd: The payload data
+ * @n: The array of count of elements in each batch, 0 terminated.
+ *
+ * Write a request to the RSC controller without caching. If the request
+ * state is ACTIVE, then the requests are treated as completion request
+ * and sent to the controller immediately. The function waits until all the
+ * commands are complete. If the request was to SLEEP or WAKE_ONLY, then the
+ * request is sent as fire-n-forget and no ack is expected.
+ *
+ * May sleep. Do not call from atomic contexts for ACTIVE_ONLY requests.
+ */
+int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
+                    const struct tcs_cmd *cmd, u32 *n)
+{
+       struct rpmh_request *rpm_msg[RPMH_MAX_REQ_IN_BATCH] = { NULL };
+       DECLARE_COMPLETION_ONSTACK(compl);
+       atomic_t wait_count = ATOMIC_INIT(0);
+       struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+       int count = 0;
+       int ret, i;
+
+       if (IS_ERR(ctrlr) || !cmd || !n)
+               return -EINVAL;

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 444

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+
+       while (n[count++] > 0)
+               ;
+       count--;
+       if (!count || count > RPMH_MAX_REQ_IN_BATCH)
+               return -EINVAL;
+
+       for (i = 0; i < count; i++) {
+               rpm_msg[i] = __get_rpmh_msg_async(state, cmd, n[i]);
+               if (IS_ERR_OR_NULL(rpm_msg[i])) {

Just "IS_ERR". It's never NULL.

...also add a i-- somewhere in here or you're going to be kfree()ing your error value, aren't you?

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 454

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+                       ret = PTR_ERR(rpm_msg[i]);
+                       for (; i >= 0; i--)
+                               kfree(rpm_msg[i]->free);
+                       return ret;
+               }
+               cmd += n[i];
+       }
+
+       if (state != RPMH_ACTIVE_ONLY_STATE)
+               return cache_batch(ctrlr, rpm_msg, count);

Don't you need to free rpm_msg items in this case?

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+
+       atomic_set(&wait_count, count);
+
+       for (i = 0; i < count; i++) {

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 468

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+               rpm_msg[i]->completion = &compl;
+               rpm_msg[i]->wait_count = &wait_count;
+               ret = rpmh_rsc_send_data(ctrlr->drv, &rpm_msg[i]->msg);
+               if (ret) {
+                       int j;
+
+                       pr_err("Error(%d) sending RPMH message addr=%#x\n",
+                              ret, rpm_msg[i]->msg.cmds[0].addr);
+                       for (j = i; j < count; j++)
+                               rpmh_tx_done(&rpm_msg[j]->msg, ret);

You're just using rpmh_tx_done() to free memory? Note that you'll probably do your error handling in this function a favor if you rename __get_rpmh_msg_async() to __fill_rpmh_msg() and remove the memory allocation from there. Then you can do one big allocation of the whole array in rpmh_write_batch() and then you'll only have one free at the end...

Quoted 1 line

+ break;

"break" seems wrong here. You'll end up waiting for the completion, then I guess timing out, then returning -ETIMEDOUT?

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 473

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+               }
+       }
+
+       ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);

The "wait_count" abstraction is confusing and I believe it's not needed. I think you can remove it and change the above to this (untested) code:

time_left = RPMH_TIMEOUT_MS;
for (i = 0; i < count; i++) {
time_left = wait_for_completion_timeout(&compl, time_left); if (!time_left)

    return -ETIMEDOUT;
}

...specifically completions are additive, so just wait "count" times and then the reader doesn't need to learn your new wait_count abstraction and try to reason about it.

...and, actually, I argue in other replies that this should't use a timeout, so even cleaner:

for (i = 0; i < count; i++)
wait_for_completion(&compl);

Once you do that, you can also get rid of the need to pre-count "n", so all your loops turn into:

for (i = 0; n[i]; i++)

I suppose you might want to get rid of "RPMH_MAX_REQ_IN_BATCH" and dynamically allocate your array too, but that seems sane. As per above it seems like you should just dynamically allocate a whole array of "struct rpmh_request" items at once anyway.

---

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 477

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       return (ret > 0) ? 0 : -ETIMEDOUT;
+
+}
+EXPORT_SYMBOL(rpmh_write_batch);

Perhaps an even simpler thing than taking all my advice above: can't you just add a optional completion to rpmh_write_async()? That would just be stuffed into rpm_msg.

Now your batch code would just be a bunch of calls to rpmh_write_async() with an equal number of wait_for_completion() calls at the end. Is there a reason that wouldn't work? You'd get rid of _a lot_ of code.

-Doug

author
#28 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

Hi Doug,

Will explain only the key points now.

Quoted 12 lines · Doug Anderson

Hi,

Quoted 9 lines · Lina Iyer

/**

drivers/soc/qcom/rpmh.c +1 -0
@@ -77,12 +82,14 @@ struct rpmh_request {
  * @cache: the list of cached requests
  * @lock: synchronize access to the controller data
  * @dirty: was the cache updated since flush
+ * @batch_cache: Cache sleep and wake requests sent as batch
  */
 struct rpmh_ctrlr {
        struct rsc_drv *drv;
Quoted 9 lines

drivers/soc/qcom/rpmh.c struct rpmh_request { · line 92

Quoted 4 lines
drivers/soc/qcom/rpmh.c +1 -0
        struct list_head cache;
        spinlock_t lock;
        bool dirty;
+       const struct rpmh_request *batch_cache[RPMH_MAX_BATCH_CACHE];

I'm pretty confused about why the "batch_cache" is separate from the normal cache. As far as I can tell the purpose of the two is the same but you have two totally separate code paths and data structures.

Due to a hardware limitation, requests made by bus drivers must be set up in the sleep and wake TCS first before setting up the requests from other drivers. Bus drivers use batch mode for any and all RPMH communication. Hence their request are the only ones in the batch_cache.

Quoted 48 lines
Quoted 12 lines
drivers/soc/qcom/rpmh.c +1 -0
 };
 
 static struct rpmh_ctrlr rpmh_rsc[RPMH_MAX_CTRLR];
@@ -133,6 +140,7 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
        struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request,
                                                    msg);
        struct completion *compl = rpm_msg->completion;
+       atomic_t *wc = rpm_msg->wait_count;
 
        rpm_msg->err = r;
 

drivers/soc/qcom/rpmh.c void rpmh_tx_done(const struct tcs_request *msg, int r) · line 155

Quoted 7 lines
drivers/soc/qcom/rpmh.c +2 -2
@@ -143,8 +151,13 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
        kfree(rpm_msg->free);
 
        /* Signal the blocking thread we are done */
-       if (compl)
-               complete(compl);
+       if (!compl)
+               return;

The comment above this "if" block no longer applies to the line next to it after your patch. ...but below I suggest you get rid of "wait_count", so maybe this part of the patch will go away.

Quoted 9 lines
drivers/soc/qcom/rpmh.c +9 -0
+static int cache_batch(struct rpmh_ctrlr *ctrlr,
+                      struct rpmh_request **rpm_msg, int count)
+{
+       unsigned long flags;
+       int ret = 0;
+       int index = 0;
+       int i;
+
+       spin_lock_irqsave(&ctrlr->lock, flags);

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 366

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+       while (index < RPMH_MAX_BATCH_CACHE && ctrlr->batch_cache[index])
+               index++;
+       if (index + count >= RPMH_MAX_BATCH_CACHE) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       for (i = 0; i < count; i++)
+               ctrlr->batch_cache[index + i] = rpm_msg[i];
+fail:

Nit: this label is for both failure and normal exit, so call it "exit".

Quoted 13 lines

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 370

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+       return ret;
+}

As part of my overall confusion about why the batch cache is different than the normal one: for the normal use case you still call rpmh_rsc_write_ctrl_data() for things you put in your cache, but you don't for the batch cache. I still haven't totally figured out what rpmh_rsc_write_ctrl_data() does, but it seems strange that you don't do it for the batch cache but you do for the other one.

flush_batch does write to the controller using
rpmh_rsc_write_ctrl_data()

Thanks,
Lina

Quoted 146 lines
Quoted 29 lines
drivers/soc/qcom/rpmh.c +29 -0
+/**
+ * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the
+ * batch to finish.
+ *
+ * @dev: the device making the request
+ * @state: Active/sleep set
+ * @cmd: The payload data
+ * @n: The array of count of elements in each batch, 0 terminated.
+ *
+ * Write a request to the RSC controller without caching. If the request
+ * state is ACTIVE, then the requests are treated as completion request
+ * and sent to the controller immediately. The function waits until all the
+ * commands are complete. If the request was to SLEEP or WAKE_ONLY, then the
+ * request is sent as fire-n-forget and no ack is expected.
+ *
+ * May sleep. Do not call from atomic contexts for ACTIVE_ONLY requests.
+ */
+int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
+                    const struct tcs_cmd *cmd, u32 *n)
+{
+       struct rpmh_request *rpm_msg[RPMH_MAX_REQ_IN_BATCH] = { NULL };
+       DECLARE_COMPLETION_ONSTACK(compl);
+       atomic_t wait_count = ATOMIC_INIT(0);
+       struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+       int count = 0;
+       int ret, i;
+
+       if (IS_ERR(ctrlr) || !cmd || !n)
+               return -EINVAL;

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 444

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+
+       while (n[count++] > 0)
+               ;
+       count--;
+       if (!count || count > RPMH_MAX_REQ_IN_BATCH)
+               return -EINVAL;
+
+       for (i = 0; i < count; i++) {
+               rpm_msg[i] = __get_rpmh_msg_async(state, cmd, n[i]);
+               if (IS_ERR_OR_NULL(rpm_msg[i])) {

Just "IS_ERR". It's never NULL.

...also add a i-- somewhere in here or you're going to be kfree()ing your error value, aren't you?

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 454

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+                       ret = PTR_ERR(rpm_msg[i]);
+                       for (; i >= 0; i--)
+                               kfree(rpm_msg[i]->free);
+                       return ret;
+               }
+               cmd += n[i];
+       }
+
+       if (state != RPMH_ACTIVE_ONLY_STATE)
+               return cache_batch(ctrlr, rpm_msg, count);

Don't you need to free rpm_msg items in this case?

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+
+       atomic_set(&wait_count, count);
+
+       for (i = 0; i < count; i++) {

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 468

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+               rpm_msg[i]->completion = &compl;
+               rpm_msg[i]->wait_count = &wait_count;
+               ret = rpmh_rsc_send_data(ctrlr->drv, &rpm_msg[i]->msg);
+               if (ret) {
+                       int j;
+
+                       pr_err("Error(%d) sending RPMH message addr=%#x\n",
+                              ret, rpm_msg[i]->msg.cmds[0].addr);
+                       for (j = i; j < count; j++)
+                               rpmh_tx_done(&rpm_msg[j]->msg, ret);

You're just using rpmh_tx_done() to free memory? Note that you'll probably do your error handling in this function a favor if you rename __get_rpmh_msg_async() to __fill_rpmh_msg() and remove the memory allocation from there. Then you can do one big allocation of the whole array in rpmh_write_batch() and then you'll only have one free at the end...

Quoted 1 line

+ break;

"break" seems wrong here. You'll end up waiting for the completion, then I guess timing out, then returning -ETIMEDOUT?

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 473

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+               }
+       }
+
+       ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);

The "wait_count" abstraction is confusing and I believe it's not needed. I think you can remove it and change the above to this (untested) code:

time_left = RPMH_TIMEOUT_MS;
for (i = 0; i < count; i++) {
time_left = wait_for_completion_timeout(&compl, time_left); if (!time_left)
return -ETIMEDOUT;
}

...specifically completions are additive, so just wait "count" times and then the reader doesn't need to learn your new wait_count abstraction and try to reason about it.

...and, actually, I argue in other replies that this should't use a timeout, so even cleaner:

for (i = 0; i < count; i++)
wait_for_completion(&compl);

Once you do that, you can also get rid of the need to pre-count "n", so all your loops turn into:

for (i = 0; n[i]; i++)

I suppose you might want to get rid of "RPMH_MAX_REQ_IN_BATCH" and dynamically allocate your array too, but that seems sane. As per above it seems like you should just dynamically allocate a whole array of "struct rpmh_request" items at once anyway.

---

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 477

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       return (ret > 0) ? 0 : -ETIMEDOUT;
+
+}
+EXPORT_SYMBOL(rpmh_write_batch);

Perhaps an even simpler thing than taking all my advice above: can't you just add a optional completion to rpmh_write_async()? That would just be stuffed into rpm_msg.

Now your batch code would just be a bunch of calls to rpmh_write_async() with an equal number of wait_for_completion() calls at the end. Is there a reason that wouldn't work? You'd get rid of _a lot_ of code.

-Doug

#29 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

Hi,

Quoted 19 lines · Lina Iyer
Quoted 10 lines
Quoted 10 lines

/**

drivers/soc/qcom/rpmh.c +1 -0
@@ -77,12 +82,14 @@ struct rpmh_request {
  * @cache: the list of cached requests
  * @lock: synchronize access to the controller data
  * @dirty: was the cache updated since flush
+ * @batch_cache: Cache sleep and wake requests sent as batch
  */
 struct rpmh_ctrlr {
        struct rsc_drv *drv;
        struct list_head cache;
Quoted 9 lines

drivers/soc/qcom/rpmh.c struct rpmh_request { · line 92

Quoted 3 lines
drivers/soc/qcom/rpmh.c +1 -0
        spinlock_t lock;
        bool dirty;
+       const struct rpmh_request *batch_cache[RPMH_MAX_BATCH_CACHE];

I'm pretty confused about why the "batch_cache" is separate from the normal cache. As far as I can tell the purpose of the two is the same but you have two totally separate code paths and data structures.

Quoted 4 lines

Due to a hardware limitation, requests made by bus drivers must be set up in the sleep and wake TCS first before setting up the requests from other drivers. Bus drivers use batch mode for any and all RPMH communication. Hence their request are the only ones in the batch_cache.

This is totally not obvious and not commented. Why not rename to "priority" instead of "batch"?

If the only requirement is that these come first, that's still no reason to use totally separate code paths and mechanisms. These requests can still use the same data structures / functions and just be ordered to come first, can't they? ...or given a boolean "priority" and you can do two passes through your queue: one to do the priority ones and one to do the secondary ones.

Quoted 14 lines
Quoted 6 lines

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 370

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+       return ret;
+}
Quoted 8 lines

As part of my overall confusion about why the batch cache is different than the normal one: for the normal use case you still call rpmh_rsc_write_ctrl_data() for things you put in your cache, but you don't for the batch cache. I still haven't totally figured out what rpmh_rsc_write_ctrl_data() does, but it seems strange that you don't do it for the batch cache but you do for the other one.

Quoted 2 lines

flush_batch does write to the controller using
rpmh_rsc_write_ctrl_data()

My confusion is that they happen at different times. As I understand it:

* For the normal case, you immediately calls rpmh_rsc_write_ctrl_data() and then later do the rest of the work.

* For the batch case, you call both later.

Is there a good reason for this, or is it just an arbitrary difference? If there's a good reason, it should be documented.

-Doug

author
#30 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

Quoted 31 lines · Doug Anderson

Hi,

Quoted 19 lines · Lina Iyer
Quoted 10 lines
Quoted 10 lines

/**

drivers/soc/qcom/rpmh.c +1 -0
@@ -77,12 +82,14 @@ struct rpmh_request {
  * @cache: the list of cached requests
  * @lock: synchronize access to the controller data
  * @dirty: was the cache updated since flush
+ * @batch_cache: Cache sleep and wake requests sent as batch
  */
 struct rpmh_ctrlr {
        struct rsc_drv *drv;
        struct list_head cache;
Quoted 9 lines

drivers/soc/qcom/rpmh.c struct rpmh_request { · line 92

Quoted 3 lines
drivers/soc/qcom/rpmh.c +1 -0
        spinlock_t lock;
        bool dirty;
+       const struct rpmh_request *batch_cache[RPMH_MAX_BATCH_CACHE];

I'm pretty confused about why the "batch_cache" is separate from the normal cache. As far as I can tell the purpose of the two is the same but you have two totally separate code paths and data structures.

Quoted 4 lines

Due to a hardware limitation, requests made by bus drivers must be set up in the sleep and wake TCS first before setting up the requests from other drivers. Bus drivers use batch mode for any and all RPMH communication. Hence their request are the only ones in the batch_cache.

This is totally not obvious and not commented. Why not rename to "priority" instead of "batch"?

Quoted 8 lines

If the only requirement is that these come first, that's still no reason to use totally separate code paths and mechanisms. These requests can still use the same data structures / functions and just be ordered to come first, can't they? ...or given a boolean "priority" and you can do two passes through your queue: one to do the priority ones and one to do the secondary ones.

The bus requests have a certain order and cannot be mutated by the RPMH driver. It has to be maintained in the TCS in the same order. Also, the bus requests have quite a churn during the course of an usecase. They are setup and invalidated often.
It is faster to have them separate and invalidate the whole lot of the batch_cache instead of intertwining them with requests from other drivers and then figuring out which all must be invalidated and rebuilt when the next batch requests comes in. Remember, that requests may come from any driver any time and therefore will be mangled if we use the same data structure. So to be faster and to avoid having mangled requests in the TCS, I have kept the data structures separate.

Quoted 19 lines
Quoted 14 lines
Quoted 6 lines

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 370

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+       return ret;
+}
Quoted 8 lines

As part of my overall confusion about why the batch cache is different than the normal one: for the normal use case you still call rpmh_rsc_write_ctrl_data() for things you put in your cache, but you don't for the batch cache. I still haven't totally figured out what rpmh_rsc_write_ctrl_data() does, but it seems strange that you don't do it for the batch cache but you do for the other one.

Quoted 2 lines

flush_batch does write to the controller using
rpmh_rsc_write_ctrl_data()

My confusion is that they happen at different times. As I understand it:

Quoted 9 lines

* For the normal case, you immediately calls rpmh_rsc_write_ctrl_data() and then later do the rest of the work.

* For the batch case, you call both later.

Is there a good reason for this, or is it just an arbitrary difference? If there's a good reason, it should be documented.

In both the cases, the requests are cached in the rpmh library and are only sent to the controller only when the flushed. I am not sure the work is any different. The rpmh_flush() flushes out batch requests and then the requests from other drivers.

Thanks,
Lina

#31 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

Hi,

Quoted 43 lines · Lina Iyer
Quoted 34 lines · Doug Anderson

Hi,

Quoted 20 lines · Lina Iyer
Quoted 11 lines
Quoted 11 lines

/**

drivers/soc/qcom/rpmh.c +1 -0
@@ -77,12 +82,14 @@ struct rpmh_request {
  * @cache: the list of cached requests
  * @lock: synchronize access to the controller data
  * @dirty: was the cache updated since flush
+ * @batch_cache: Cache sleep and wake requests sent as batch
  */
 struct rpmh_ctrlr {
        struct rsc_drv *drv;
        struct list_head cache;
        spinlock_t lock;
Quoted 9 lines

drivers/soc/qcom/rpmh.c struct rpmh_request { · line 92

Quoted 2 lines
drivers/soc/qcom/rpmh.c +1 -0
        bool dirty;
+       const struct rpmh_request *batch_cache[RPMH_MAX_BATCH_CACHE];

I'm pretty confused about why the "batch_cache" is separate from the normal cache. As far as I can tell the purpose of the two is the same but you have two totally separate code paths and data structures.

Quoted 4 lines

Due to a hardware limitation, requests made by bus drivers must be set up in the sleep and wake TCS first before setting up the requests from other drivers. Bus drivers use batch mode for any and all RPMH communication. Hence their request are the only ones in the batch_cache.

This is totally not obvious and not commented. Why not rename to "priority" instead of "batch"?

Quoted 8 lines

If the only requirement is that these come first, that's still no reason to use totally separate code paths and mechanisms. These requests can still use the same data structures / functions and just be ordered to come first, can't they? ...or given a boolean "priority" and you can do two passes through your queue: one to do the priority ones and one to do the secondary ones.

Quoted 2 lines

The bus requests have a certain order and cannot be mutated by the RPMH driver. It has to be maintained in the TCS in the same order.

Please document this requirement in the code.

Quoted 9 lines

Also, the bus requests have quite a churn during the course of an usecase. They are setup and invalidated often.
It is faster to have them separate and invalidate the whole lot of the batch_cache instead of intertwining them with requests from other drivers and then figuring out which all must be invalidated and rebuilt when the next batch requests comes in. Remember, that requests may come from any driver any time and therefore will be mangled if we use the same data structure. So to be faster and to avoid having mangled requests in the TCS, I have kept the data structures separate.

If you need to find a certain group of requests then can't you just tag them and it's easy to find them? I'm still not seeing the need for a whole separate code path and data structure.

Quoted 26 lines
Quoted 26 lines
Quoted 15 lines
Quoted 7 lines

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 370

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+       return ret;
+}
Quoted 8 lines

As part of my overall confusion about why the batch cache is different than the normal one: for the normal use case you still call rpmh_rsc_write_ctrl_data() for things you put in your cache, but you don't for the batch cache. I still haven't totally figured out what rpmh_rsc_write_ctrl_data() does, but it seems strange that you don't do it for the batch cache but you do for the other one.

Quoted 2 lines

flush_batch does write to the controller using
rpmh_rsc_write_ctrl_data()

My confusion is that they happen at different times. As I understand it:

* For the normal case, you immediately calls rpmh_rsc_write_ctrl_data() and then later do the rest of the work.

* For the batch case, you call both later.

Quoted 8 lines
Quoted 4 lines

Is there a good reason for this, or is it just an arbitrary difference? If there's a good reason, it should be documented.

In both the cases, the requests are cached in the rpmh library and are only sent to the controller only when the flushed. I am not sure the work is any different. The rpmh_flush() flushes out batch requests and then the requests from other drivers.

OK then, here are the code paths I see:

rpmh_write
=> __rpmh_write
=> cache_rpm_request()
=> (state != RPMH_ACTIVE_ONLY_STATE): rpmh_rsc_send_data()

rpmh_write_batch
=> (state != RPMH_ACTIVE_ONLY_STATE): cache_batch()
=> No call to rpmh_rsc_send_data()

Said another way:

* if you call rpmh_write() for something you're going to defer you will still call cache_rpm_request() _before_ rpmh_write() returns.

* if you call rpmh_write_batch() for something you're going to defer then you _don't_ call cache_rpm_request() before rpmh_write_batch() returns.

Do you find a fault in my analysis of the current code? If you see a fault then please point it out. If you don't see a fault, please say why the behaviors are different. I certainly understand that eventually you will call cache_rpm_request() for both cases. It's just that in one case the call happens right away and the other case it is deferred.

author
#32 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

Quoted 46 lines · Doug Anderson

Hi,

Quoted 43 lines · Lina Iyer
Quoted 42 lines · Doug Anderson

Hi,

Quoted 20 lines · Lina Iyer
Quoted 11 lines
Quoted 11 lines

/**

drivers/soc/qcom/rpmh.c +1 -0
@@ -77,12 +82,14 @@ struct rpmh_request {
  * @cache: the list of cached requests
  * @lock: synchronize access to the controller data
  * @dirty: was the cache updated since flush
+ * @batch_cache: Cache sleep and wake requests sent as batch
  */
 struct rpmh_ctrlr {
        struct rsc_drv *drv;
        struct list_head cache;
        spinlock_t lock;
Quoted 9 lines

drivers/soc/qcom/rpmh.c struct rpmh_request { · line 92

Quoted 2 lines
drivers/soc/qcom/rpmh.c +1 -0
        bool dirty;
+       const struct rpmh_request *batch_cache[RPMH_MAX_BATCH_CACHE];

I'm pretty confused about why the "batch_cache" is separate from the normal cache. As far as I can tell the purpose of the two is the same but you have two totally separate code paths and data structures.

Quoted 4 lines

Due to a hardware limitation, requests made by bus drivers must be set up in the sleep and wake TCS first before setting up the requests from other drivers. Bus drivers use batch mode for any and all RPMH communication. Hence their request are the only ones in the batch_cache.

This is totally not obvious and not commented. Why not rename to "priority" instead of "batch"?

If the only requirement is that these come first, that's still no reason to use totally separate code paths and mechanisms. These requests can still use the same data structures / functions and just be ordered to come first, can't they? ...or given a boolean "priority" and you can do two passes through your queue: one to do the priority ones and one to do the secondary ones.

Quoted 6 lines
Quoted 2 lines

The bus requests have a certain order and cannot be mutated by the RPMH driver. It has to be maintained in the TCS in the same order.

Please document this requirement in the code.

OK.

Quoted 10 lines
Quoted 9 lines

Also, the bus requests have quite a churn during the course of an usecase. They are setup and invalidated often.
It is faster to have them separate and invalidate the whole lot of the batch_cache instead of intertwining them with requests from other drivers and then figuring out which all must be invalidated and rebuilt when the next batch requests comes in. Remember, that requests may come from any driver any time and therefore will be mangled if we use the same data structure. So to be faster and to avoid having mangled requests in the TCS, I have kept the data structures separate.

Quoted 4 lines

If you need to find a certain group of requests then can't you just tag them and it's easy to find them? I'm still not seeing the need for a whole separate code path and data structure.

Could be done but it will be slower and involve a lot more code than separate data structures.

Quoted 57 lines
Quoted 26 lines
Quoted 26 lines
Quoted 15 lines
Quoted 7 lines

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 370

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+       return ret;
+}
Quoted 8 lines

As part of my overall confusion about why the batch cache is different than the normal one: for the normal use case you still call rpmh_rsc_write_ctrl_data() for things you put in your cache, but you don't for the batch cache. I still haven't totally figured out what rpmh_rsc_write_ctrl_data() does, but it seems strange that you don't do it for the batch cache but you do for the other one.

Quoted 2 lines

flush_batch does write to the controller using
rpmh_rsc_write_ctrl_data()

My confusion is that they happen at different times. As I understand it:

* For the normal case, you immediately calls rpmh_rsc_write_ctrl_data() and then later do the rest of the work.

* For the batch case, you call both later.

Quoted 8 lines
Quoted 4 lines

Is there a good reason for this, or is it just an arbitrary difference? If there's a good reason, it should be documented.

In both the cases, the requests are cached in the rpmh library and are only sent to the controller only when the flushed. I am not sure the work is any different. The rpmh_flush() flushes out batch requests and then the requests from other drivers.

OK then, here are the code paths I see:

rpmh_write
=> __rpmh_write
=> cache_rpm_request()
=> (state != RPMH_ACTIVE_ONLY_STATE): rpmh_rsc_send_data()

rpmh_write_batch
=> (state != RPMH_ACTIVE_ONLY_STATE): cache_batch()
=> No call to rpmh_rsc_send_data()

Said another way:

* if you call rpmh_write() for something you're going to defer you will still call cache_rpm_request() _before_ rpmh_write() returns.

* if you call rpmh_write_batch() for something you're going to defer then you _don't_ call cache_rpm_request() before rpmh_write_batch() returns.

Quoted 6 lines

Do you find a fault in my analysis of the current code? If you see a fault then please point it out. If you don't see a fault, please say why the behaviors are different. I certainly understand that eventually you will call cache_rpm_request() for both cases. It's just that in one case the call happens right away and the other case it is deferred.

True. I see where your confusion is. It is because of an optimization and our existential need for saving power at every opportunity.

For rpmh_write path -
The reason being for the disparity is that, we can vote for a system low power mode (modes where we flush the caches and put the entire silicon is a low power state) at any point when all the CPUs are idle. If a driver has updated its vote for a system resource, it needs to be updated in the cache and thats what cache_rpm_request() does. Its possible that we may enter a system low power mode immediately after that. The rpmh_flush() would be called by the idle driver and we would flush the cached values and enter the idle state. By writing immediately to the TCS, we avoid increasing the latency of entering an idle state.

For the rpmh_write_batch() path -
The Bus driver would invalidate the TCS and the batch_cache. The use case fills up the batch_cache with values as needed by the use case. While during the usecase, the CPUs can go idle and the idle drivers would call rpmh_flush(). At that time, we would write the batch_cache into the already invalidated TCSes and then write the rest of the cached requests from ->cache. We then enter the low power modes.

The optimization of writing the sleep/wake votes in the same context of the driver making the request, helps us avoid writing some extra registers in the critical idle path.

Hope this helps.

-- Lina

#33 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

Hi,

Quoted 11 lines · Lina Iyer
Quoted 11 lines
Quoted 9 lines

Also, the bus requests have quite a churn during the course of an usecase. They are setup and invalidated often.
It is faster to have them separate and invalidate the whole lot of the batch_cache instead of intertwining them with requests from other drivers and then figuring out which all must be invalidated and rebuilt when the next batch requests comes in. Remember, that requests may come from any driver any time and therefore will be mangled if we use the same data structure. So to be faster and to avoid having mangled requests in the TCS, I have kept the data structures separate.

Quoted 6 lines
Quoted 4 lines

If you need to find a certain group of requests then can't you just tag them and it's easy to find them? I'm still not seeing the need for a whole separate code path and data structure.

Could be done but it will be slower and involve a lot more code than separate data structures.

When you say "a lot more code", you mean that you'll have to write a lot more code or that each request will execute a lot more code? I would argue that very little code would need to be added (compared to your patch) if rpmh_write_batch() was implemented atop rpmh_write_async(). Even with extra tagging / prioritization it would be small. I could try to prototype it I guess.

I think you mean that it would _execute_ a lot more code and thus be slower. Is that right? Is your main argument here that statically pre-allocating 20 slots in an array is going to be a lot faster than kzalloc-ing 20 linked list nodes and chaining them together? If you're truly worried about the kzalloc calls being slow, I suppose you could always allocate them with a kmem_cache. ...but we're talking fewer than 20 kzalloc calls, right?

I'll also make an argument (with no data to back me up) that maintaining separate functions for handling the batch cases will slow down your kernel because your footprint in the icache will be bigger and you'll be more likely to kick something else out that actually needs to run fast.

Quoted 90 lines
Quoted 61 lines
Quoted 29 lines
Quoted 29 lines
Quoted 16 lines
Quoted 8 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+       spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+       return ret;
+}
Quoted 8 lines

As part of my overall confusion about why the batch cache is different than the normal one: for the normal use case you still call rpmh_rsc_write_ctrl_data() for things you put in your cache, but you don't for the batch cache. I still haven't totally figured out what rpmh_rsc_write_ctrl_data() does, but it seems strange that you don't do it for the batch cache but you do for the other one.

Quoted 2 lines

flush_batch does write to the controller using
rpmh_rsc_write_ctrl_data()

My confusion is that they happen at different times. As I understand it:

* For the normal case, you immediately calls rpmh_rsc_write_ctrl_data() and then later do the rest of the work.

* For the batch case, you call both later.

Quoted 8 lines
Quoted 4 lines

Is there a good reason for this, or is it just an arbitrary difference? If there's a good reason, it should be documented.

In both the cases, the requests are cached in the rpmh library and are only sent to the controller only when the flushed. I am not sure the work is any different. The rpmh_flush() flushes out batch requests and then the requests from other drivers.

OK then, here are the code paths I see:

rpmh_write
=> __rpmh_write
=> cache_rpm_request()
=> (state != RPMH_ACTIVE_ONLY_STATE): rpmh_rsc_send_data()

rpmh_write_batch
=> (state != RPMH_ACTIVE_ONLY_STATE): cache_batch()
=> No call to rpmh_rsc_send_data()

Said another way:

* if you call rpmh_write() for something you're going to defer you will still call cache_rpm_request() _before_ rpmh_write() returns.

* if you call rpmh_write_batch() for something you're going to defer then you _don't_ call cache_rpm_request() before rpmh_write_batch() returns.

Quoted 6 lines

Do you find a fault in my analysis of the current code? If you see a fault then please point it out. If you don't see a fault, please say why the behaviors are different. I certainly understand that eventually you will call cache_rpm_request() for both cases. It's just that in one case the call happens right away and the other case it is deferred.

True. I see where your confusion is. It is because of an optimization and our existential need for saving power at every opportunity.

For rpmh_write path -
The reason being for the disparity is that, we can vote for a system low power mode (modes where we flush the caches and put the entire silicon is a low power state) at any point when all the CPUs are idle. If a driver has updated its vote for a system resource, it needs to be updated in the cache and thats what cache_rpm_request() does. Its possible that we may enter a system low power mode immediately after that. The rpmh_flush() would be called by the idle driver and we would flush the cached values and enter the idle state. By writing immediately to the TCS, we avoid increasing the latency of entering an idle state.

For the rpmh_write_batch() path -
The Bus driver would invalidate the TCS and the batch_cache. The use case fills up the batch_cache with values as needed by the use case. While during the usecase, the CPUs can go idle and the idle drivers would call rpmh_flush(). At that time, we would write the batch_cache into the already invalidated TCSes and then write the rest of the cached requests from ->cache. We then enter the low power modes.

Quoted 3 lines

The optimization of writing the sleep/wake votes in the same context of the driver making the request, helps us avoid writing some extra registers in the critical idle path.

I don't think I followed all that, but I'll try to read deeper into RPMh. ...but one question: could the two paths be changed to be the same? AKA would it be bad to call rpmh_rsc_send_data() synchronously in rpmh_write_batch()?

If the two cases really need to behave differently then it should be documented in the code.

-Doug

#34 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

In reply to Doug Anderson

Hi,

will reply on points other than what Lina has responded.

Quoted 118 lines · Doug Anderson

Hi,

drivers/soc/qcom/rpmh.c struct rpmh_request { · line 92

Quoted 13 lines · Lina Iyer

/**

drivers/soc/qcom/rpmh.c +2 -0
@@ -77,12 +82,14 @@ struct rpmh_request {
   * @cache: the list of cached requests
   * @lock: synchronize access to the controller data
   * @dirty: was the cache updated since flush
+ * @batch_cache: Cache sleep and wake requests sent as batch
   */
  struct rpmh_ctrlr {
         struct rsc_drv *drv;
         struct list_head cache;
         spinlock_t lock;
         bool dirty;
+       const struct rpmh_request *batch_cache[RPMH_MAX_BATCH_CACHE];

I'm pretty confused about why the "batch_cache" is separate from the normal cache. As far as I can tell the purpose of the two is the same but you have two totally separate code paths and data structures.

Quoted 12 lines
drivers/soc/qcom/rpmh.c +1 -0
  };
 
  static struct rpmh_ctrlr rpmh_rsc[RPMH_MAX_CTRLR];
@@ -133,6 +140,7 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
         struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request,
                                                     msg);
         struct completion *compl = rpm_msg->completion;
+       atomic_t *wc = rpm_msg->wait_count;
 
         rpm_msg->err = r;
 

drivers/soc/qcom/rpmh.c void rpmh_tx_done(const struct tcs_request *msg, int r) · line 155

Quoted 7 lines
drivers/soc/qcom/rpmh.c +2 -2
@@ -143,8 +151,13 @@ void rpmh_tx_done(const struct tcs_request *msg, int r)
         kfree(rpm_msg->free);
 
         /* Signal the blocking thread we are done */
-       if (compl)
-               complete(compl);
+       if (!compl)
+               return;

The comment above this "if" block no longer applies to the line next to it after your patch. ...but below I suggest you get rid of "wait_count", so maybe this part of the patch will go away.

Quoted 9 lines
drivers/soc/qcom/rpmh.c +9 -0
+static int cache_batch(struct rpmh_ctrlr *ctrlr,
+                      struct rpmh_request **rpm_msg, int count)
+{
+       unsigned long flags;
+       int ret = 0;
+       int index = 0;
+       int i;
+
+       spin_lock_irqsave(&ctrlr->lock, flags);

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 366

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+       while (index < RPMH_MAX_BATCH_CACHE && ctrlr->batch_cache[index])
+               index++;
+       if (index + count >= RPMH_MAX_BATCH_CACHE) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       for (i = 0; i < count; i++)
+               ctrlr->batch_cache[index + i] = rpm_msg[i];
+fail:

Nit: this label is for both failure and normal exit, so call it "exit".

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 370

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       spin_unlock_irqrestore(&ctrlr->lock, flags);
+
+       return ret;
+}

As part of my overall confusion about why the batch cache is different than the normal one: for the normal use case you still call rpmh_rsc_write_ctrl_data() for things you put in your cache, but you don't for the batch cache. I still haven't totally figured out what rpmh_rsc_write_ctrl_data() does, but it seems strange that you don't do it for the batch cache but you do for the other one.

Quoted 35 lines
drivers/soc/qcom/rpmh.c +35 -0
+/**
+ * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the
+ * batch to finish.
+ *
+ * @dev: the device making the request
+ * @state: Active/sleep set
+ * @cmd: The payload data
+ * @n: The array of count of elements in each batch, 0 terminated.
+ *
+ * Write a request to the RSC controller without caching. If the request
+ * state is ACTIVE, then the requests are treated as completion request
+ * and sent to the controller immediately. The function waits until all the
+ * commands are complete. If the request was to SLEEP or WAKE_ONLY, then the
+ * request is sent as fire-n-forget and no ack is expected.
+ *
+ * May sleep. Do not call from atomic contexts for ACTIVE_ONLY requests.
+ */
+int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
+                    const struct tcs_cmd *cmd, u32 *n)
+{
+       struct rpmh_request *rpm_msg[RPMH_MAX_REQ_IN_BATCH] = { NULL };
+       DECLARE_COMPLETION_ONSTACK(compl);
+       atomic_t wait_count = ATOMIC_INIT(0);
+       struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+       int count = 0;
+       int ret, i;
+
+       if (IS_ERR(ctrlr) || !cmd || !n)
+               return -EINVAL;
+
+       while (n[count++] > 0)
+               ;
+       count--;
+       if (!count || count > RPMH_MAX_REQ_IN_BATCH)
+               return -EINVAL;
Quoted 8 lines

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 444

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+
+       for (i = 0; i < count; i++) {
+               rpm_msg[i] = __get_rpmh_msg_async(state, cmd, n[i]);
+               if (IS_ERR_OR_NULL(rpm_msg[i])) {

Just "IS_ERR". It's never NULL.
...also add a i-- somewhere in here or you're going to be kfree()ing your error value, aren't you?

Sure. Will make change in next patch.

Quoted 40 lines

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 454

Quoted 12 lines
drivers/soc/qcom/rpmh.c +10 -0
+                       ret = PTR_ERR(rpm_msg[i]);
+                       for (; i >= 0; i--)
+                               kfree(rpm_msg[i]->free);
+                       return ret;
+               }
+               cmd += n[i];
+       }
+
+       if (state != RPMH_ACTIVE_ONLY_STATE)
+               return cache_batch(ctrlr, rpm_msg, count);

Don't you need to free rpm_msg items in this case?

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+
+       atomic_set(&wait_count, count);
+
+       for (i = 0; i < count; i++) {

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 468

Quoted 10 lines
drivers/soc/qcom/rpmh.c +10 -0
+               rpm_msg[i]->completion = &compl;
+               rpm_msg[i]->wait_count = &wait_count;
+               ret = rpmh_rsc_send_data(ctrlr->drv, &rpm_msg[i]->msg);
+               if (ret) {
+                       int j;
+
+                       pr_err("Error(%d) sending RPMH message addr=%#x\n",
+                              ret, rpm_msg[i]->msg.cmds[0].addr);
+                       for (j = i; j < count; j++)
+                               rpmh_tx_done(&rpm_msg[j]->msg, ret);

You're just using rpmh_tx_done() to free memory? Note that you'll probably do your error handling in this function a favor if you rename __get_rpmh_msg_async() to __fill_rpmh_msg() and remove the memory allocation from there. Then you can do one big allocation of the whole array in rpmh_write_batch() and then you'll only have one free at the end...

Quoted 4 lines
Quoted 1 line

+ break;

"break" seems wrong here. You'll end up waiting for the completion, then I guess timing out, then returning -ETIMEDOUT?

As the loop above break runs for remaining count, completion will be notified so there will not be waiting.

Thanks,
Raju

Quoted 59 lines

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 473

Quoted 6 lines
drivers/soc/qcom/rpmh.c +4 -0
+               }
+       }
+
+       ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);

The "wait_count" abstraction is confusing and I believe it's not needed. I think you can remove it and change the above to this (untested) code:

time_left = RPMH_TIMEOUT_MS;

for (i = 0; i < count; i++) {
   time_left = wait_for_completion_timeout(&compl, time_left);
   if (!time_left)
     return -ETIMEDOUT;
}

...specifically completions are additive, so just wait "count" times and then the reader doesn't need to learn your new wait_count abstraction and try to reason about it.

...and, actually, I argue in other replies that this should't use a timeout, so even cleaner:

for (i = 0; i < count; i++)
wait_for_completion(&compl);

Once you do that, you can also get rid of the need to pre-count "n", so all your loops turn into:

for (i = 0; n[i]; i++)

I suppose you might want to get rid of "RPMH_MAX_REQ_IN_BATCH" and dynamically allocate your array too, but that seems sane. As per above it seems like you should just dynamically allocate a whole array of "struct rpmh_request" items at once anyway.

---

drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 477

Quoted 4 lines
drivers/soc/qcom/rpmh.c +4 -0
+       return (ret > 0) ? 0 : -ETIMEDOUT;
+
+}
+EXPORT_SYMBOL(rpmh_write_batch);

Perhaps an even simpler thing than taking all my advice above: can't you just add a optional completion to rpmh_write_async()? That would just be stuffed into rpm_msg.

Now your batch code would just be a bunch of calls to rpmh_write_async() with an equal number of wait_for_completion() calls at the end. Is there a reason that wouldn't work? You'd get rid of _a lot_ of code.

-Doug

#35 lore raw

Re: [PATCH v8 09/10] drivers: qcom: rpmh: add support for batch RPMH request

Hi,

Quoted 9 lines · Raju P L S S S N
Quoted 5 lines
Quoted 1 line

+ break;

"break" seems wrong here. You'll end up waiting for the completion, then I guess timing out, then returning -ETIMEDOUT?

As the loop above break runs for remaining count, completion will be notified so there will not be waiting.

Ah, I see. I missed that.

...oh, I guess you need to wait because any requests you might have made will try to signal your completion which is on the stack...

author
#36 lore raw

PATCH v8 10/10 drivers: qcom: rpmh-rsc: allow active requests from wake TCS

In reply to Lina Iyer

Some RSCs may only have sleep and wake TCS, i.e, there is no dedicated TCS for active mode request, but drivers may still want to make active requests from these RSCs. In such cases re-purpose the wake TCS to send active state requests.

The requirement for this is that the driver is aware that the wake TCS is being repurposed to send active request, hence the sleep and wake TCSes be invalidated before the active request is sent.

---
 drivers/soc/qcom/rpmh-rsc.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)
drivers/soc/qcom/rpmh-rsc.c +17 -1
index 68c25ebbbe09..369b9b3eedc5 100644
@@ -153,6 +153,7 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
 					 const struct tcs_request *msg)
 {
 	int type;
+	struct tcs_group *tcs;
 
 	switch (msg->state) {
 	case RPMH_ACTIVE_ONLY_STATE:
@@ -168,7 +169,22 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
 		return ERR_PTR(-EINVAL);
 	}
 
-	return get_tcs_of_type(drv, type);
+	/*
+	 * If we are making an active request on a RSC that does not have a
+	 * dedicated TCS for active state use, then re-purpose a wake TCS to
+	 * send active votes.
+	 * NOTE: The driver must be aware that this RSC does not have a
+	 * dedicated AMC, and therefore would invalidate the sleep and wake
+	 * TCSes before making an active state request.
+	 */
+	tcs = get_tcs_of_type(drv, type);
+	if (msg->state == RPMH_ACTIVE_ONLY_STATE && IS_ERR(tcs)) {
+		tcs = get_tcs_of_type(drv, WAKE_TCS);
+		if (!IS_ERR(tcs))
+			rpmh_rsc_invalidate(drv);
+	}
+
+	return tcs;
 }
 
 static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#37 lore raw

Re: [PATCH v8 10/10] drivers: qcom: rpmh-rsc: allow active requests from wake TCS

Hi,

Quoted 35 lines · Lina Iyer

Some RSCs may only have sleep and wake TCS, i.e, there is no dedicated TCS for active mode request, but drivers may still want to make active requests from these RSCs. In such cases re-purpose the wake TCS to send active state requests.

The requirement for this is that the driver is aware that the wake TCS is being repurposed to send active request, hence the sleep and wake TCSes be invalidated before the active request is sent.

---
 drivers/soc/qcom/rpmh-rsc.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)
drivers/soc/qcom/rpmh-rsc.c +4 -1
index 68c25ebbbe09..369b9b3eedc5 100644
@@ -153,6 +153,7 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
                                         const struct tcs_request *msg)
 {
        int type;
+       struct tcs_group *tcs;
 
        switch (msg->state) {
        case RPMH_ACTIVE_ONLY_STATE:
@@ -168,7 +169,22 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
                return ERR_PTR(-EINVAL);
        }
 
-       return get_tcs_of_type(drv, type);
+       /*
+        * If we are making an active request on a RSC that does not have a
+        * dedicated TCS for active state use, then re-purpose a wake TCS to

drivers/soc/qcom/rpmh-rsc.c static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, · line 184

Quoted 10 lines
drivers/soc/qcom/rpmh-rsc.c +10 -0
+        * send active votes.
+        * NOTE: The driver must be aware that this RSC does not have a
+        * dedicated AMC, and therefore would invalidate the sleep and wake
+        * TCSes before making an active state request.
+        */
+       tcs = get_tcs_of_type(drv, type);
+       if (msg->state == RPMH_ACTIVE_ONLY_STATE && IS_ERR(tcs)) {
+               tcs = get_tcs_of_type(drv, WAKE_TCS);
+               if (!IS_ERR(tcs))
+                       rpmh_rsc_invalidate(drv);

I noticed that rpmh_rsc_invalidate() can return -EAGAIN. Do you need to deal with that here?

-Doug

#38 lore raw

Re: [PATCH v8 10/10] drivers: qcom: rpmh-rsc: allow active requests from wake TCS

Hi,

Quoted 43 lines · Doug Anderson

Hi,

Quoted 40 lines · Lina Iyer

Some RSCs may only have sleep and wake TCS, i.e, there is no dedicated TCS for active mode request, but drivers may still want to make active requests from these RSCs. In such cases re-purpose the wake TCS to send active state requests.

The requirement for this is that the driver is aware that the wake TCS is being repurposed to send active request, hence the sleep and wake TCSes be invalidated before the active request is sent.

---
  drivers/soc/qcom/rpmh-rsc.c | 18 +++++++++++++++++-
  1 file changed, 17 insertions(+), 1 deletion(-)
drivers/soc/qcom/rpmh-rsc.c +9 -1
index 68c25ebbbe09..369b9b3eedc5 100644
@@ -153,6 +153,7 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
                                          const struct tcs_request *msg)
  {
         int type;
+       struct tcs_group *tcs;
 
         switch (msg->state) {
         case RPMH_ACTIVE_ONLY_STATE:
@@ -168,7 +169,22 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
                 return ERR_PTR(-EINVAL);
         }
 
-       return get_tcs_of_type(drv, type);
+       /*
+        * If we are making an active request on a RSC that does not have a
+        * dedicated TCS for active state use, then re-purpose a wake TCS to
+        * send active votes.
+        * NOTE: The driver must be aware that this RSC does not have a
+        * dedicated AMC, and therefore would invalidate the sleep and wake
+        * TCSes before making an active state request.
+        */
Quoted 8 lines

drivers/soc/qcom/rpmh-rsc.c static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, · line 184

Quoted 5 lines
drivers/soc/qcom/rpmh-rsc.c +5 -0
+       tcs = get_tcs_of_type(drv, type);
+       if (msg->state == RPMH_ACTIVE_ONLY_STATE && IS_ERR(tcs)) {
+               tcs = get_tcs_of_type(drv, WAKE_TCS);
+               if (!IS_ERR(tcs))
+                       rpmh_rsc_invalidate(drv);

I noticed that rpmh_rsc_invalidate() can return -EAGAIN. Do you need to deal with that here?

Yes. will add the check here.

Thanks,
Raju.

Quoted 4 lines

-Doug