drivers/qcom: add RPMH communication support
v6 cover drivers/qcom: add RPMH communication support
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
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 | 132 ++++ drivers/soc/qcom/Kconfig | 10 + drivers/soc/qcom/Makefile | 4 + drivers/soc/qcom/rpmh-internal.h | 83 +++ drivers/soc/qcom/rpmh-rsc.c | 674 ++++++++++++++++++ drivers/soc/qcom/rpmh.c | 578 +++++++++++++++ drivers/soc/qcom/trace-rpmh.h | 81 +++ include/dt-bindings/soc/qcom,rpmh-rsc.h | 14 + include/soc/qcom/rpmh.h | 51 ++ include/soc/qcom/tcs.h | 56 ++ 10 files changed, 1683 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
PATCH v6 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 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 | 477 ++++++++++++++++++++++++ include/dt-bindings/soc/qcom,rpmh-rsc.h | 14 + include/soc/qcom/tcs.h | 56 +++ 6 files changed, 627 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
@@ -55,6 +55,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
@@ -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 +477 -0
477 lines not on this page · raw message
include/dt-bindings/soc/qcom,rpmh-rsc.h new file +14 -0
@@ -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
PATCH v6 2/10 dt-bindings: introduce RPMH RSC bindings for Qualcomm SoCs
In reply to Lina IyerAdd 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 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 | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt new file +132 -0
@@ -0,0 +1,132 @@ +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@179e000 { + label = "apps_rsc"; + compatible = "qcom,rpmh-rsc"; + reg = <0x179c0000 0x10000>, <0x179d0000 0x10000>, + <0x179e0000 0x10000>, <0xd00 0>; + reg-names = "drv-0", "drv-1", "drv-2", "tcs-offset"; + interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; + 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>, <0x1c00 0>; + reg-names = "drv-0", "tcs-offset"; + interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>; + 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
Re: [PATCH v6 02/10] dt-bindings: introduce RPMH RSC bindings for Qualcomm SoCs
Hi,
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt line 99
Quoted 3 lines · Lina Iyer
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt new file +3
-0+TCS-OFFSET: 0xD00 + + apps_rsc: rsc@179e000 {
This label is wrong in two ways:
1. I believe it's supposed to be the first reg listed, so 179c0000
2. There's only 3 0's at the end.
Thus, it should be:
apps_rsc: rsc@179c0000
Quoted 12 lines
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt new file +12
-0+ label = "apps_rsc"; + compatible = "qcom,rpmh-rsc"; + reg = <0x179c0000 0x10000>, <0x179d0000 0x10000>, + <0x179e0000 0x10000>, <0xd00 0>; + reg-names = "drv-0", "drv-1", "drv-2", "tcs-offset"; + interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; + qcom,drv-id = <2>; + qcom,tcs-config = <SLEEP_TCS 3>, + <WAKE_TCS 3>, + <ACTIVE_TCS 2>, + <CONTROL_TCS 1>; + };
Re: [PATCH v6 02/10] dt-bindings: introduce RPMH RSC bindings for Qualcomm SoCs
Quoted 9 lines · Doug Anderson
Hi,
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt line 99
Quoted 3 lines · Lina Iyer
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt new file +3
-0+TCS-OFFSET: 0xD00 + + apps_rsc: rsc@179e000 {This label is wrong in two ways:
Quoted 8 lines
1. I believe it's supposed to be the first reg listed, so
179c00002. There's only 3 0's at the end.
Thus, it should be:
apps_rsc: rsc@179c0000
Ok. Will fix.
I did not hear back on this idea. Does this binding work for everyone else?
Thanks,
Lina
Quoted 12 lines
Quoted 12 lines
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt new file +12
-0+ label = "apps_rsc"; + compatible = "qcom,rpmh-rsc"; + reg = <0x179c0000 0x10000>, <0x179d0000 0x10000>, + <0x179e0000 0x10000>, <0xd00 0>; + reg-names = "drv-0", "drv-1", "drv-2", "tcs-offset"; + interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; + qcom,drv-id = <2>; + qcom,tcs-config = <SLEEP_TCS 3>, + <WAKE_TCS 3>, + <ACTIVE_TCS 2>, + <CONTROL_TCS 1>; + };
PATCH v6 3/10 drivers: qcom: rpmh-rsc: log RPMH requests in FTRACE
In reply to Lina IyerLog sent RPMH requests and interrupt responses in FTRACE.
---
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 | 10 ++++- drivers/soc/qcom/trace-rpmh.h | 81 +++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/qcom/trace-rpmh.h
drivers/soc/qcom/Makefile +1 -0
@@ -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 +9 -1
@@ -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
@@ -132,7 +135,7 @@ static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv, int m)static irqreturn_t tcs_tx_done (int irq,void *p) {struct rsc_drv *drv = p; -int m, i; +int m, i, err;unsigned long irq_status;const struct tcs_request *req;struct tcs_cmd *cmd;
@@ -140,6 +143,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p) irq_status =read_tcs_reg (drv,RSC_DRV_IRQ_STATUS ,0 ,0 );for_each_set_bit (m, &irq_status,BITS_PER_LONG ) { + err =0 ; req =get_req_from_tcs (drv, m);if (!req) {WARN_ON (1 );
@@ -156,8 +160,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, m, req, err); skip:/* Reclaim the TCS */ write_tcs_reg (drv,RSC_DRV_CMD_ENABLE , m,0 );
@@ -194,6 +201,7 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int m, int n,write_tcs_cmd (drv,RSC_DRV_CMD_MSGID , m, j, msgid);write_tcs_cmd (drv,RSC_DRV_CMD_ADDR , m, j, cmd->addr);write_tcs_cmd (drv,RSC_DRV_CMD_DATA , m, j, cmd->data); +trace_rpmh_send_msg (drv, m, j, msgid, cmd); }write_tcs_reg (drv,RSC_DRV_CMD_WAIT_FOR_CMPL , m, cmd_complete);
drivers/soc/qcom/trace-rpmh.h new file +81 -0
81 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
PATCH v6 4/10 drivers: qcom: rpmh: add RPMH helper functions
In reply to Lina IyerSending 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 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 | 168 +++++++++++++++++++++++++++++++ include/soc/qcom/rpmh.h | 25 +++++ 5 files changed, 210 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
@@ -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
@@ -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 2struct 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
@@ -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 m,int n) { return readl_relaxed(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * m +
@@ -172,6 +174,8 @@ static irqreturn_t tcs_tx_done(int irq, void *p)spin_lock (&drv->lock);clear_bit (m, drv->tcs_in_use);spin_unlock (&drv->lock); +if (req) +rpmh_tx_done (req, err); }return IRQ_HANDLED ;
@@ -462,6 +466,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 +168 -0
168 lines not on this page · raw message
include/soc/qcom/rpmh.h new file +25 -0
@@ -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
Re: [PATCH v6 04/10] drivers: qcom: rpmh: add RPMH helper functions
Hi Lina,
Quoted 66 lines · 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.Signed-off-by Lina Iyer <[email protected]>---
Changes in v6:
- replacerpmh_clientwith device
- inlinewait_for_tx_done()Changes in v4:
- use const structtcs_cmdin API
- remove wait count from this patch
- changed-EFAULTto-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 | 168 +++++++++++++++++++++++++++++++ include/soc/qcom/rpmh.h | 25 +++++ 5 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/qcom/rpmh.c create mode 100644 include/soc/qcom/rpmh.hdrivers/soc/qcom/Makefile +3
-1@@ -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.odrivers/soc/qcom/rpmh-internal.h +2
-0@@ -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 2struct 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 ];
drivers/soc/qcom/rpmh-internal.h struct rsc_drv { · line 73
Quoted 10 lines
drivers/soc/qcom/rpmh-internal.h +3
-0DECLARE_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);
~
nit: consider using a more expressive name, like status, rc, res or err.
<snip>
Quoted 13 lines
drivers/soc/qcom/rpmh-internal.h +13
-0+static struct rpmh_ctrlr *get_rpmh_ctrlr (const struct device *dev) +{ +int i; +struct rsc_drv *p, *drv =dev_get_drvdata (dev->parent); +struct rpmh_ctrlr *ctrlr =ERR_PTR (-EINVAL ); + +if (!drv) +return ctrlr; + +for (i =0 ; i <RPMH_MAX_CTRLR ; i++) { +if (rpmh_rsc[i].drv == drv) { + ctrlr = &rpmh_rsc[i]; +return ctrlr;
drivers/soc/qcom/rpmh-internal.h struct rsc_drv {
Quoted 10 lines
drivers/soc/qcom/rpmh-internal.h +10
-0+ } + } + +list_for_each_entry (p, &rsc_drv_list, list) { +if (drv == p) { +for (i =0 ; i <RPMH_MAX_CTRLR ; i++) { +if (!rpmh_rsc[i].drv) +break ; + } + rpmh_rsc[i].drv = drv;
There is a race condition, get_rpmh_ctrlr() could be executed simulatenously in different contexts, and select the same controller instance for different DRVs.
It's probably an unlikely case, but to be safe you'd have to do something like this:
retry:
for (i = 0; i < RPMH_MAX_CTRLR; i++) {
if (!rpmh_rsc[i].drv)
break;
}spin_lock(&rpmh_rsc[i].lock);
if (!rpmh_rsc[i].drv) {
rpmh_rsc[i].drv = drv;
ctrlr = &rpmh_rsc[i];
} else {
spin_unlock(&rpmh_rsc[i].lock);
goto retry;
}spin_unlock(&rpmh_rsc[i].lock);
The above code doesn't address another potential error case, where #DRV > RPMH_MAX_CTRLR. In this case we'd access memory beyond rpmh_rsc. This would be a configuration error, not sure if it's strictly necessary to handle it.
<snip>
Quoted 4 lines
+/**
+ *rpmh_write: Write a set of RPMH commands and block until response
+ *
+ * @rc: The RPMh handle got fromrpmh_get_client
~~~~
nit: Other than this the driver consistently uses 'RPMH'
Matthias
Re: [PATCH v6 04/10] drivers: qcom: rpmh: add RPMH helper functions
Quoted 76 lines · Matthias Kaehlcke
Hi Lina,
Quoted 73 lines · 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.Signed-off-by Lina Iyer <[email protected]>---
Changes in v6:
- replacerpmh_clientwith device
- inlinewait_for_tx_done()Changes in v4:
- use const structtcs_cmdin API
- remove wait count from this patch
- changed-EFAULTto-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 | 168 +++++++++++++++++++++++++++++++ include/soc/qcom/rpmh.h | 25 +++++ 5 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/qcom/rpmh.c create mode 100644 include/soc/qcom/rpmh.hdrivers/soc/qcom/Makefile +3
-1@@ -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.odrivers/soc/qcom/rpmh-internal.h +4
-0@@ -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 2struct 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;
Quoted 8 lines
drivers/soc/qcom/rpmh-internal.h struct rsc_drv { · line 73
Quoted 3 lines
drivers/soc/qcom/rpmh-internal.h +1
-0int rpmh_rsc_send_data (struct rsc_drv *drv,const struct tcs_request *msg); +void rpmh_tx_done (const struct tcs_request *msg,int r);~nit: consider using a more expressive name, like status, rc, res or err.
OK. Will fix.
Quoted 21 lines
<snip>
Quoted 19 lines
drivers/soc/qcom/rpmh-internal.h +19
-0+static struct rpmh_ctrlr *get_rpmh_ctrlr (const struct device *dev) +{ +int i; +struct rsc_drv *p, *drv =dev_get_drvdata (dev->parent); +struct rpmh_ctrlr *ctrlr =ERR_PTR (-EINVAL ); + +if (!drv) +return ctrlr; + +for (i =0 ; i <RPMH_MAX_CTRLR ; i++) { +if (rpmh_rsc[i].drv == drv) { + ctrlr = &rpmh_rsc[i]; +return ctrlr; + } + } + +list_for_each_entry (p, &rsc_drv_list, list) { +if (drv == p) { +for (i =0 ; i <RPMH_MAX_CTRLR ; i++) {
Quoted 9 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h +4
-0+if (!rpmh_rsc[i].drv) +break ; + } + rpmh_rsc[i].drv = drv;There is a race condition,
get_rpmh_ctrlr()could be executed simulatenously in different contexts, and select the same controller instance for different DRVs.
True. This will need to be serialized.
Quoted 19 lines
It's probably an unlikely case, but to be safe you'd have to do something like this:
retry:
for (i = 0; i < RPMH_MAX_CTRLR; i++) { if (!rpmh_rsc[i].drv) break; }
spin_lock(&rpmh_rsc[i].lock);if (!rpmh_rsc[i].drv) { rpmh_rsc[i].drv = drv; ctrlr = &rpmh_rsc[i]; } else { spin_unlock(&rpmh_rsc[i].lock); goto retry; }
Quoted 8 lines
spin_unlock(&rpmh_rsc[i].lock);The above code doesn't address another potential error case, where #DRV >
RPMH_MAX_CTRLR. In this case we'd access memory beyondrpmh_rsc. This would be a configuration error, not sure if it's strictly necessary to handle it.
I think I might be able to solve for this a bit simpler with another spinlock for the rpmh_rsc.
Quoted 10 lines
<snip>
Quoted 4 lines
+/**
+ *rpmh_write: Write a set of RPMH commands and block until response
+ *
+ * @rc: The RPMh handle got fromrpmh_get_client~~~~nit: Other than this the driver consistently uses 'RPMH'
OK.
Thanks for the review Matthias.
-- Lina
PATCH v6 5/10 drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
In reply to Lina IyerSleep 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-internal.h | 8 +++ drivers/soc/qcom/rpmh-rsc.c | 120 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+)
drivers/soc/qcom/rpmh-internal.h +8 -0
@@ -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 2struct 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 +120 -0
138 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
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Quoted 23 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.
Signed-off-by Lina Iyer <[email protected]>Reviewed-by Evan Green <[email protected]>--- drivers/soc/qcom/rpmh-internal.h | 8 +++ drivers/soc/qcom/rpmh-rsc.c | 120 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+)drivers/soc/qcom/rpmh-internal.h +0
-0
<snip>
drivers/soc/qcom/rpmh-internal.h
Quoted 9 lines
drivers/soc/qcom/rpmh-internal.h +9
-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 ) { +for (j =0 ; j < len; j++) { +if (tcs->cmd_cache[i] != cmd[0 ].addr) {
Shouldn't the condition be 'tcs->cmd_cache[i + j] != cmd[j].addr'?
Otherwise the code below the following if branch will never be executed. Either the 'tcs->cmd_cache[i] != cmd[0].addr' branch isn't entered because the addresses match, or the addresses don't match and the inner loop is aborted after the first iteration.
Quoted 13 lines
drivers/soc/qcom/rpmh-internal.h +13
-0+if (j ==0 ) +break ; +WARN (tcs->cmd_cache[i + j] != cmd[j].addr, +"Message does not match previous sequence.\n" ); +return -EINVAL ; + }else if (j == len -1 ) { +return i; + } + } + } + +return -ENODATA ; +}
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Quoted 27 lines · Matthias Kaehlcke
Quoted 23 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.
Signed-off-by Lina Iyer <[email protected]>Reviewed-by Evan Green <[email protected]>--- drivers/soc/qcom/rpmh-internal.h | 8 +++ drivers/soc/qcom/rpmh-rsc.c | 120 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+)drivers/soc/qcom/rpmh-internal.h +0
-0<snip>
Quoted 12 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 9 lines
drivers/soc/qcom/rpmh-internal.h +9
-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 ) { +for (j =0 ; j < len; j++) { +if (tcs->cmd_cache[i] != cmd[0 ].addr) {Shouldn't the condition be 'tcs->cmd_cache[i + j] != cmd[j].addr'?
Here, we are trying to find the first address from the request and its position 'i' in the cmd_cache.
Quoted 9 lines
Otherwise the code below the following if branch will never be executed. Either the 'tcs->cmd_cache[i] != cmd[0].addr' branch isn't entered because the addresses match, or the addresses don't match and the inner loop is aborted after the first iteration.
drivers/soc/qcom/rpmh-internal.h
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h +4
-0+if (j ==0 ) +break ; +WARN (tcs->cmd_cache[i + j] != cmd[j].addr, +"Message does not match previous sequence.\n" );
We now check for the sequence using the iterator 'j' only after we have found 'i' (the beginning of our request).
I hope that helps clear the concern.
-- Lina
Quoted 9 lines
Quoted 9 lines
drivers/soc/qcom/rpmh-internal.h +9
-0+return -EINVAL ; + }else if (j == len -1 ) { +return i; + } + } + } + +return -ENODATA ; +}
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Quoted 48 lines · Lina Iyer
Quoted 27 lines · Matthias Kaehlcke
Quoted 23 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.
Signed-off-by Lina Iyer <[email protected]>Reviewed-by Evan Green <[email protected]>--- drivers/soc/qcom/rpmh-internal.h | 8 +++ drivers/soc/qcom/rpmh-rsc.c | 120 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+)drivers/soc/qcom/rpmh-internal.h +0
-0<snip>
Quoted 12 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 9 lines
drivers/soc/qcom/rpmh-internal.h +9
-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 ) { +for (j =0 ; j < len; j++) { +if (tcs->cmd_cache[i] != cmd[0 ].addr) {Shouldn't the condition be 'tcs->cmd_cache[i + j] != cmd[j].addr'?
Here, we are trying to find the first address from the request and its position 'i' in the
cmd_cache.Quoted 5 lines
Otherwise the code below the following if branch will never be executed. Either the 'tcs->cmd_cache[i] != cmd[0].addr' branch isn't entered because the addresses match, or the addresses don't match and the inner loop is aborted after the first iteration.
Quoted 8 lines
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h +4
-0+if (j ==0 ) +break ; +WARN (tcs->cmd_cache[i + j] != cmd[j].addr, +"Message does not match previous sequence.\n" );We now check for the sequence using the iterator 'j' only after we have found 'i' (the beginning of our request).
I hope that helps clear the concern.
It doesn't, maybe I'm just confused, the driver has a certain complexity and I don't claim to have a comprehensive understanding :)
If I understand correctly find_match() is used to find a sequence of commands of length 'len' in the command cache. If that is correct I would expect it to do the following:
1. iterate through the commands in the command cache and find a command that matches the first command in the sequence
2. verify that the (len - 1) subsequent commands match those in the sequence, otherwise bail out
If I'm not mistaken the current version of find_match() only checks that the first command exists. After that it happily increases the command index, but doesn't perform any checks (after finding the first command 'tcs->cmd_cache[i] != cmd[0].addr' remains false for the subsequent values of j). When j reaches (len - 1) the function returns the index of the first command in the cache, regardless of whether the other commands match or not.
Please correct me if I'm just confused, I think the negative logic of 'tcs->cmd_cache[i] != cmd[0].addr' doesn't help the readability of the code.
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Quoted 71 lines · Matthias Kaehlcke
Quoted 48 lines · Lina Iyer
Quoted 27 lines · Matthias Kaehlcke
Quoted 23 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.
Signed-off-by Lina Iyer <[email protected]>Reviewed-by Evan Green <[email protected]>--- drivers/soc/qcom/rpmh-internal.h | 8 +++ drivers/soc/qcom/rpmh-rsc.c | 120 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+)drivers/soc/qcom/rpmh-internal.h +0
-0<snip>
Quoted 12 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 9 lines
drivers/soc/qcom/rpmh-internal.h +9
-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 ) { +for (j =0 ; j < len; j++) { +if (tcs->cmd_cache[i] != cmd[0 ].addr) {Shouldn't the condition be 'tcs->cmd_cache[i + j] != cmd[j].addr'?
Here, we are trying to find the first address from the request and its position 'i' in the
cmd_cache.Quoted 5 lines
Otherwise the code below the following if branch will never be executed. Either the 'tcs->cmd_cache[i] != cmd[0].addr' branch isn't entered because the addresses match, or the addresses don't match and the inner loop is aborted after the first iteration.
Quoted 8 lines
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h +4
-0+if (j ==0 ) +break ; +WARN (tcs->cmd_cache[i + j] != cmd[j].addr, +"Message does not match previous sequence.\n" );We now check for the sequence using the iterator 'j' only after we have found 'i' (the beginning of our request).
I hope that helps clear the concern.
It doesn't, maybe I'm just confused, the driver has a certain complexity and I don't claim to have a comprehensive understanding :)
If I understand correctly
find_match()is used to find a sequence of commands of length 'len' in the command cache. If that is correct I would expect it to do the following:1. iterate through the commands in the command cache and find a command that matches the first command in the sequence
2. verify that the (len - 1) subsequent commands match those in the sequence, otherwise bail out
Quoted 8 lines
If I'm not mistaken the current version of
find_match()only checks that the first command exists. After that it happily increases the command index, but doesn't perform any checks (after finding the first command 'tcs->cmd_cache[i] != cmd[0].addr' remains false for the subsequent values of j). When j reaches (len - 1) the function returns the index of the first command in the cache, regardless of whether the other commands match or not.
Did you miss the check inside the WARN?
WARN(tcs->cmd_cache[i + j] != cmd[j].addr,
--Lina
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Quoted 80 lines · Lina Iyer
Quoted 71 lines · Matthias Kaehlcke
Quoted 48 lines · Lina Iyer
Quoted 27 lines · Matthias Kaehlcke
Quoted 23 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.
Signed-off-by Lina Iyer <[email protected]>Reviewed-by Evan Green <[email protected]>--- drivers/soc/qcom/rpmh-internal.h | 8 +++ drivers/soc/qcom/rpmh-rsc.c | 120 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+)drivers/soc/qcom/rpmh-internal.h +0
-0<snip>
Quoted 12 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 9 lines
drivers/soc/qcom/rpmh-internal.h +9
-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 ) { +for (j =0 ; j < len; j++) { +if (tcs->cmd_cache[i] != cmd[0 ].addr) {Shouldn't the condition be 'tcs->cmd_cache[i + j] != cmd[j].addr'?
Here, we are trying to find the first address from the request and its position 'i' in the
cmd_cache.Quoted 5 lines
Otherwise the code below the following if branch will never be executed. Either the 'tcs->cmd_cache[i] != cmd[0].addr' branch isn't entered because the addresses match, or the addresses don't match and the inner loop is aborted after the first iteration.
Quoted 8 lines
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h +4
-0+if (j ==0 ) +break ; +WARN (tcs->cmd_cache[i + j] != cmd[j].addr, +"Message does not match previous sequence.\n" );We now check for the sequence using the iterator 'j' only after we have found 'i' (the beginning of our request).
I hope that helps clear the concern.
It doesn't, maybe I'm just confused, the driver has a certain complexity and I don't claim to have a comprehensive understanding :)
If I understand correctly
find_match()is used to find a sequence of commands of length 'len' in the command cache. If that is correct I would expect it to do the following:1. iterate through the commands in the command cache and find a command that matches the first command in the sequence
2. verify that the (len - 1) subsequent commands match those in the sequence, otherwise bail out
Quoted 8 lines
If I'm not mistaken the current version of
find_match()only checks that the first command exists. After that it happily increases the command index, but doesn't perform any checks (after finding the first command 'tcs->cmd_cache[i] != cmd[0].addr' remains false for the subsequent values of j). When j reaches (len - 1) the function returns the index of the first command in the cache, regardless of whether the other commands match or not.
Quoted 2 lines
Did you miss the check inside the WARN?
WARN(tcs->cmd_cache[i + j] != cmd[j].addr,
My point is that this code is never reached, also regardless of the condition, the branch would always return -EINVAL.
for (j = 0; j < len; j++) {
if (tcs->cmd_cache[i] != cmd[0].addr) {
if (j == 0)
break;
WARN(tcs->cmd_cache[i + j] != cmd[j].addr,
"Message does not match previous sequence.\n");
return -EINVAL;
} else if (j == len - 1) {
return i;
}
}Let's single step through this, assuming the sequence of len=3 is in the cache:
1. j=0
2. (tcs->cmd_cache[i] != cmd[0].addr): false
=> branch with WARN + EINVAL not executed (good, this is the first
command we are looking for)
3. (j == len - 1): false
4. j=1
5. (tcs->cmd_cache[i] != cmd[0].addr): false
=> branch with WARN + EINVAL not executed
6. (j == len - 1): false
7. j=2
8. (tcs->cmd_cache[i] != cmd[0].addr): false
=> branch with WARN + EINVAL not executed
9. (j == len - 1): true
=> return i
Am I getting something wrong here?
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Quoted 113 lines · Matthias Kaehlcke
Quoted 80 lines · Lina Iyer
Quoted 71 lines · Matthias Kaehlcke
Quoted 48 lines · Lina Iyer
Quoted 27 lines · Matthias Kaehlcke
Quoted 23 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.
Signed-off-by Lina Iyer <[email protected]>Reviewed-by Evan Green <[email protected]>--- drivers/soc/qcom/rpmh-internal.h | 8 +++ drivers/soc/qcom/rpmh-rsc.c | 120 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+)drivers/soc/qcom/rpmh-internal.h +0
-0<snip>
Quoted 12 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 9 lines
drivers/soc/qcom/rpmh-internal.h +9
-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 ) { +for (j =0 ; j < len; j++) { +if (tcs->cmd_cache[i] != cmd[0 ].addr) {Shouldn't the condition be 'tcs->cmd_cache[i + j] != cmd[j].addr'?
Here, we are trying to find the first address from the request and its position 'i' in the
cmd_cache.Quoted 5 lines
Otherwise the code below the following if branch will never be executed. Either the 'tcs->cmd_cache[i] != cmd[0].addr' branch isn't entered because the addresses match, or the addresses don't match and the inner loop is aborted after the first iteration.
Quoted 8 lines
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h +4
-0+if (j ==0 ) +break ; +WARN (tcs->cmd_cache[i + j] != cmd[j].addr, +"Message does not match previous sequence.\n" );We now check for the sequence using the iterator 'j' only after we have found 'i' (the beginning of our request).
I hope that helps clear the concern.
It doesn't, maybe I'm just confused, the driver has a certain complexity and I don't claim to have a comprehensive understanding :)
If I understand correctly
find_match()is used to find a sequence of commands of length 'len' in the command cache. If that is correct I would expect it to do the following:1. iterate through the commands in the command cache and find a command that matches the first command in the sequence
2. verify that the (len - 1) subsequent commands match those in the sequence, otherwise bail out
Quoted 8 lines
If I'm not mistaken the current version of
find_match()only checks that the first command exists. After that it happily increases the command index, but doesn't perform any checks (after finding the first command 'tcs->cmd_cache[i] != cmd[0].addr' remains false for the subsequent values of j). When j reaches (len - 1) the function returns the index of the first command in the cache, regardless of whether the other commands match or not.Quoted 2 lines
Did you miss the check inside the WARN?
WARN(tcs->cmd_cache[i + j] != cmd[j].addr,My point is that this code is never reached, also regardless of the condition, the branch would always return
-EINVAL.for (j = 0; j < len; j++) { if (tcs->cmd_cache[i] != cmd[0].addr) { if (j == 0) break; WARN(tcs->cmd_cache[i + j] != cmd[j].addr, "Message does not match previous sequence.\n"); return -EINVAL; } else if (j == len - 1) { return i; } }Let's single step through this, assuming the sequence of len=3 is in the cache:
1. j=0
2. (tcs->cmd_cache[i] != cmd[0].addr): false
=> branch with WARN + EINVAL not executed (good, this is the first
command we are looking for)
3. (j == len - 1): false4. j=1
5. (tcs->cmd_cache[i] != cmd[0].addr): false
=> branch with WARN + EINVAL not executed
6. (j == len - 1): false
Quoted 7 lines
7. j=2
8. (tcs->cmd_cache[i] != cmd[0].addr): false
=> branch with WARN + EINVAL not executed
9. (j == len - 1): true
=> return iAm I getting something wrong here?
The for_each_set_bit() should increment the 'i' and we would attempt to compare the first address in the request with the next command in the TCS cache. If they don't match we repeat the process again. If it does, then we loop through 'j' to find if the sequence matches.
Did I miss something?
-- Lina
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Quoted 120 lines · Lina Iyer
Quoted 119 lines · Matthias Kaehlcke
Quoted 80 lines · Lina Iyer
Quoted 71 lines · Matthias Kaehlcke
Quoted 48 lines · Lina Iyer
Quoted 27 lines · Matthias Kaehlcke
Quoted 23 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.
Signed-off-by Lina Iyer <[email protected]>Reviewed-by Evan Green <[email protected]>--- drivers/soc/qcom/rpmh-internal.h | 8 +++ drivers/soc/qcom/rpmh-rsc.c | 120 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+)drivers/soc/qcom/rpmh-internal.h +0
-0<snip>
Quoted 12 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 9 lines
drivers/soc/qcom/rpmh-internal.h +9
-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 ) { +for (j =0 ; j < len; j++) { +if (tcs->cmd_cache[i] != cmd[0 ].addr) {Shouldn't the condition be 'tcs->cmd_cache[i + j] != cmd[j].addr'?
Here, we are trying to find the first address from the request and its position 'i' in the
cmd_cache.Quoted 5 lines
Otherwise the code below the following if branch will never be executed. Either the 'tcs->cmd_cache[i] != cmd[0].addr' branch isn't entered because the addresses match, or the addresses don't match and the inner loop is aborted after the first iteration.
Quoted 8 lines
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h
Quoted 4 lines
drivers/soc/qcom/rpmh-internal.h +4
-0+if (j ==0 ) +break ; +WARN (tcs->cmd_cache[i + j] != cmd[j].addr, +"Message does not match previous sequence.\n" );We now check for the sequence using the iterator 'j' only after we have found 'i' (the beginning of our request).
I hope that helps clear the concern.
It doesn't, maybe I'm just confused, the driver has a certain complexity and I don't claim to have a comprehensive understanding :)
If I understand correctly
find_match()is used to find a sequence of commands of length 'len' in the command cache. If that is correct I would expect it to do the following:1. iterate through the commands in the command cache and find a command that matches the first command in the sequence
2. verify that the (len - 1) subsequent commands match those in the sequence, otherwise bail out
Quoted 8 lines
If I'm not mistaken the current version of
find_match()only checks that the first command exists. After that it happily increases the command index, but doesn't perform any checks (after finding the first command 'tcs->cmd_cache[i] != cmd[0].addr' remains false for the subsequent values of j). When j reaches (len - 1) the function returns the index of the first command in the cache, regardless of whether the other commands match or not.Quoted 2 lines
Did you miss the check inside the WARN?
WARN(tcs->cmd_cache[i + j] != cmd[j].addr,My point is that this code is never reached, also regardless of the condition, the branch would always return
-EINVAL.for (j = 0; j < len; j++) { if (tcs->cmd_cache[i] != cmd[0].addr) { if (j == 0) break; WARN(tcs->cmd_cache[i + j] != cmd[j].addr, "Message does not match previous sequence.\n"); return -EINVAL; } else if (j == len - 1) { return i; } }Let's single step through this, assuming the sequence of len=3 is in the cache:
1. j=0
2. (tcs->cmd_cache[i] != cmd[0].addr): false
=> branch with WARN + EINVAL not executed (good, this is the first
command we are looking for)
3. (j == len - 1): false4. j=1
5. (tcs->cmd_cache[i] != cmd[0].addr): false
=> branch with WARN + EINVAL not executed
6. (j == len - 1): false7. j=2
8. (tcs->cmd_cache[i] != cmd[0].addr): false
=> branch with WARN + EINVAL not executed
9. (j == len - 1): true
=> return i
Quoted 8 lines
Quoted 1 line
Am I getting something wrong here?
The
for_each_set_bit()should increment the 'i' and we would attempt to compare the first address in the request with the next command in the TCS cache. If they don't match we repeat the process again. If it does, then we loop through 'j' to find if the sequence matches.Did I miss something?
One of us is clearly in need of more caffeine or ready for the weekend, it might be me :) Maybe another pair of eyeballs could help to resolve this deadlock ...
My single stepping above assumes that tcs->cmd_cache[i] matches cmd[0].addr, i.e. we either found the start of the sequence we are looking for or another sequence that starts with the same address. My claim is that the code returns i in either case, whether the subsequent addresses match or not.
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Hi,
Quoted 13 lines · Matthias Kaehlcke
Quoted 8 lines
Quoted 1 line
Am I getting something wrong here?
The
for_each_set_bit()should increment the 'i' and we would attempt to compare the first address in the request with the next command in the TCS cache. If they don't match we repeat the process again. If it does, then we loop through 'j' to find if the sequence matches.Did I miss something?
One of us is clearly in need of more caffeine or ready for the weekend, it might be me :) Maybe another pair of eyeballs could help to resolve this deadlock ...
Quoted 5 lines
My single stepping above assumes that tcs->cmd_cache[i] matches cmd[0].addr, i.e. we either found the start of the sequence we are looking for or another sequence that starts with the same address. My claim is that the code returns i in either case, whether the subsequent addresses match or not.
I haven't reviewed this patch in detail, but I attempted to be another pair of eyes here. Something is definitely wrong with the "for (j = 0; j < len; j++)" loop. I believe the code that's written right now is equivalent to this much shorter function:
+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)
+ return i;
+ }
+
+ return -ENODATA;
+}
Specifically the test "if (tcs->cmd_cache[i] != cmd[0].addr)" does not take "j" into account. Thus if it was false when "j == 0" it will continue to be false for "j == 1", "j == 2", etc. Eventually you'll hit the "else if (j == len - 1)" and return.
I believe that's what Matthias has been saying. I personally haven't looked at the rest of the patch to see how things out to be fixed, but I'm quite convinced that the function either has a bug or should be written as the shorter version I've written above.
-Doug
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Quoted 14 lines · Doug Anderson
Hi,
Quoted 11 lines · Matthias Kaehlcke
Quoted 8 lines
Quoted 1 line
Am I getting something wrong here?
The
for_each_set_bit()should increment the 'i' and we would attempt to compare the first address in the request with the next command in the TCS cache. If they don't match we repeat the process again. If it does, then we loop through 'j' to find if the sequence matches.Did I miss something?
One of us is clearly in need of more caffeine or ready for the weekend, it might be me :) Maybe another pair of eyeballs could help
I need them both. Sorry about the back and forth. I understand what the problem is. The code doesnt look right. I seem to have messed it up. Thanks Matthias for being patient and going through this.
Quoted 32 lines
Quoted 7 lines
to resolve this deadlock ...
My single stepping above assumes that tcs->cmd_cache[i] matches cmd[0].addr, i.e. we either found the start of the sequence we are looking for or another sequence that starts with the same address. My claim is that the code returns i in either case, whether the subsequent addresses match or not.
I haven't reviewed this patch in detail, but I attempted to be another pair of eyes here. Something is definitely wrong with the "for (j = 0; j < len; j++)" loop. I believe the code that's written right now is equivalent to this much shorter function:
(quoted diff) +13
-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) + return i; + } + + return -ENODATA; +}Specifically the test "if (tcs->cmd_cache[i] != cmd[0].addr)" does not take "j" into account. Thus if it was false when "j == 0" it will continue to be false for "j == 1", "j == 2", etc. Eventually you'll hit the "else if (j == len - 1)" and return.
Quoted 5 lines
I believe that's what Matthias has been saying. I personally haven't looked at the rest of the patch to see how things out to be fixed, but I'm quite convinced that the function either has a bug or should be written as the shorter version I've written above.
Yes, this is incorrect in its current form. This is what it should be -
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;
for (j = 0; j < len; j++) {
WARN(tcs->cmd_cache[i + j] != cmd[j].addr,
"Message does not match previous sequence.\n");
return -EINVAL;
}
if (j == len - 1)
return i;
}
return -ENODATA;
}Thanks,
Lina
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Hi,
Quoted 5 lines · Lina Iyer
Yes, this is incorrect in its current form. This is what it should be -
static int find_match(const struct
tcs_group*tcs, const structtcs_cmd*cmd,
int len)
Quoted 12 lines
{
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;
for (j = 0; j < len; j++) {
WARN(tcs->cmd_cache[i + j] != cmd[j].addr,
"Message does not match previous sequence.\n");
return -EINVAL;
}If len > 0, won't the above always return -EINVAL?
Quoted 2 lines
if (j == len - 1)
return i;Care to explain how you could get here and the test "if (j == len - 1)" could be false? ;-P
-Doug
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
Quoted 21 lines · Doug Anderson
Hi,
Quoted 5 lines · Lina Iyer
Yes, this is incorrect in its current form. This is what it should be -
static int find_match(const struct
tcs_group*tcs, const structtcs_cmd*cmd,
int len)Quoted 12 lines
{ 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; for (j = 0; j < len; j++) { WARN(tcs->cmd_cache[i + j] != cmd[j].addr, "Message does not match previous sequence.\n"); return -EINVAL; }
Quoted 9 lines
If len > 0, won't the above always return
-EINVAL?Quoted 2 lines
if (j == len - 1) return i;Care to explain how you could get here and the test "if (j == len - 1)" could be false? ;-P
True. Will fix it in the next spin correctly.
--Lina
Re: [PATCH v6 05/10] drivers: qcom: rpmh-rsc: write sleep/wake requests to TCS
In reply to Lina IyerHi Lina,
Quoted 60 lines · Lina Iyer
Quoted 14 lines · Doug Anderson
Hi,
Quoted 11 lines · Matthias Kaehlcke
Quoted 8 lines
Quoted 1 line
Am I getting something wrong here?
The
for_each_set_bit()should increment the 'i' and we would attempt to compare the first address in the request with the next command in the TCS cache. If they don't match we repeat the process again. If it does, then we loop through 'j' to find if the sequence matches.Did I miss something?
One of us is clearly in need of more caffeine or ready for the weekend, it might be me :) Maybe another pair of eyeballs could help
I need them both. Sorry about the back and forth. I understand what the problem is. The code doesnt look right. I seem to have messed it up. Thanks Matthias for being patient and going through this.
Quoted 32 lines
Quoted 7 lines
to resolve this deadlock ...
My single stepping above assumes that tcs->cmd_cache[i] matches cmd[0].addr, i.e. we either found the start of the sequence we are looking for or another sequence that starts with the same address. My claim is that the code returns i in either case, whether the subsequent addresses match or not.
I haven't reviewed this patch in detail, but I attempted to be another pair of eyes here. Something is definitely wrong with the "for (j = 0; j < len; j++)" loop. I believe the code that's written right now is equivalent to this much shorter function:
(quoted diff) +13
-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) + return i; + } + + return -ENODATA; +}Specifically the test "if (tcs->cmd_cache[i] != cmd[0].addr)" does not take "j" into account. Thus if it was false when "j == 0" it will continue to be false for "j == 1", "j == 2", etc. Eventually you'll hit the "else if (j == len - 1)" and return.
Quoted 5 lines
I believe that's what Matthias has been saying. I personally haven't looked at the rest of the patch to see how things out to be fixed, but I'm quite convinced that the function either has a bug or should be written as the shorter version I've written above.
Yes, this is incorrect in its current form. This is what it should be -
static int find_match(const struct
tcs_group*tcs, const structtcs_cmd*cmd,
int len)
Quoted 7 lines
{
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;This looks better.
Quoted 5 lines
for (j = 0; j < len; j++) {
WARN(tcs->cmd_cache[i + j] != cmd[j].addr,
"Message does not match previous sequence.\n");
return -EINVAL;
}However this will return -EINVAL for any message in the first iteration.
Quoted 3 lines
if (j == len - 1)
return i;
}You can just return 'i' here, 'j' will always be equals to 'len' (not 'len - 1') when this point is reached.
I think you want something like this:
for (j = 0; j < len; j++) {
if (tcs->cmd_cache[i + j] != cmd[j].addr) {
pr_warn("Message does not match previous sequence.\n");
return -EINVAL;
}}
return i;
Before entering the loop you also have to verify that 'i + (len - 1)' doesn't exceed 'tcs->cmd_cache'.
PATCH v6 6/10 drivers: qcom: rpmh-rsc: allow invalidation of sleep/wake TCS
In reply to Lina IyerAllow sleep and wake commands to be cleared from the respective TCSes, so that they can be re-populated.
---
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
@@ -101,6 +101,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
Re: [PATCH v6 06/10] drivers: qcom: rpmh-rsc: allow invalidation of sleep/wake TCS
Quoted 36 lines · Lina Iyer
Allow sleep and wake commands to be cleared from the respective TCSes, so that they can be re-populated.
Signed-off-by Lina Iyer <[email protected]>---
Changes in v6:
- remove unnecessary locks around__tcs_invalidate
- rename function totcs_invaldiateChanges in v4:
- refactored therphm_rsc_invalidate()--- drivers/soc/qcom/rpmh-rsc.c | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)drivers/soc/qcom/rpmh-rsc.c +12
-0@@ -101,6 +101,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);
drivers/soc/qcom/rpmh-rsc.c static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type) · line 125
Quoted 10 lines
drivers/soc/qcom/rpmh-rsc.c +10
-0+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 );
Shouldn't this only clear the slots corresponding to 'm'?
If resetting the entire bitmap is really intended it could be done once after the loop, though the bitmap might be left in an inconsistent state if a TCS is still in use.
Re: [PATCH v6 06/10] drivers: qcom: rpmh-rsc: allow invalidation of sleep/wake TCS
Quoted 50 lines · Matthias Kaehlcke
Quoted 36 lines · Lina Iyer
Allow sleep and wake commands to be cleared from the respective TCSes, so that they can be re-populated.
Signed-off-by Lina Iyer <[email protected]>---
Changes in v6:
- remove unnecessary locks around__tcs_invalidate
- rename function totcs_invaldiateChanges in v4:
- refactored therphm_rsc_invalidate()--- drivers/soc/qcom/rpmh-rsc.c | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)drivers/soc/qcom/rpmh-rsc.c +12
-0@@ -101,6 +101,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);drivers/soc/qcom/rpmh-rsc.c static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type) · line 125
Quoted 10 lines
drivers/soc/qcom/rpmh-rsc.c +10
-0+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 );Shouldn't this only clear the slots corresponding to 'm'?
Quoted 3 lines
If resetting the entire bitmap is really intended it could be done once after the loop, though the bitmap might be left in an inconsistent state if a TCS is still in use.
The TCS will not be in use, the check tcs_is_free() will ensure that. I will move the bitmap_zero() outside the for().
Thanks,
Lina
Re: [PATCH v6 06/10] drivers: qcom: rpmh-rsc: allow invalidation of sleep/wake TCS
Quoted 51 lines · Lina Iyer
Quoted 50 lines · Matthias Kaehlcke
Quoted 36 lines · Lina Iyer
Allow sleep and wake commands to be cleared from the respective TCSes, so that they can be re-populated.
Signed-off-by Lina Iyer <[email protected]>---
Changes in v6:
- remove unnecessary locks around__tcs_invalidate
- rename function totcs_invaldiateChanges in v4:
- refactored therphm_rsc_invalidate()--- drivers/soc/qcom/rpmh-rsc.c | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)drivers/soc/qcom/rpmh-rsc.c +12
-0@@ -101,6 +101,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);drivers/soc/qcom/rpmh-rsc.c static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type) · line 125
Quoted 10 lines
drivers/soc/qcom/rpmh-rsc.c +10
-0+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 );Shouldn't this only clear the slots corresponding to 'm'?
Quoted 5 lines
Quoted 3 lines
If resetting the entire bitmap is really intended it could be done once after the loop, though the bitmap might be left in an inconsistent state if a TCS is still in use.
The TCS will not be in use, the check
tcs_is_free()will ensure that. I will move thebitmap_zero()outside thefor().
Sure, but it could occur that one or more TCSes are disabled, then tcs_is_free() returns false for the next TCS and the function returns without having updated tcs->slots for the TCSes that have been disabled.
PATCH v6 7/10 drivers: qcom: rpmh: cache sleep/wake state requests
In reply to Lina IyerActive 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
@@ -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 ];
@@ -84,6 +107,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 ; }
@@ -110,29 +135,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); + } -WARN_ON (irqs_disabled ()); + rpm_msg->msg.state = state; + +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; }/**
@@ -166,3 +271,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
@@ -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
PATCH v6 8/10 drivers: qcom: rpmh: allow requests to be sent asynchronously
In reply to Lina IyerPlatform 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
@@ -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; };/**
@@ -129,6 +132,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);
@@ -240,6 +245,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
@@ -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
PATCH v6 9/10 drivers: qcom: rpmh: add support for batch RPMH request
In reply to Lina IyerPlatform 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 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 | 155 +++++++++++++++++++++++++++++++++++++++- include/soc/qcom/rpmh.h | 8 +++ 2 files changed, 161 insertions(+), 2 deletions(-)
drivers/soc/qcom/rpmh.c +153 -2
@@ -21,6 +21,7 @@#include "rpmh-internal.h" #define RPMH_TIMEOUT_MS msecs_to_jiffies(10000) +#define RPMH_MAX_REQ_IN_BATCH 10#define DEFINE_RPMH_MSG_ONSTACK (dev, s, q, name) \ struct rpmh_request name = { \
@@ -34,6 +35,7 @@ .completion = q, \ .dev = dev, \ .free = NULL, \ + .wait_count = NULL, \ }/**
@@ -60,6 +62,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 +71,7 @@ struct rpmh_request {const struct device *dev;int err;struct rpmh_request *free; +atomic_t *wait_count; };/**
@@ -77,12 +81,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[2 *RPMH_MAX_REQ_IN_BATCH ]; };static struct rpmh_ctrlr rpmh_rsc[RPMH_MAX_CTRLR ];
@@ -125,6 +131,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;
@@ -135,8 +142,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);
@@ -324,6 +336,139 @@ 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 (ctrlr->batch_cache[index]) + index++; +if (index + count >=2 *RPMH_MAX_REQ_IN_BATCH ) { + 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 ; +int i; + +spin_lock_irqsave (&ctrlr->lock, flags); +while (ctrlr->batch_cache[index]) + index++; +for (i =0 ; i < index; i++) { +kfree (ctrlr->batch_cache[i]->free); + ctrlr->batch_cache[i] =NULL ; + } +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 &&
@@ -375,6 +520,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.
@@ -416,6 +566,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
@@ -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
Re: [PATCH v6 09/10] drivers: qcom: rpmh: add support for batch RPMH request
Quoted 74 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 theirtx_donecallbacks.Signed-off-by Lina Iyer <[email protected]>---
Changes in v6:
- replacerpmh_clientwith device *
Changes in v4:
- reorganizerpmh_write_batch()
- introducewait_counthere, instead of patch#4--- drivers/soc/qcom/rpmh.c | 155 +++++++++++++++++++++++++++++++++++++++- include/soc/qcom/rpmh.h | 8 +++ 2 files changed, 161 insertions(+), 2 deletions(-)drivers/soc/qcom/rpmh.c +6
-0@@ -21,6 +21,7 @@#include "rpmh-internal.h" #define RPMH_TIMEOUT_MS msecs_to_jiffies(10000) +#define RPMH_MAX_REQ_IN_BATCH 10#define DEFINE_RPMH_MSG_ONSTACK (dev, s, q, name) \ struct rpmh_request name = { \@@ -34,6 +35,7 @@ .completion = q, \ .dev = dev, \ .free = NULL, \ + .wait_count = NULL, \ }/** @@ -60,6 +62,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 +71,7 @@ struct rpmh_request {const struct device *dev;int err;struct rpmh_request *free; +atomic_t *wait_count; };/** @@ -77,12 +81,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[2 *RPMH_MAX_REQ_IN_BATCH ]; };
<snip>
drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 349
Quoted 11 lines
drivers/soc/qcom/rpmh.c +11
-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); +while (ctrlr->batch_cache[index]) + index++;
This will access memory beyond 'batch_cache' when the cache is full.
<snip>
drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 391
Quoted 9 lines
drivers/soc/qcom/rpmh.c +9
-0+static void invalidate_batch (struct rpmh_ctrlr *ctrlr) +{ +unsigned long flags; +int index =0 ; +int i; + +spin_lock_irqsave (&ctrlr->lock, flags); +while (ctrlr->batch_cache[index]) + index++;
Same as above. Also, why loop twice?
Quoted 4 lines
drivers/soc/qcom/rpmh.c +4
-0+for (i =0 ; i < index; i++) { +kfree (ctrlr->batch_cache[i]->free); + ctrlr->batch_cache[i] =NULL ; + }
<snip>
Quoted 8 lines
+/**
+ *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.
nit: in this driver 'n' is usually associated with the command offset within a TCS. Since it isn't an overly descriptive name it may already cost the reader a while to commit that to his/her memory, and now we are overloading 'n' with a different meaning (I also noticed this in another patch of this series, but didn't comment).
Quoted 68 lines
+ * 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 orWAKE_ONLY, then the
+ * request is sent as fire-n-forget and no ack is expected.
+ *
+ * May sleep. Do not call from atomic contexts forACTIVE_ONLYrequests.
+ */drivers/soc/qcom/rpmh.c +56
-0+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 &&
drivers/soc/qcom/rpmh.c int rpmh_flush(const struct device *dev) · line 529
Quoted 10 lines
drivers/soc/qcom/rpmh.c +5
-0@@ -375,6 +520,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,,
~
Remove extra comma.
Re: [PATCH v6 09/10] drivers: qcom: rpmh: add support for batch RPMH request
Quoted 28 lines · Matthias Kaehlcke
Quoted 24 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 theirtx_donecallbacks.Signed-off-by Lina Iyer <[email protected]>---
Changes in v6:
- replacerpmh_clientwith device *
Changes in v4:
- reorganizerpmh_write_batch()
- introducewait_counthere, instead of patch#4--- drivers/soc/qcom/rpmh.c | 155 +++++++++++++++++++++++++++++++++++++++- include/soc/qcom/rpmh.h | 8 +++ 2 files changed, 161 insertions(+), 2 deletions(-)<snip>
Quoted 14 lines
drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 349
Quoted 11 lines
drivers/soc/qcom/rpmh.c +11
-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); +while (ctrlr->batch_cache[index]) + index++;This will access memory beyond '
batch_cache' when the cache is full.
Ok. Will check for index.
Quoted 14 lines
<snip>
drivers/soc/qcom/rpmh.c int rpmh_write(const struct device *dev, enum rpmh_state state, · line 391
Quoted 9 lines
drivers/soc/qcom/rpmh.c +9
-0+static void invalidate_batch (struct rpmh_ctrlr *ctrlr) +{ +unsigned long flags; +int index =0 ; +int i; + +spin_lock_irqsave (&ctrlr->lock, flags); +while (ctrlr->batch_cache[index]) + index++;Same as above. Also, why loop twice?
Good idea. Will fix.
Quoted 16 lines
Quoted 4 lines
drivers/soc/qcom/rpmh.c +4
-0+for (i =0 ; i < index; i++) { +kfree (ctrlr->batch_cache[i]->free); + ctrlr->batch_cache[i] =NULL ; + }<snip>
Quoted 8 lines
+/**
+ *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.
Quoted 6 lines
nit: in this driver 'n' is usually associated with the command offset within a TCS. Since it isn't an overly descriptive name it may already cost the reader a while to commit that to his/her memory, and now we are overloading 'n' with a different meaning (I also noticed this in another patch of this series, but didn't comment).
Sure, will change the variable name here.
Quoted 4 lines
drivers/soc/qcom/rpmh.c int rpmh_flush(const struct device *dev) · line 529
Quoted 2 lines
drivers/soc/qcom/rpmh.c +0
-0/* * Nobody else should be calling this function other than system PM,, ~Remove extra comma.
OK.
Thanks,
Lina
PATCH v6 10/10 drivers: qcom: rpmh-rsc: allow active requests from wake TCS
In reply to Lina IyerSome 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
@@ -150,6 +150,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 :
@@ -165,7 +166,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,int m)
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH v6 10/10] drivers: qcom: rpmh-rsc: allow active requests from wake TCS
Quoted 10 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.
Signed-off-by Lina Iyer <[email protected]>