From nobody Mon May 25 01:15:21 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 305723AFD1C for ; Tue, 19 May 2026 19:05:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779217525; cv=none; b=XTHtIGT/kuD0M3JlXw3fPMDJkjHEAAzuaqAEqqap0eKD+HJ416vXpm6SwVoVFxx29lE7xq7YLuv++khswuqyUBCecxmhj+mh+YqXiSEJWqKiBKcb26f4QW3JQOY16XFeSnNygsOhPzpDljmOrSuOWfyFm1487UQzCIVnQdbiias= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779217525; c=relaxed/simple; bh=FswLjexa4W/19cOLQDCspqJYNyI5kjTklXut13h725o=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=aeOVlH5vaGHhV7mOjpgSlOuWTFzfBoplYb6mm79mR/ZVeJ4BbC3jIpMMRt8mPC1cjJWuoCNXUv8KaroY0PMb5HVLmT63d/vzhbiK+iSU15T4bCIv7qEilCxsAYOapGG0zOUN0oWYE+GuZ5jhvGIn17/uLaVhKSScpIo9vdlmHQE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=Q6b8drNk; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=XkyLd6MB; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="Q6b8drNk"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="XkyLd6MB" Received: from pps.filterd (m0279863.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 64JExHwQ1237896 for ; Tue, 19 May 2026 19:05:23 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= gHAPpvkbvRI2RnEglKIra6lXC/2fx7JbZmYyoLMJzkg=; b=Q6b8drNkWgalir8q qSifjUkbQahQkJKS/9lDriMXkn8wZnsz2aGwTUyi/VwTIhr2C1FrVyAMA+ipaW/d 6AYFlFl28LHF1D9gPEJ7r8zTwOitRT2hDn97S21/l1zrnaHpE6F31Idgb5i4obke EcyQO5GCWeJglRQGCT/d/VIVrud6NdBZ9I3EIoAG3y+DGygCUbcldfFwdH4pQmIJ NPHHFilkReR8fs8T/Ncd/uLNIdObCRdGaKuNxbWWg2uzcOE4EJgV6kwp3ggUPOrf osZv9xxULK+KfEjxnXj4jzC+IHr9L/OfqevTcnxOPYWgo+upzWK+vGJvfMKKOJS+ 4FZDCQ== Received: from mail-pg1-f200.google.com (mail-pg1-f200.google.com [209.85.215.200]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4e8t3ss2tc-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Tue, 19 May 2026 19:05:23 +0000 (GMT) Received: by mail-pg1-f200.google.com with SMTP id 41be03b00d2f7-c828acf7c1dso6610576a12.3 for ; Tue, 19 May 2026 12:05:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1779217523; x=1779822323; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=gHAPpvkbvRI2RnEglKIra6lXC/2fx7JbZmYyoLMJzkg=; b=XkyLd6MBys6SpwD/3YdJErNtOAuoZp60mSiWQkebEfJ94bIp+pTqQyOkUppf5YIy9/ GRBJY86qSeLSUy9WMBeWbCm8mDF6KibGCbWPSKjol6ki1F69whHvwIVz73dQgW4ZviZj 4u4ifbXmf4R36IQCxeJzlaniw/iwHlm+tfWjpDGlLplGfdmfipIGZaQ+boX4tjv0fKGv 7Lo9y0/iwm2OaKxzo1oz5cN1x0c/Pu4uqb2+MFVTbql1AW0AEZ0tQ/rf68dMARSseAP4 J4JNyxX23JbKWtIw1gGGn/wn8C/XGW51GMN1aB7zfmcZKIsBXxsd18HouV1K1JsvWO9v jngA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779217523; x=1779822323; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=gHAPpvkbvRI2RnEglKIra6lXC/2fx7JbZmYyoLMJzkg=; b=gIAMdSPerDgIK5xuPP+tBbYCcrAxppCE2PKCKDG7/7sMIpNYMXHeD8QGal9wyVBMrH 3z1n7AMyVmLTB4/ELGGoEo1kqhxBQgO7NIBBiGb3jqKVvq1ZXV9ImK7QFEEh18fZM665 SCKeCTnYc31uOtuB5pTLuuNmtAWPOdxe/8bg1xVPBFNg5T5DVleyGmSeE6XT6iG3xrwW r1obUK+LaA4prKPgKPCxCsmKjw88pnF1uaFwYpjLYruCItN+PR1156GzvW2Fe5jCYbse ruKixykJWF9qIo8PIpCxzl+MWdLyMQGMtYuTJCJ1g7n+Q3ANME715L5IiETjvjleYwk4 cpmw== X-Forwarded-Encrypted: i=1; AFNElJ8PZBCZMR07mj2rzgz4aUM6RPXaIkBXI7MXw49GJ5XskGFUaKS+mec2ITDcdd+09FIH46eqD9EkVSrx//s=@vger.kernel.org X-Gm-Message-State: AOJu0Yxw9R/ptcS2vryuHL0M99JJn5abtDdiT1GblazUEhPH3wh7Pms1 nPc7rgRsZO/NUcSqFTUFp763uckd6dBchGIZSbot6XBErz/gBwxNWzP3jEj3GOHGxuMNypy3YGj cHHtPpKMn0nc+cfRUL3+tSUrSBdIu0ujJBgvDxIWE4PnH9aCHJfj7J2d3tKd8fJfnlQ0= X-Gm-Gg: Acq92OEv+f9x/NbP1G/7Aq4P0vPxUbsulHV9cdA8jHgMQod+HlyxbYStZAzUKysKoVt yquf2rMcG708dtPWPLfYHnm9VPRWBVkbdq0wiiTAMTwAvd6fUGg5w1EqFvQj6CZAHoYOdC/Lthe /wiSvpu2ZhqZ4M0FUg5PbL43kDvjwWnZ9TkauzFmUwKN+aTbm+WYjQbruf/wSbfb6f/qb1QM2Fx 1KaLYgTPoYcS1dnWoPA9FimVliKS9llgwALDr7Qdjx3KMCkFDUXxUXeP0vDcQzegROYtZP9Yh+z h9Dlm7PhjOS/VCJWouFy23/FnnJ72kUWp886MtjVA2Kb7SPnKT8xACvg2uOUnNCuVl+PCJB/LA0 pElhhdquwxw7jO0gQAafNP1cPKGcZ9aUcq3xfhQQ9ZbJAXFG90akkMXvwZfvP8JXAs2jNK38uvT IFKnQ5AagL1+J4CTSSLVUtVRwCZ8Y0IP9BwycXn16Vw2our/zn7K4= X-Received: by 2002:a05:6a20:9188:b0:3a2:e089:ae56 with SMTP id adf61e73a8af0-3b22ee36135mr24018352637.52.1779217522720; Tue, 19 May 2026 12:05:22 -0700 (PDT) X-Received: by 2002:a05:6a20:9188:b0:3a2:e089:ae56 with SMTP id adf61e73a8af0-3b22ee36135mr24018308637.52.1779217522069; Tue, 19 May 2026 12:05:22 -0700 (PDT) Received: from hu-viswanat-blr.qualcomm.com (blr-bdr-fw-01_GlobalNAT_AllZones-Outside.qualcomm.com. [103.229.18.19]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-83f19794e6esm17423401b3a.25.2026.05.19.12.05.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 19 May 2026 12:05:21 -0700 (PDT) From: Vignesh Viswanathan Date: Wed, 20 May 2026 00:35:09 +0530 Subject: [PATCH 1/2] dt-bindings: soc: qcom: Document CDSP Power Management Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260520-cdsp-power-v1-1-85eb9501a1cd@oss.qualcomm.com> References: <20260520-cdsp-power-v1-0-85eb9501a1cd@oss.qualcomm.com> In-Reply-To: <20260520-cdsp-power-v1-0-85eb9501a1cd@oss.qualcomm.com> To: Bjorn Andersson , Konrad Dybcio , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Liam Girdwood , Mark Brown , Vignesh Viswanathan Cc: linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779217514; l=5161; i=vignesh.viswanathan@oss.qualcomm.com; s=20260518; h=from:subject:message-id; bh=FswLjexa4W/19cOLQDCspqJYNyI5kjTklXut13h725o=; b=R/5ABYELjlk9wnZSNkHlXqO9inFOaDo6atAm1JPNGo2b1bxoEK8e21KAIv9zuc8/jpap//BlT kOHtpjS8SwAAsG92mxeQQh3I5aLQaZRx85GVpGmQrV5W3+HNmLHna9W X-Developer-Key: i=vignesh.viswanathan@oss.qualcomm.com; a=ed25519; pk=/lHspsTTqZQg546ZudgrbywCsk3Whx/C0XNVUevaKNk= X-Proofpoint-GUID: 1_eRwebachSsqjibeAmUtOYSZfFLRiO_ X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNTE5MDE5MCBTYWx0ZWRfX5fesJrgE6e6t /cZsid40pPgXComaXpz4ZhKFBQKdzKBroz7TWY6h5Xfb/iF1eiHZhBiAQJQYduHzQqG53qD2Lhf vK/SSvtPNf1iUZoJK0mzVvQv0I3KgbexfWihPsMooRHEnSc2VVB6LWa+b0s9KoGL68Yue3Yt9Fv c+Tkd9hdRssa255ohzrAQybEVZtWQclyMO1y1dJqzdvTGMJJrgoZC4R1Zm4tPzMne89yE4Rn82y bvRCfbFG3Oz1nG6XPvO7lCerwsu2WKBAimbjvR8DA6gBFYZROrTmi7d6ry5lxkX7ZAaf2vC3XR6 qDsge/uQ6QzOF1sKuABXb91cRHlv1fAfG+as4ASSO4s0+zwzdALh5xnZ9oYVAcNeVjZOvCEl4z2 +RbX5oESRuuxuHD8CX4TM/cjvtvNTkKsx+o94RMOMcrEiyTIg2KXQS2tEOASBfbSK4IhfOvrbVN y9REuhHDBOW03GG5tAg== X-Proofpoint-ORIG-GUID: 1_eRwebachSsqjibeAmUtOYSZfFLRiO_ X-Authority-Analysis: v=2.4 cv=SNhykuvH c=1 sm=1 tr=0 ts=6a0cb473 cx=c_pps a=oF/VQ+ItUULfLr/lQ2/icg==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=NGcC8JguVDcA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yOCtJkima9RkubShWh1s:22 a=gEfo2CItAAAA:8 a=EUspDBNiAAAA:8 a=85UeY_1I7I5IrrPC5DQA:9 a=QEXdDO2ut3YA:10 a=3WC7DwWrALyhR5TkjVHa:22 a=sptkURWiP4Gy88Gu7hUp:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-05-19_05,2026-05-18_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 spamscore=0 bulkscore=0 adultscore=0 impostorscore=0 lowpriorityscore=0 priorityscore=1501 malwarescore=0 phishscore=0 clxscore=1015 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2605130000 definitions=main-2605190190 Add documentation for the CDSP Power Management driver, which handles Dynamic Clock and Voltage Scaling (DCVS) requests via SMEM, manages Low Power Mode (LPM) transitions via MPM handshake, and provides virtual regulators for the remoteproc driver to control CDSP power rails. Signed-off-by: Vignesh Viswanathan --- .../bindings/soc/qcom/qcom,cdsp-power.yaml | 138 +++++++++++++++++= ++++ 1 file changed, 138 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,cdsp-power.yam= l b/Documentation/devicetree/bindings/soc/qcom/qcom,cdsp-power.yaml new file mode 100644 index 000000000000..f0f89fdeba4e --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,cdsp-power.yaml @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,cdsp-power.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm CDSP Power Management + +maintainers: + - Vignesh Viswanathan + +description: + The CDSP Power Management driver provides power management services for = the + Qualcomm Compute DSP (CDSP) subsystem. It handles Dynamic Clock and Volt= age + Scaling (DCVS) requests via SMEM, manages Low Power Mode (LPM) transitio= ns + via MPM handshake, and provides virtual regulators that are consumed by = the + CDSP remoteproc driver. + +properties: + compatible: + const: qcom,cdsp-power + + reg: + items: + - description: MPM (Modem Power Manager) register region + - description: RSCC (RSC Configuration) register region + + reg-names: + items: + - const: mpm + - const: rscc + + interrupts-extended: + items: + - description: LPM (Low Power Mode) interrupt from MPM + - description: DCVS (Dynamic Clock and Voltage Scaling) interrupt fr= om IPCC + + interrupt-names: + items: + - const: lpm + - const: dcvs + + mboxes: + maxItems: 1 + description: IPCC mailbox channel for sending DCVS responses to CDSP + + qcom,smem-item: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + SMEM item ID used for DCVS communication channel between APSS and CD= SP. + This is a platform-specific value that identifies the shared memory = region. + + vdd-cx-supply: + description: + Phandle to the CX voltage regulator. This is the actual hardware reg= ulator + (e.g., from MP8899 PMIC) that supplies power to the CDSP CX rail. + + vdd-mx-supply: + description: + Phandle to the MX voltage regulator. This is the actual hardware reg= ulator + (e.g., from MP8899 PMIC) that supplies power to the CDSP MX rail. Op= tional + on boards where MX rail is always-on or not present. + + regulators: + type: object + description: + Virtual regulators provided by this driver for consumption by the CD= SP + remoteproc driver. These virtual regulators pass through enable/disa= ble + requests to the actual hardware regulators (vdd-cx-supply, vdd-mx-su= pply). + + properties: + cdsp-vdd-cx: + type: object + $ref: /schemas/regulator/regulator.yaml# + description: Virtual CX regulator for CDSP + unevaluatedProperties: false + + cdsp-vdd-mx: + type: object + $ref: /schemas/regulator/regulator.yaml# + description: Virtual MX regulator for CDSP + unevaluatedProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - reg-names + - interrupts-extended + - interrupt-names + - mboxes + - qcom,smem-item + - vdd-cx-supply + - regulators + +additionalProperties: false + +examples: + - | + #include + #include + #include + + soc { + #address-cells =3D <2>; + #size-cells =3D <2>; + + cdsp_power: cdsp-power@4ae000 { + compatible =3D "qcom,cdsp-power"; + reg =3D <0x0 0x004ae000 0x0 0x1000>, + <0x0 0x26018018 0x0 0x4>; + reg-names =3D "mpm", "rscc"; + + interrupts-extended =3D <&intc GIC_SPI 65 IRQ_TYPE_EDGE_RISING= 0>, + <&ipcc IPCC_CLIENT_CDSP + IPCC_MPROC_SIGNAL_PING + IRQ_TYPE_EDGE_RISING>; + interrupt-names =3D "lpm", "dcvs"; + + mboxes =3D <&ipcc IPCC_CLIENT_CDSP IPCC_MPROC_SIGNAL_PING>; + + qcom,smem-item =3D <503>; + + vdd-cx-supply =3D <&ipq9650_s2>; + vdd-mx-supply =3D <&ipq9650_s4>; + + regulators { + cdsp_vdd_cx: cdsp-vdd-cx { + regulator-name =3D "cdsp-vdd-cx"; + }; + + cdsp_vdd_mx: cdsp-vdd-mx { + regulator-name =3D "cdsp-vdd-mx"; + }; + }; + }; + }; --=20 2.43.0 From nobody Mon May 25 01:15:21 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 673FE343892 for ; Tue, 19 May 2026 19:05:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779217533; cv=none; b=rA296qFqEbXudzF7BLVxZWxmC9zWdGjw2+DC4hU+kT6e/1E9cjffPsuxmr8Gl3ULyJcfgcTgpS2wUpCw924vXe9B9aDZNlL4kdeXHZUSm9sUlC1CQnXfkIZLqgMb8ACk6icjnBqhmCiOXgqt1SHFl3Lca/h6119xPZkYBlfrdRQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779217533; c=relaxed/simple; bh=0nJ5cuYmgNR1GXW+ovUrU1Nk0Oy9Hy/TWxUGuvD0/rI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=medaObvvGyiDvi7Y7eWK0BEvOOBUuCJQe+eNVWTlchEwnnDpDmQeMfFn7ghWL4T53Q9iZ8VRjVu45rj8O+xfRVAcGnf1Gczm17+R3vjqR9un9JP4MRGqqgmIz8ARz9LfUVR+OPstC6y1xkMetNV5a0mbXn5Wk17d8uP9P5kbCKg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=L5+zLoYj; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=NL+P9C1y; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="L5+zLoYj"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="NL+P9C1y" Received: from pps.filterd (m0279868.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 64JExNQS1963707 for ; Tue, 19 May 2026 19:05:29 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= e5POHKLczdfKTAqg1xTtu+Qh+k2SCbUWnBpov+SLib0=; b=L5+zLoYjEugRlEZZ IV4HZhJK0H5BlbWqcX9JgNhLy2Y820oWNMeAM7cpRo6XzEsEzUdPPlMCqa7BOwdt WPX/mvBhq5TShDAB+yTElbUNceQtvKoPQR4mMpvzTBzK+KhbqnC7QnFqClO6yDO4 CCkee7cXSgRWpm6bEqWsSuxEOZL0k9c0sCGD4D0Pdu8rVwsd1B+A8tKPR1Igx/Aa UsCusQhfPcBJMc7w5f7J8gjwGoMhWhZMcKZnDFVsk4ePSD6tmtbjFTK9cUETTHpQ nF8LC3Wyu3f1NvEg6cw0wf38SaiuHL1GzMXrkiH5H0is9UVspQZP5znvySQykWuX pFfldg== Received: from mail-pf1-f197.google.com (mail-pf1-f197.google.com [209.85.210.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4e8t3t12xb-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Tue, 19 May 2026 19:05:29 +0000 (GMT) Received: by mail-pf1-f197.google.com with SMTP id d2e1a72fcca58-82fa1c94b37so4707770b3a.0 for ; Tue, 19 May 2026 12:05:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1779217528; x=1779822328; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=e5POHKLczdfKTAqg1xTtu+Qh+k2SCbUWnBpov+SLib0=; b=NL+P9C1yaH9I7SWwLKdUSDC5oapdBrpIUM/FJ4KFBYAVIaaOjNZ5+h8SPqZHVhat+h 8QKZ9oc85OXcK5gm5Tsgt9XiZJNyl7uvZLQgp8QluBqDA7fWDEezmU88Cv0e5f1ULuYu TtX3anCRbkJdog5zfGSPDFLHt95JVV1tvIe/WmVnp0YM8pdwY4Bnqpibm/cAfznB5Bn3 GhluNtUluTmG6YHoFSm09w5iSSQFAl4NUH//CKQacnbCustYfLwDP7DHG+mojGxzVPVy +RmijbtJtOvSS9oAS9J1u59yTrLBtePhKvi7kltrhKMxhjY2mCX2Q4enwmAnwVAKrP99 ACpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779217528; x=1779822328; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=e5POHKLczdfKTAqg1xTtu+Qh+k2SCbUWnBpov+SLib0=; b=QQ875fLKbF07jEv42GreyXFVUp7OF/j8ahPATJujWmcl5vHIhY6SRIL4CCIL1d2jOI 5PTU+8nV4y7IFPX12MGZ2idCTocOzgikVf1kZJcV6uukowSW5c/9SBUOhRIILIr+mgRs Bdiy+k2/+gu5ZxvhMRxUk+bom5TjqjD2g+/bHwUdoZdcdyYCH4xXF9VBnmJfbGtgSeCr r4cNv8fJpKT1rpg4sSczCFBf/n//RL2vBG3TG+NIg3Qg7HA+Rnk8gkCDW9ER4x265QKe rkgn7mSDuAw0PS6jQJroceso0E57/GHBSwSAGYb2jzZelC+A7va5x01BZXs4hhtgti6Y AEXA== X-Forwarded-Encrypted: i=1; AFNElJ+KAJYaVSs/SV6IBeIUiZGPG3ol5yCP3gKQeBsGjeDYUW/HlXwGC1428XEIHz3hwOvCGvmo09WGVRhnYfk=@vger.kernel.org X-Gm-Message-State: AOJu0YwyBPvzukygk/odgMUS+Geh/XzqTzVQlGxUgyuZAt18EHCNnQl9 xgFzG/koeA7hcrL/mb02IKYcz7/AXjzu8XVH/X1Evx7u0RVFr3wOeuuhdR6SXF4AJrhlL7jxR/f k2SbFk6Nl//nP+0j+7FxGAPI56p/MpebB+k/wQQTTxUsRZV38igp2SwNeaDKZXoh+NKQ= X-Gm-Gg: Acq92OFFheVIqx3SMnjZt8AJc9kj3FzS0MAJuU4owkiVBzrtNQuX68SVMZkpB9BwLQp QUO1t9CwGq9NazUfvhNwsdkqMATJnIQvQhzANB6V2IM+vB0T/FN/lpanWZg89OWyiCXjdnHydJN NAp3kA242p1uNWxZb6K3gxpzTy69gGclvfDPWRiuIZi7wIgJAJt6GjCHf8MNwn0gGwYXBNwrP5x 6TcTLLEClskTY4u4Zax5KoZN7fiHKDXMiw77RJ3SUwwdHMnb0z63zNTsKg+oyn8Z8G599tJOXIB vRmcHgcj67OCqgNzkTBSIPHbgD8qcrTOBfqdLHe5EN12cMTtGWhfM8dARC816aXmUqBnU4cZ+E/ Ath8ygvbyY/nPTwbEZ7jsZA6xA3OewHqjtsx/lximBNoKNbYldQnOprCf4cgjTrovjiFQfI+40d iCkN19Sp/qfBxm8DyMbSk66nxZPUzUmYqaehq4bfYViaXlcJY6JXE= X-Received: by 2002:a05:6a00:3a24:b0:834:e24c:3f9c with SMTP id d2e1a72fcca58-83f18e7662fmr20565770b3a.22.1779217527431; Tue, 19 May 2026 12:05:27 -0700 (PDT) X-Received: by 2002:a05:6a00:3a24:b0:834:e24c:3f9c with SMTP id d2e1a72fcca58-83f18e7662fmr20565737b3a.22.1779217526646; Tue, 19 May 2026 12:05:26 -0700 (PDT) Received: from hu-viswanat-blr.qualcomm.com (blr-bdr-fw-01_GlobalNAT_AllZones-Outside.qualcomm.com. [103.229.18.19]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-83f19794e6esm17423401b3a.25.2026.05.19.12.05.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 19 May 2026 12:05:26 -0700 (PDT) From: Vignesh Viswanathan Date: Wed, 20 May 2026 00:35:10 +0530 Subject: [PATCH 2/2] soc: qcom: Add CDSP power management driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260520-cdsp-power-v1-2-85eb9501a1cd@oss.qualcomm.com> References: <20260520-cdsp-power-v1-0-85eb9501a1cd@oss.qualcomm.com> In-Reply-To: <20260520-cdsp-power-v1-0-85eb9501a1cd@oss.qualcomm.com> To: Bjorn Andersson , Konrad Dybcio , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Liam Girdwood , Mark Brown , Vignesh Viswanathan Cc: linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Praveenkumar I , Manikanta Mylavarapu X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779217514; l=39344; i=vignesh.viswanathan@oss.qualcomm.com; s=20260518; h=from:subject:message-id; bh=I7fNLM4cZMHz1yzcQlS+hGvENR35Q/4zn9e4ogIUBRs=; b=SKtaz2zDORn3u2wi2tQ9dDl5KPR24Zm+FWwb15lKam7H5H1arac/7b+ejKVpa4DGKw9IYNYZE LoWOpR4gUjwAvPIfWBTLTKqhn56Z4R8pCqnZD8NttBugu/am+bPSapq X-Developer-Key: i=vignesh.viswanathan@oss.qualcomm.com; a=ed25519; pk=/lHspsTTqZQg546ZudgrbywCsk3Whx/C0XNVUevaKNk= X-Proofpoint-ORIG-GUID: wfvTbGZIm9G8BC5YyZVxg5AQjxrfEgqu X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNTE5MDE5MCBTYWx0ZWRfX+xfRFxiQmnGi SoGUKWwJC/E48AV4RlUEAAb4uh2lEXpkj0zFcVpEr2jslfWjdGmEY4uTAbi3pO67Bp71e7gulD1 l8vAKj1bxEdLu88PFjVMJY2DedR0XzdJiEA8HqWWZhqp5Gt3ETwCnbSnqs9bDgpiMNwAHbL0T6+ zb97ZjIQiePhxrMZe6jsms5zhTyRgDEG2bWxG5GvCqz7g5GZs0s9ENbhGJeGn/0sx3qmQXzsyCv XOwUPWimSvMA4GIyHQFaUe3mklTJtl/hwfA/Z7SpOe7QWzm6CiKeSp0EYwedUycpLei5OIWwLff XD8msUdaR4C6lH2MPd0ORjcMXpgByr4FImqJJXASGUazJjcagvCWVyknbkyPnMjYyJ4muonj8Cs pRfJ0zFFkOhYxYJ56El2oMOjn6ZSGfGv86LYq5rc58VmV+8Rz11u3dP2uscWOx4jkr0njb1u6nw S6ukmAZZLQO2KfkMjZg== X-Authority-Analysis: v=2.4 cv=Y/bIdBeN c=1 sm=1 tr=0 ts=6a0cb479 cx=c_pps a=rEQLjTOiSrHUhVqRoksmgQ==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=IkcTkHD0fZMA:10 a=NGcC8JguVDcA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=ZpdpYltYx_vBUK5n70dp:22 a=EUspDBNiAAAA:8 a=uuWAtIs7gR2RaXFyqDYA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=2VI0MkxyNR6bbpdq8BZq:22 X-Proofpoint-GUID: wfvTbGZIm9G8BC5YyZVxg5AQjxrfEgqu X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-05-19_05,2026-05-18_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=0 bulkscore=0 adultscore=0 phishscore=0 priorityscore=1501 impostorscore=0 lowpriorityscore=0 clxscore=1015 malwarescore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2605130000 definitions=main-2605190190 From: Praveenkumar I On platforms like IPQ9650, the CDSP subsystem cannot manage its own power rails and requires the APSS to handle power management on its behalf. Add a platform driver to fulfill this role. Handle LPM (Low Power Mode) by executing hardware isolation and restoration sequences via MPM register programming, coordinated with the NSP Q6 through a handshake protocol. Support both FULL_PC (CX+MX collapse) and LONG_APCR (CX-only) modes, detected dynamically at runtime. Handle DCVS (Dynamic Clock and Voltage Scaling) requests from the NSP Q6 received via a shared SMEM channel. Apply voltages via the regulator framework and write responses back to SMEM. Expose virtual cdsp-vdd-cx and cdsp-vdd-mx regulators so that the PAS remoteproc driver can control the NSP power rails through the standard regulator framework. Signed-off-by: Praveenkumar I Signed-off-by: Manikanta Mylavarapu Signed-off-by: Vignesh Viswanathan --- drivers/soc/qcom/Kconfig | 17 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/cdsp_power.c | 1065 +++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 1083 insertions(+) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 2caadbbcf830..f4b9204d4e9a 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -26,6 +26,23 @@ config QCOM_COMMAND_DB resource on a RPM-hardened platform must use this database to get SoC specific identifier and information for the shared resources. =20 +config QCOM_CDSP_POWER + tristate "Qualcomm CDSP Power Management driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on QCOM_SMEM + depends on REGULATOR + help + This driver manages power for the CDSP (Compute DSP) subsystem on + Qualcomm platforms. It handles DCVS (Dynamic Clock and Voltage Scaling) + for voltage scaling via SMEM/SMP2P, and LPM (Low Power Mode) for power + collapse/restore via MPM handshake. + + The driver coordinates with the cdsp_rproc driver for shared regulator + access and provides runtime voltage scaling and power management for + the CDSP subsystem. + + Say M here if you want to include support for CDSP power management. + config QCOM_GENI_SE tristate "QCOM GENI Serial Engine Driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index b7f1d2a57367..f3490a437cb8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -40,3 +40,4 @@ qcom_ice-objs +=3D ice.o obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE) +=3D qcom_ice.o obj-$(CONFIG_QCOM_PBS) +=3D qcom-pbs.o obj-$(CONFIG_QCOM_UBWC_CONFIG) +=3D ubwc_config.o +obj-$(CONFIG_QCOM_CDSP_POWER) +=3D cdsp_power.o diff --git a/drivers/soc/qcom/cdsp_power.c b/drivers/soc/qcom/cdsp_power.c new file mode 100644 index 000000000000..97aff858b139 --- /dev/null +++ b/drivers/soc/qcom/cdsp_power.c @@ -0,0 +1,1065 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * CDSP Power Management Driver for IPQ9650 + * + * Regulator ownership hierarchy: + * PAS remoteproc (cx-supply / mx-supply) + * -> cdsp-vdd-cx / cdsp-vdd-mx (virtual, provided by this driver) + * -> vdd-cx / vdd-mx (voltage regulators, consumed by th= is driver) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SMEM Protocol Definitions */ +/* NSP Q6 SMEM host ID */ +#define CDSP_SMEM_NSP_HOST_ID 5 +/* hdr(64) + request_area(1024) + response_area(256) */ +#define CDSP_SMEM_SIZE 1344 +/* Protocol magic number 'RPMH' */ +#define CDSP_SMEM_MAGIC 0x52504D48 +/* Protocol version v1.0 */ +#define CDSP_SMEM_VERSION 0x00010001 +#define CDSP_MSG_ID_REQUEST 0x01 +#define CDSP_MSG_ID_RESPONSE 0x02 +#define CDSP_RESP_MSG_SIZE 24 +#define CDSP_MAX_KVP 64 + +/* SMEM layout fixed offsets (relative to start of SMEM item) */ +#define CDSP_SMEM_REQUEST_OFFSET 64 +#define CDSP_SMEM_REQUEST_SIZE 1024 +#define CDSP_SMEM_RESPONSE_OFFSET 1088 +#define CDSP_SMEM_RESPONSE_SIZE 256 + +/* NSP CX voltage rail resource ID */ +#define CDSP_RESOURCE_ID_CX 0x03 +/* NSP MX voltage rail resource ID */ +#define CDSP_RESOURCE_ID_MX 0x04 + +/* MPM Register Offsets */ +#define RSC_HDSHK_IRQ_STAT 0x0004 +#define CLIENT_RSC_HDSHK(n) (0x0010 + (n) * 0x10) +#define CLIENT_RSC_IRQ_STAT(n) (0x0014 + (n) * 0x10) +#define CLIENT_RSC_IRQ_CLR(n) (0x0018 + (n) * 0x10) +#define VDD_RAIL_ISO_CTRL(n) (0x0330 + (n) * 0x4) + +/* RSCC Register Offsets */ +#define RSCC_BR_EVENT 0x0 +#define RSCC_BR_EVENT_PC_MODE BIT(3) + +/* MPM Handshake Bits */ +#define MPM_SHUTDOWN_REQ BIT(0) +#define MPM_SHUTDOWN_ACK BIT(1) +#define MPM_BRINGUP_REQ BIT(2) +#define MPM_BRINGUP_ACK BIT(3) + +/* MPM Isolation Control Bits */ +#define ISO_CLK_DIS BIT(0) +#define ISO_CLK_DIS_ACK BIT(1) +#define ISO_INPUT BIT(8) +#define ISO_INPUT_DFT BIT(9) +#define ISO_INPUT_CLKS BIT(10) +#define ISO_CLAMP_MEM BIT(11) +#define ISO_RET_0PIN BIT(16) +#define ISO_SAVE_FF BIT(17) +#define ISO_RESTORE_FF BIT(18) +#define ISO_FREEZE_OUTPUT BIT(24) +#define ISO_PWR_UP_RESET BIT(25) + +/* MXC rail mapped to isolation control register index 0 */ +#define VDD_RAIL_MX 0 +/* NSP/CX rail mapped to isolation control register index 1 */ +#define VDD_RAIL_CX 1 + +/* Power States */ +#define CDSP_POWER_OFF 0 +#define CDSP_POWER_ON 1 + +/* Timeouts */ +#define MPM_POLL_TIMEOUT_US 10000 +#define MPM_POLL_SLEEP_US 10 + +/* Virtual regulator IDs */ +enum cdsp_virt_reg_id { + CDSP_VIRT_NSP_CX =3D 0, + CDSP_VIRT_NSP_MX =3D 1, + CDSP_VIRT_MAX, +}; + +/** + * struct cdsp_smem_channel_hdr - SMEM channel header (64 bytes, at offset= 0x000) + * @magic: Protocol magic number (0x52504D48 =3D 'RPMH') + * @version: Protocol version (0x00010001 =3D v1.0) + * @cdsp_state: NSP Q6 readiness flag; 0 =3D not ready, 1 =3D ready= (written by NSP) + * @apss_state: APSS readiness flag; 0 =3D not ready, 1 =3D ready (= written by APSS) + * @request_offset: Byte offset of the request area from the start of t= he SMEM item + * @request_size: Size of the request area in bytes + * @response_offset: Byte offset of the response area from the start of = the SMEM item + * @response_size: Size of the response area in bytes + * @request_in_flight: Channel busy flag; 0 =3D idle, 1 =3D busy (NSP sets= , APSS clears) + * @cdsp_to_apss_irq: IPCC signal ID used for NSP-to-APSS interrupts + * @apss_to_cdsp_irq: IPCC signal ID used for APSS-to-NSP interrupts + * @cdsp_tx_count: Number of DCVS requests sent by NSP + * @cdsp_rx_count: Number of DCVS responses received by NSP + * @apss_tx_count: Number of DCVS responses sent by APSS + * @apss_rx_count: Number of DCVS requests received by APSS + * @reserved: Reserved, must be zero + * + * Fixed at offset 0x000 in the SMEM item. Matches rpmh_smem_channel_heade= r_t + * on the NSP Q6 side. APSS initialises this at probe time; NSP Q6 reads it + * to discover the request/response area offsets and to synchronise readin= ess. + */ +struct cdsp_smem_channel_hdr { + u32 magic; + u32 version; + u32 cdsp_state; + u32 apss_state; + u32 request_offset; + u32 request_size; + u32 response_offset; + u32 response_size; + u32 request_in_flight; + u32 cdsp_to_apss_irq; + u32 apss_to_cdsp_irq; + u32 cdsp_tx_count; + u32 cdsp_rx_count; + u32 apss_tx_count; + u32 apss_rx_count; + u32 reserved; +} __packed; + +/** + * struct cdsp_kvp - Key-Value-Pair entry in a DCVS request + * @key: Resource identifier; upper 16 bits =3D CDSP_RESOURCE_ID_CX or = _MX + * @length: Length of the value field in bytes (always 4) + * @value: Requested voltage in microvolts + */ +struct cdsp_kvp { + u32 key; + u32 length; + u32 value; +} __packed; + +/** + * struct cdsp_smem_request - DCVS request message written by NSP Q6, read= by APSS + * @msg_size: Total message size in bytes + * @sequence: Monotonically increasing sequence number + * @msg_id: Message type; CDSP_MSG_ID_REQUEST (0x01) for DCVS reques= ts + * @req_id: Request identifier echoed in the response + * @set: Power set being requested (ACTIVE / SLEEP / WAKE) + * @num_commands: Number of KVP entries that follow + * @timestamp_us: Request timestamp in microseconds + * @kvp: Array of key-value-pair voltage commands (up to CDSP_MAX= _KVP) + */ +struct cdsp_smem_request { + u32 msg_size; + u32 sequence; + u32 msg_id; + u32 req_id; + u32 set; + u32 num_commands; + u32 timestamp_us; + struct cdsp_kvp kvp[CDSP_MAX_KVP]; +} __packed; + +/** + * struct cdsp_smem_response - DCVS response message written by APSS, read= by NSP Q6 + * @msg_size: Total message size in bytes (CDSP_RESP_MSG_SIZE =3D 24) + * @sequence: Echo of the request sequence number + * @msg_id: Message type; CDSP_MSG_ID_RESPONSE (0x02) + * @status: Result code; 0 on success, negative errno on failure + * @data: Actual voltage applied in microvolts (valid when status = =3D=3D 0) + * @timestamp_us: Completion timestamp in microseconds + */ +struct cdsp_smem_response { + u32 msg_size; + u32 sequence; + u32 msg_id; + u32 status; + u32 data; + u32 timestamp_us; +} __packed; + +/** + * struct cdsp_smem_region - Full SMEM item layout (1344 bytes total) + * @hdr: Channel header at offset 0x000 (64 bytes) + * @request: DCVS request area at offset 0x040 (padded to 1024 bytes) + * @_req_pad: Padding to align response area to offset 0x440 + * @response: DCVS response area at offset 0x440 (padded to 256 bytes) + * @_resp_pad: Padding to complete the 256-byte response area + * + * Layout matches the rpmh_smem_channel_header_t offsets on the NSP Q6 sid= e. + */ +struct cdsp_smem_region { + struct cdsp_smem_channel_hdr hdr; + struct cdsp_smem_request request; + u8 _req_pad[228]; + struct cdsp_smem_response response; + u8 _resp_pad[232]; +} __packed; + +/** + * struct cdsp_power_driver - Main driver context + * @dev: Device pointer + * @smem: Pointer to SMEM region + * @dcvs_mbox_client: Mailbox client for DCVS response interrupt (APSS->NS= P) + * @dcvs_mbox_chan: Mailbox channel for DCVS response interrupt + * @dcvs_irq: DCVS interrupt number (NSP->APSS IPCC PING) + * @dcvs_work: Work structure for DCVS processing + * @mpm_regmap: Regmap for MPM handshake registers + * @lpm_irq: LPM interrupt number + * @lpm_work: Work structure for LPM processing + * @rscc_regmap: Regmap for RSCC power mode detection register + * @vdd_cx: CX voltage regulator (consumer handle) + * @vdd_mx: MX voltage regulator (consumer handle, NULL if absent on this = board) + * @power_state: Current NSP power state (CDSP_POWER_ON / CDSP_POWER_OFF) + * @lock: Mutex serialising DCVS and LPM work + */ +struct cdsp_power_driver { + struct device *dev; + + /* SMEM for DCVS */ + struct cdsp_smem_region *smem; + + /* Mbox for DCVS response (APSS -> NSP via IPCC PING) */ + struct mbox_client dcvs_mbox_client; + struct mbox_chan *dcvs_mbox_chan; + + int dcvs_irq; + struct work_struct dcvs_work; + + /* MPM for LPM */ + struct regmap *mpm_regmap; + int lpm_irq; + struct workqueue_struct *lpm_wq; + struct work_struct lpm_work; + + /* RSCC for power mode detection */ + struct regmap *rscc_regmap; + + /* PMIC regulator consumer handles */ + struct regulator *vdd_cx; + struct regulator *vdd_mx; + + /* State tracking */ + atomic_t power_state; + /* Mutex serialising DCVS and LPM work */ + struct mutex lock; +}; + +/** + * cdsp_virt_reg_enable() - Enable a virtual NSP regulator + * @rdev: Regulator device + * + * Passes the enable request through to the underlying PMIC consumer handle + * (vdd_cx or vdd_mx). Returns 0 immediately if the MX rail is absent on + * this board. + * + * Return: 0 on success, negative error code on failure + */ +static int cdsp_virt_reg_enable(struct regulator_dev *rdev) +{ + struct cdsp_power_driver *drv =3D rdev_get_drvdata(rdev); + int id =3D rdev_get_id(rdev); + struct regulator *reg =3D (id =3D=3D CDSP_VIRT_NSP_CX) ? drv->vdd_cx : dr= v->vdd_mx; + + if (!reg) + return 0; + return regulator_enable(reg); +} + +/** + * cdsp_virt_reg_is_enabled() - Check if a virtual NSP regulator is enabled + * @rdev: Regulator device + * + * Queries the enable state of the underlying PMIC consumer handle (vdd_cx + * or vdd_mx). Returns 1 if the MX rail is absent on this board, since the + * MX hardware rail is always on in that configuration. + * + * Return: 1 if enabled (or absent), 0 if disabled, negative error code on= failure + */ +static int cdsp_virt_reg_is_enabled(struct regulator_dev *rdev) +{ + struct cdsp_power_driver *drv =3D rdev_get_drvdata(rdev); + int id =3D rdev_get_id(rdev); + struct regulator *reg =3D (id =3D=3D CDSP_VIRT_NSP_CX) ? drv->vdd_cx : dr= v->vdd_mx; + + if (!reg) + return 1; + return regulator_is_enabled(reg); +} + +/** + * cdsp_virt_reg_disable() - Disable a virtual NSP regulator + * @rdev: Regulator device + * + * Passes the disable request through to the underlying PMIC consumer hand= le + * (vdd_cx or vdd_mx). Returns 0 immediately if the MX rail is absent on + * this board. + * + * Return: 0 on success, negative error code on failure + */ +static int cdsp_virt_reg_disable(struct regulator_dev *rdev) +{ + struct cdsp_power_driver *drv =3D rdev_get_drvdata(rdev); + int id =3D rdev_get_id(rdev); + struct regulator *reg =3D (id =3D=3D CDSP_VIRT_NSP_CX) ? drv->vdd_cx : dr= v->vdd_mx; + + if (!reg) + return 0; + + /* Disable the regulator if it's enabled */ + if (cdsp_virt_reg_is_enabled(rdev)) + return regulator_disable(reg); + + return 0; +} + +static const struct regulator_ops cdsp_virt_reg_ops =3D { + .enable =3D cdsp_virt_reg_enable, + .disable =3D cdsp_virt_reg_disable, + .is_enabled =3D cdsp_virt_reg_is_enabled, +}; + +static const struct regulator_desc cdsp_virt_reg_descs[CDSP_VIRT_MAX] =3D { + [CDSP_VIRT_NSP_CX] =3D { + .id =3D CDSP_VIRT_NSP_CX, + .name =3D "cdsp-vdd-cx", + .of_match =3D "cdsp-vdd-cx", + .regulators_node =3D "regulators", + .ops =3D &cdsp_virt_reg_ops, + .type =3D REGULATOR_VOLTAGE, + .owner =3D THIS_MODULE, + }, + [CDSP_VIRT_NSP_MX] =3D { + .id =3D CDSP_VIRT_NSP_MX, + .name =3D "cdsp-vdd-mx", + .of_match =3D "cdsp-vdd-mx", + .regulators_node =3D "regulators", + .ops =3D &cdsp_virt_reg_ops, + .type =3D REGULATOR_VOLTAGE, + .owner =3D THIS_MODULE, + }, +}; + +/** + * cdsp_is_full_pc_mode() - Check if FULL_PC mode is selected + * @drv: Driver context + * + * Reads the CDSP RSCC_BR_EVENT register to determine power collapse mode. + * Bit 3: 0 =3D FULL_PC (collapse both CX and MX) + * 1 =3D LONG_APCR (collapse only CX) + * + * Return: true if FULL_PC mode, false if LONG_APCR mode + */ +static bool cdsp_is_full_pc_mode(struct cdsp_power_driver *drv) +{ + u32 br_event; + int ret; + + ret =3D regmap_read(drv->rscc_regmap, RSCC_BR_EVENT, &br_event); + if (ret) { + dev_err(drv->dev, "Failed to read RSCC_BR_EVENT: %d\n", ret); + return true; /* Default to FULL_PC on error */ + } + + /* Bit 3: 0 =3D FULL_PC (collapse both CX and MX) + * 1 =3D LONG_APCR (collapse only CX) + */ + return !(br_event & RSCC_BR_EVENT_PC_MODE); +} + +/** + * cdsp_dcvs_irq_handler() - DCVS interrupt handler + * @irq: Interrupt number + * @data: Driver context + * + * Called when CDSP sends a DCVS request via IPCC PING interrupt. + * Schedules work to process the request. + * + * Return: IRQ_HANDLED + */ +static irqreturn_t cdsp_dcvs_irq_handler(int irq, void *data) +{ + struct cdsp_power_driver *drv =3D data; + + /* Schedule work to process DCVS request */ + schedule_work(&drv->dcvs_work); + + return IRQ_HANDLED; +} + +/** + * cdsp_dcvs_work_fn() - Process a DCVS voltage scaling request from the N= SP Q6 + * @work: Work structure embedded in struct cdsp_power_driver + * + * Reads the KVP request from the SMEM channel, validates the resource ID = and + * voltage, applies the voltage via the regulator framework, writes the re= sponse + * back to SMEM, and signals the NSP Q6 via the IPCC PING mailbox channel. + */ +static void cdsp_dcvs_work_fn(struct work_struct *work) +{ + struct cdsp_power_driver *drv =3D container_of(work, + struct cdsp_power_driver, + dcvs_work); + struct cdsp_smem_region *smem =3D drv->smem; + u32 sequence, num_commands; + int ret =3D 0, actual_uv =3D 0; + int i; + + mutex_lock(&drv->lock); + + /* Drop DCVS requests while the NSP Q6 is powered off */ + if (atomic_read(&drv->power_state) =3D=3D CDSP_POWER_OFF) { + dev_warn(drv->dev, "DCVS request while powered off, dropping\n"); + mutex_unlock(&drv->lock); + return; + } + + /* Read request header from SMEM */ + rmb(); + sequence =3D smem->request.sequence; + num_commands =3D smem->request.num_commands; + + /* Validate message type =E2=80=94 only process DCVS requests */ + if (smem->request.msg_id !=3D CDSP_MSG_ID_REQUEST) { + dev_err(drv->dev, "Unexpected msg_id: 0x%x (expected 0x%x)\n", + smem->request.msg_id, CDSP_MSG_ID_REQUEST); + ret =3D -EINVAL; + goto send_response; + } + + dev_dbg(drv->dev, "DCVS request: seq=3D%u, num_commands=3D%u, timestamp= =3D%u us\n", + sequence, num_commands, smem->request.timestamp_us); + + if (num_commands > CDSP_MAX_KVP) { + dev_err(drv->dev, "Too many KVP commands: %u (max %d)\n", + num_commands, CDSP_MAX_KVP); + ret =3D -EINVAL; + goto send_response; + } + + /* Process each KVP: key=3Dresource_type, value=3Dvoltage_uv */ + for (i =3D 0; i < num_commands; i++) { + u32 key =3D smem->request.kvp[i].key; + u32 voltage_uv =3D smem->request.kvp[i].value; + /* Upper 16 bits of the KVP key encode the resource ID */ + u32 resource_id =3D (key >> 16) & 0xFFFF; + struct regulator *reg; + int uv; + + if (resource_id =3D=3D CDSP_RESOURCE_ID_CX) { + reg =3D drv->vdd_cx; + } else if (resource_id =3D=3D CDSP_RESOURCE_ID_MX) { + if (!drv->vdd_mx) { + dev_dbg(drv->dev, + "KVP[%d]: MX rail not available on this board\n", + i); + ret =3D -EINVAL; + goto send_response; + } + reg =3D drv->vdd_mx; + } else { + dev_err(drv->dev, "KVP[%d]: unknown key 0x%x\n", i, key); + ret =3D -EINVAL; + goto send_response; + } + + /* Set voltage; the regulator framework enforces DTS constraints */ + ret =3D regulator_set_voltage(reg, voltage_uv, voltage_uv); + if (ret) { + dev_err(drv->dev, "KVP[%d]: failed to set %s voltage %u uV: %d\n", + i, resource_id =3D=3D CDSP_RESOURCE_ID_CX ? "CX" : "MX", + voltage_uv, ret); + goto send_response; + } + + /* Read back actual voltage */ + uv =3D regulator_get_voltage(reg); + if (uv < 0) { + dev_warn(drv->dev, "KVP[%d]: failed to read back voltage: %d\n", + i, uv); + uv =3D voltage_uv; + } + /* Track the last successfully set voltage to report in the response */ + actual_uv =3D uv; + + dev_dbg(drv->dev, "DCVS: Set %s to %d uV (requested %u uV)\n", + resource_id =3D=3D CDSP_RESOURCE_ID_CX ? "CX" : "MX", + actual_uv, voltage_uv); + } + +send_response: + /* Write response to SMEM response area */ + smem->response.msg_size =3D CDSP_RESP_MSG_SIZE; + smem->response.sequence =3D sequence; + smem->response.msg_id =3D CDSP_MSG_ID_RESPONSE; + smem->response.status =3D ret; + smem->response.data =3D (ret =3D=3D 0) ? actual_uv : 0; + smem->response.timestamp_us =3D ktime_to_us(ktime_get()); + + /* + * Update header statistics and clear in-flight flag. + * Order: write response data -> clear request_in_flight -> wmb -> + * send interrupt. NSP Q6 checks request_in_flight =3D=3D 0 to know + * the response is ready. + */ + smem->hdr.apss_rx_count++; + smem->hdr.apss_tx_count++; + /* Mark channel idle before sending the interrupt */ + WRITE_ONCE(smem->hdr.request_in_flight, 0); + /* Ensure response data is visible before sending interrupt to NSP Q6 */ + wmb(); + + /* Send IPCC PING interrupt to NSP Q6 */ + mbox_send_message(drv->dcvs_mbox_chan, NULL); + mbox_client_txdone(drv->dcvs_mbox_chan, 0); + + mutex_unlock(&drv->lock); +} + +/** + * cdsp_execute_isolation_sequence() - Execute isolation and power-down se= quence + * @drv: Driver context + * + * Executes the isolation sequence for power collapse as defined in + * the MPM power collapse specification, followed by regulator disable. + * Processes NSP/CX rail first (always), then MXC/MX rail (FULL_PC mode on= ly). + */ +static void cdsp_execute_isolation_sequence(struct cdsp_power_driver *drv) +{ + /* + * When vdd_mx is absent (board has no MX regulator handle), always + * execute FULL_PC isolation for both CX and MX rails regardless of + * what the Q6 requested. + */ + int num_rails =3D (!drv->vdd_mx || cdsp_is_full_pc_mode(drv)) ? 2 : 1; + unsigned int iso_regs[] =3D { + VDD_RAIL_ISO_CTRL(VDD_RAIL_CX), /* NSP/CX rail - always processed */ + VDD_RAIL_ISO_CTRL(VDD_RAIL_MX), /* MXC/MX rail - FULL_PC only */ + }; + struct regulator *regulators[] =3D { drv->vdd_cx, drv->vdd_mx }; + struct regmap *mpm_map =3D drv->mpm_regmap; + int ret, i; + u32 val; + + for (i =3D 0; i < num_rails; i++) { + unsigned int iso =3D iso_regs[i]; + + /* Step 1: Disable clocks */ + regmap_update_bits(mpm_map, iso, ISO_CLK_DIS, ISO_CLK_DIS); + + /* Poll CLK_DIS_ACK only for NSP/CX rail (MXC CLK_DIS_ACK is tied to 0) = */ + if (i =3D=3D 0) { + ret =3D regmap_read_poll_timeout(mpm_map, iso, val, + (val & ISO_CLK_DIS_ACK), + MPM_POLL_SLEEP_US, MPM_POLL_TIMEOUT_US); + if (ret) + dev_err(drv->dev, "Timeout waiting for NSP CLK_DIS_ACK\n"); + } + + /* Step 2: Isolate inputs */ + regmap_update_bits(mpm_map, iso, + ISO_INPUT | ISO_INPUT_CLKS | ISO_INPUT_DFT, + ISO_INPUT | ISO_INPUT_CLKS | ISO_INPUT_DFT); + /* Step 3: Clamp memories */ + regmap_update_bits(mpm_map, iso, ISO_CLAMP_MEM, ISO_CLAMP_MEM); + /* Step 4: Enable 0-pin retention */ + regmap_update_bits(mpm_map, iso, ISO_RET_0PIN, ISO_RET_0PIN); + /* Step 5: Save to balloon latch (low-high-low pulse) */ + regmap_update_bits(mpm_map, iso, ISO_SAVE_FF, 0x0); + regmap_update_bits(mpm_map, iso, ISO_SAVE_FF, ISO_SAVE_FF); + regmap_update_bits(mpm_map, iso, ISO_SAVE_FF, 0x0); + /* Step 6: Prepare restore signal (low-high) */ + regmap_update_bits(mpm_map, iso, ISO_RESTORE_FF, ISO_RESTORE_FF); + /* Step 7: Freeze outputs */ + regmap_update_bits(mpm_map, iso, ISO_FREEZE_OUTPUT, ISO_FREEZE_OUTPUT); + + /* Step 8: Turn off regulator */ + if (regulators[i]) { + ret =3D regulator_disable(regulators[i]); + if (ret) + dev_err(drv->dev, "Failed to disable %s rail: %d\n", + i =3D=3D 0 ? "CX" : "MX", ret); + else + usleep_range(8000, 10000); + } + } + + dev_dbg(drv->dev, "Isolation sequence complete (%s mode)\n", + num_rails > 1 ? "FULL_PC" : "LONG_APCR"); +} + +/** + * cdsp_execute_restoration_sequence() - Execute 9-step restoration sequen= ce + * @drv: Driver context + * + * Executes the restoration sequence for power restore as defined in + * the MPM power collapse specification. + * FULL_PC: Restores MXC (MX) rail first, then NSP (CX) rail + * LONG_APCR: Restores NSP (CX) rail only + */ +static void cdsp_execute_restoration_sequence(struct cdsp_power_driver *dr= v) +{ + /* + * When vdd_mx is absent, always restore both CX and MX rails + * (FULL_PC), but skip regulator_enable for MX (no handle). + */ + int start_rail =3D (!drv->vdd_mx || cdsp_is_full_pc_mode(drv)) ? 0 : 1; + unsigned int iso_regs[] =3D { + VDD_RAIL_ISO_CTRL(VDD_RAIL_MX), /* MXC/MX rail - FULL_PC only, restored= first */ + VDD_RAIL_ISO_CTRL(VDD_RAIL_CX), /* NSP/CX rail - always restored */ + }; + struct regulator *regulators[] =3D { drv->vdd_mx, drv->vdd_cx }; + struct regmap *mpm_map =3D drv->mpm_regmap; + static const char * const rail_names[] =3D { "MX", "CX" }; + int ret, i; + + for (i =3D start_rail; i < ARRAY_SIZE(iso_regs); i++) { + unsigned int iso =3D iso_regs[i]; + + /* Step 1: Assert power-up reset */ + regmap_update_bits(mpm_map, iso, ISO_PWR_UP_RESET, ISO_PWR_UP_RESET); + + /* Step 2: Enable power rail while reset is asserted (skip if no handle)= */ + if (regulators[i]) { + ret =3D regulator_enable(regulators[i]); + if (ret) + dev_err(drv->dev, "Failed to enable %s: %d\n", rail_names[i], ret); + else + usleep_range(8000, 10000); + } + + /* Step 3: Clear power-up reset */ + regmap_update_bits(mpm_map, iso, ISO_PWR_UP_RESET, 0); + /* Step 4: Restore from balloon latch */ + regmap_update_bits(mpm_map, iso, ISO_RESTORE_FF, 0); + /* Step 5: Remove memory clamp */ + regmap_update_bits(mpm_map, iso, ISO_CLAMP_MEM, 0); + /* Step 6: Restore from 0-pin retention */ + regmap_update_bits(mpm_map, iso, ISO_RET_0PIN, 0); + /* Step 7: Remove output freeze */ + regmap_update_bits(mpm_map, iso, ISO_FREEZE_OUTPUT, 0); + /* Step 8: Remove input isolation */ + regmap_update_bits(mpm_map, iso, ISO_INPUT | ISO_INPUT_DFT | ISO_INPUT_C= LKS, 0); + /* Step 9: Enable clocks */ + regmap_update_bits(mpm_map, iso, ISO_CLK_DIS, 0); + } + + dev_dbg(drv->dev, "Restoration sequence complete (%s mode)\n", + start_rail =3D=3D 0 ? "FULL_PC" : "LONG_APCR"); +} + +/** + * cdsp_lpm_shutdown_sequence() - Execute LPM shutdown sequence + * @drv: Driver context + * + * Handles power collapse request from CDSP: + * 1. Completes MPM 4-phase handshake + * 2. Executes isolation sequence + * 3. Disables regulators + * + * The regulator_disable() calls here are balanced against the + * regulator_enable() calls made by PAS remoteproc via the virtual + * vdd-cx/vdd-mx regulators (which pass through to vdd_cx/vdd_mx). + */ +static void cdsp_lpm_shutdown_sequence(struct cdsp_power_driver *drv) +{ + struct regmap *mpm_map =3D drv->mpm_regmap; + bool is_full_pc =3D cdsp_is_full_pc_mode(drv); + u32 hdshk_status; + int ret; + + dev_dbg(drv->dev, "LPM: Starting power collapse sequence\n"); + + /* Phase 1: Acknowledge shutdown request */ + regmap_update_bits(mpm_map, CLIENT_RSC_HDSHK(0), MPM_SHUTDOWN_ACK, MPM_SH= UTDOWN_ACK); + + /* Phase 2: Wait for request de-assertion */ + ret =3D regmap_read_poll_timeout(mpm_map, CLIENT_RSC_HDSHK(0), hdshk_stat= us, + !(hdshk_status & MPM_SHUTDOWN_REQ), + MPM_POLL_SLEEP_US, MPM_POLL_TIMEOUT_US); + if (ret) { + dev_err(drv->dev, "Timeout waiting for shutdown_req clear\n"); + return; + } + + /* Phase 3: Clear acknowledgment */ + regmap_update_bits(mpm_map, CLIENT_RSC_HDSHK(0), MPM_SHUTDOWN_ACK, 0x0); + + /* Phase 4: Execute isolation sequence */ + cdsp_execute_isolation_sequence(drv); + + atomic_set(&drv->power_state, CDSP_POWER_OFF); + + dev_dbg(drv->dev, "LPM: Power collapse complete (%s mode)\n", + is_full_pc ? "FULL_PC" : "LONG_APCR"); +} + +/** + * cdsp_lpm_restore_sequence() - Execute LPM restore sequence + * @drv: Driver context + * + * Handles power restore request from CDSP: + * 1. Executes restoration sequence (includes regulator enable) + * 2. Completes MPM bringup handshake + */ +static void cdsp_lpm_restore_sequence(struct cdsp_power_driver *drv) +{ + struct regmap *mpm_map =3D drv->mpm_regmap; + u32 hdshk_status; + int ret; + + dev_dbg(drv->dev, "LPM: Starting power restore sequence\n"); + + /* Execute restoration sequence (includes regulator enable) */ + cdsp_execute_restoration_sequence(drv); + + /* Assert BRINGUP_ACK */ + regmap_update_bits(mpm_map, CLIENT_RSC_HDSHK(0), MPM_BRINGUP_ACK, MPM_BRI= NGUP_ACK); + + /* Wait for BRINGUP_REQ to de-assert */ + ret =3D regmap_read_poll_timeout(mpm_map, CLIENT_RSC_HDSHK(0), hdshk_stat= us, + !(hdshk_status & MPM_BRINGUP_REQ), + MPM_POLL_SLEEP_US, MPM_POLL_TIMEOUT_US); + if (ret) { + dev_err(drv->dev, "Timeout waiting for BRINGUP_REQ clear\n"); + return; + } + + /* Clear BRINGUP_ACK */ + regmap_update_bits(mpm_map, CLIENT_RSC_HDSHK(0), MPM_BRINGUP_ACK, 0); + + atomic_set(&drv->power_state, CDSP_POWER_ON); + + dev_dbg(drv->dev, "LPM: Power restore complete\n"); +} + +/** + * cdsp_lpm_irq_handler() - LPM interrupt handler + * @irq: Interrupt number + * @data: Driver context + * + * Called when CDSP sends an LPM request via MPM. + * Schedules work to process the request. + * + * Return: IRQ_HANDLED + */ +static irqreturn_t cdsp_lpm_irq_handler(int irq, void *data) +{ + struct cdsp_power_driver *drv =3D data; + unsigned int client_rsc_hdsk_irq_val; + unsigned int rsc_hdsk_irq_val; + + regmap_read(drv->mpm_regmap, RSC_HDSHK_IRQ_STAT, &rsc_hdsk_irq_val); + regmap_read(drv->mpm_regmap, CLIENT_RSC_IRQ_STAT(0), &client_rsc_hdsk_irq= _val); + if (!client_rsc_hdsk_irq_val || !rsc_hdsk_irq_val) + return IRQ_NONE; + + /* Schedule work to process LPM request */ + queue_work(drv->lpm_wq, &drv->lpm_work); + + return IRQ_HANDLED; +} + +/** + * cdsp_lpm_work_fn() - LPM work function + * @work: Work structure + * + * Processes LPM requests from CDSP by reading MPM handshake status + * and executing appropriate shutdown or restore sequence. + */ +static void cdsp_lpm_work_fn(struct work_struct *work) +{ + struct cdsp_power_driver *drv =3D container_of(work, + struct cdsp_power_driver, + lpm_work); + u32 hdshk_status; + bool is_shutdown; + + mutex_lock(&drv->lock); + + /* Read MPM handshake status */ + regmap_read(drv->mpm_regmap, CLIENT_RSC_HDSHK(0), &hdshk_status); + + /* Clear the interrupt */ + regmap_write(drv->mpm_regmap, CLIENT_RSC_IRQ_CLR(0), 0x0); + regmap_write(drv->mpm_regmap, CLIENT_RSC_IRQ_CLR(0), 0x1); + regmap_write(drv->mpm_regmap, CLIENT_RSC_IRQ_CLR(0), 0x0); + + /* Determine if this is shutdown or bringup */ + is_shutdown =3D !!(hdshk_status & MPM_SHUTDOWN_REQ); + + if (is_shutdown) { + if (atomic_read(&drv->power_state) =3D=3D CDSP_POWER_OFF) { + dev_warn(drv->dev, "Spurious shutdown request, already powered off\n"); + goto out_unlock; + } + cdsp_lpm_shutdown_sequence(drv); + } else { + if (atomic_read(&drv->power_state) =3D=3D CDSP_POWER_ON) { + dev_warn(drv->dev, "Spurious bringup request, already powered on\n"); + goto out_unlock; + } + cdsp_lpm_restore_sequence(drv); + } + +out_unlock: + mutex_unlock(&drv->lock); +} + +static const struct regmap_config cdsp_rscc_regmap_config =3D { + .reg_bits =3D 32, + .reg_stride =3D 4, + .val_bits =3D 32, + .max_register =3D 0x4, + .fast_io =3D true, +}; + +static const struct regmap_config cdsp_regmap_config =3D { + .reg_bits =3D 32, + .reg_stride =3D 4, + .val_bits =3D 32, + .max_register =3D 0x1000, + .fast_io =3D true, +}; + +/** + * cdsp_power_probe() - Probe the CDSP power management driver + * @pdev: Platform device + * + * Acquires the PMIC regulator consumer handles, registers the virtual + * cdsp-vdd-cx (and optionally cdsp-vdd-mx) regulator providers, maps the + * MPM and RSCC register regions, and registers the DCVS and LPM interrupt + * handlers. + * + * Return: 0 on success, negative error code on failure + */ +static int cdsp_power_probe(struct platform_device *pdev) +{ + struct regulator_config virt_cfg =3D {}; + struct cdsp_power_driver *drv; + struct regulator_dev *rdev; + void __iomem *rscc_base; + void __iomem *mpm_base; + size_t smem_size; + u32 smem_id; + int ret; + + /* Allocate driver context */ + drv =3D devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->dev =3D &pdev->dev; + mutex_init(&drv->lock); + atomic_set(&drv->power_state, CDSP_POWER_ON); + + /* Get SMEM item ID from device tree */ + ret =3D of_property_read_u32(pdev->dev.of_node, "qcom,smem-item", &smem_i= d); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to get SMEM item ID\n"); + + /* Create SMEM entry for DCVS */ + ret =3D qcom_smem_alloc(CDSP_SMEM_NSP_HOST_ID, smem_id, CDSP_SMEM_SIZE); + if (ret && ret !=3D -EEXIST) + return dev_err_probe(&pdev->dev, ret, "Failed to allocate SMEM\n"); + + /* Get SMEM pointer and validate size */ + drv->smem =3D qcom_smem_get(CDSP_SMEM_NSP_HOST_ID, smem_id, &smem_size); + if (IS_ERR(drv->smem)) + return dev_err_probe(&pdev->dev, PTR_ERR(drv->smem), + "Failed to get SMEM\n"); + + if (smem_size < CDSP_SMEM_SIZE) + return dev_err_probe(&pdev->dev, -EINVAL, + "SMEM region too small: got %zu, expected %u\n", + smem_size, CDSP_SMEM_SIZE); + + /* + * Initialise the SMEM channel header. + * Zero the entire region first so all padding and reserved fields + * are clean, then fill in the fixed protocol fields. + * apss_state is set to 1 last (after wmb) so NSP Q6 only sees a + * fully-populated header once APSS is ready. + */ + memset(drv->smem, 0, sizeof(*drv->smem)); + drv->smem->hdr.magic =3D CDSP_SMEM_MAGIC; + drv->smem->hdr.version =3D CDSP_SMEM_VERSION; + drv->smem->hdr.request_offset =3D CDSP_SMEM_REQUEST_OFFSET; + drv->smem->hdr.request_size =3D CDSP_SMEM_REQUEST_SIZE; + drv->smem->hdr.response_offset =3D CDSP_SMEM_RESPONSE_OFFSET; + drv->smem->hdr.response_size =3D CDSP_SMEM_RESPONSE_SIZE; + /* Signal APSS readiness to NSP Q6 */ + WRITE_ONCE(drv->smem->hdr.apss_state, 1); + /* Ensure SMEM header is fully written before NSP Q6 reads it */ + wmb(); + + /* + * Get voltage regulator consumer handles. + * These are the actual NSP_CX and NSP_MX voltage rails. + * The virtual regulator ops pass through to these handles. + */ + drv->vdd_cx =3D devm_regulator_get(&pdev->dev, "vdd-cx"); + if (IS_ERR(drv->vdd_cx)) + return dev_err_probe(&pdev->dev, PTR_ERR(drv->vdd_cx), + "Failed to get vdd-cx regulator\n"); + + drv->vdd_mx =3D devm_regulator_get_optional(&pdev->dev, "vdd-mx"); + if (IS_ERR(drv->vdd_mx)) { + if (PTR_ERR(drv->vdd_mx) !=3D -ENODEV) + return dev_err_probe(&pdev->dev, PTR_ERR(drv->vdd_mx), + "Failed to get vdd-mx regulator\n"); + drv->vdd_mx =3D NULL; + dev_dbg(&pdev->dev, "No vdd-mx regulator, MX rail absent on this board\n= "); + } + + /* + * Register virtual regulator provider. + * + * Expose vdd-cx and vdd-mx virtual regulators so that PAS remoteproc + * can consume them via cx-supply / mx-supply DTS properties. + * The enable/disable ops pass through to vdd_cx / vdd_mx above, + * making CDSP the sole hardware power manager for the NSP subsystem. + */ + virt_cfg.dev =3D &pdev->dev; + virt_cfg.driver_data =3D drv; + virt_cfg.of_node =3D pdev->dev.of_node; + + INIT_WORK(&drv->dcvs_work, cdsp_dcvs_work_fn); + INIT_WORK(&drv->lpm_work, cdsp_lpm_work_fn); + + drv->lpm_wq =3D alloc_ordered_workqueue("cdsp_lpm_wq", 0); + if (!drv->lpm_wq) { + mbox_free_channel(drv->dcvs_mbox_chan); + return dev_err_probe(&pdev->dev, + -ENOMEM, + "failed to allocate cdsp lpm workqueue\n"); + } + + rdev =3D devm_regulator_register(&pdev->dev, + &cdsp_virt_reg_descs[CDSP_VIRT_NSP_CX], + &virt_cfg); + if (IS_ERR(rdev)) + return dev_err_probe(&pdev->dev, PTR_ERR(rdev), + "Failed to register cdsp-vdd-cx virtual regulator\n"); + + if (drv->vdd_mx) { + rdev =3D devm_regulator_register(&pdev->dev, + &cdsp_virt_reg_descs[CDSP_VIRT_NSP_MX], + &virt_cfg); + if (IS_ERR(rdev)) + return dev_err_probe(&pdev->dev, PTR_ERR(rdev), + "Failed to register cdsp-vdd-mx virtual regulator\n"); + } + + /* Register DCVS interrupt */ + drv->dcvs_irq =3D platform_get_irq_byname(pdev, "dcvs"); + if (drv->dcvs_irq < 0) + return dev_err_probe(&pdev->dev, drv->dcvs_irq, + "Failed to get DCVS IRQ\n"); + + ret =3D devm_request_threaded_irq(&pdev->dev, drv->dcvs_irq, + NULL, cdsp_dcvs_irq_handler, + IRQF_ONESHOT, "cdsp-dcvs", drv); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request DCVS IRQ\n"); + + /* Setup MPM for LPM */ + mpm_base =3D devm_platform_ioremap_resource_byname(pdev, "mpm"); + if (IS_ERR(mpm_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(mpm_base), + "Failed to map MPM registers\n"); + + drv->mpm_regmap =3D devm_regmap_init_mmio(&pdev->dev, mpm_base, &cdsp_reg= map_config); + if (IS_ERR(drv->mpm_regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(drv->mpm_regmap), + "Failed to init MPM regmap\n"); + + /* Setup RSCC for power mode detection */ + rscc_base =3D devm_platform_ioremap_resource_byname(pdev, "rscc"); + if (IS_ERR(rscc_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(rscc_base), + "Failed to map RSCC registers\n"); + + drv->rscc_regmap =3D devm_regmap_init_mmio(&pdev->dev, rscc_base, &cdsp_r= scc_regmap_config); + if (IS_ERR(drv->rscc_regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(drv->rscc_regmap), + "Failed to init RSCC regmap\n"); + + drv->lpm_irq =3D platform_get_irq_byname(pdev, "lpm"); + if (drv->lpm_irq < 0) + return dev_err_probe(&pdev->dev, drv->lpm_irq, + "Failed to get LPM IRQ\n"); + + ret =3D devm_request_threaded_irq(&pdev->dev, drv->lpm_irq, + NULL, cdsp_lpm_irq_handler, + IRQF_ONESHOT, "cdsp-lpm", drv); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request LPM IRQ\n"); + + /* Setup mbox for DCVS response */ + drv->dcvs_mbox_client.dev =3D &pdev->dev; + drv->dcvs_mbox_client.knows_txdone =3D true; + drv->dcvs_mbox_chan =3D mbox_request_channel(&drv->dcvs_mbox_client, 0); + if (IS_ERR(drv->dcvs_mbox_chan)) + return dev_err_probe(&pdev->dev, PTR_ERR(drv->dcvs_mbox_chan), + "Failed to get dcvs mbox channel\n"); + + platform_set_drvdata(pdev, drv); + + dev_dbg(&pdev->dev, "CDSP power driver initialized\n"); + + return 0; +} + +static void cdsp_power_remove(struct platform_device *pdev) +{ + struct cdsp_power_driver *drv =3D platform_get_drvdata(pdev); + + /* Cancel any pending work */ + cancel_work_sync(&drv->dcvs_work); + cancel_work_sync(&drv->lpm_work); + + if (drv->lpm_wq) + destroy_workqueue(drv->lpm_wq); + + mbox_free_channel(drv->dcvs_mbox_chan); +} + +static const struct of_device_id cdsp_power_of_match[] =3D { + { .compatible =3D "qcom,cdsp-power" }, + { } +}; +MODULE_DEVICE_TABLE(of, cdsp_power_of_match); + +static struct platform_driver cdsp_power_driver =3D { + .probe =3D cdsp_power_probe, + .remove =3D cdsp_power_remove, + .driver =3D { + .name =3D "cdsp-power", + .of_match_table =3D cdsp_power_of_match, + }, +}; + +module_platform_driver(cdsp_power_driver); + +MODULE_DESCRIPTION("CDSP Power Management Driver"); +MODULE_LICENSE("GPL"); --=20 2.43.0