From nobody Mon Jun 8 09:48:06 2026 Received: from outbound8.mail.transip.nl (outbound8.mail.transip.nl [136.144.136.8]) (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 C901E2080C1; Sat, 30 May 2026 14:08:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.8 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780150085; cv=none; b=CKoLcu+9HNhELj0jDSIsMy+Mq2B5LhF7i59zBiOTutahb26n/AFUtc1anonaYa3w2P8XxtLRi8L7fIzQV6JEGTYv3B3QsqSqypoTJgn8SHB/4YX6GJjqrF8vocAmm8qZImmM+tCwWNRdRWZd9QO1PzgHtbCC6DOAsmNE5PpBEX0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780150085; c=relaxed/simple; bh=w5mR+H5rPyVbSgV+adhHaSX2uDpuYP4IW4V5S+BD9ms=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VwqpR7OKE2RC5/2k106E5bIaDrCyDC9UNb/7Fm7ShG/5TVa3V7hFfKZLZF07ebI3mCYQLoiPFjLeaTw5UykbmwtxlK0UJl6/cZ50G36COe10nVVGyluLwuzwOyXYT60czFiOCsreEX4oL/lNlHK2zdI8TWhWKJ7y7ZqwQN1eX14= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=T1zCa6H1; arc=none smtp.client-ip=136.144.136.8 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="T1zCa6H1" Received: from submission14.mail.transip.nl (unknown [10.103.8.165]) by outbound8.mail.transip.nl (Postfix) with ESMTP id 4gSMLP5NQrzY76Kg; Sat, 30 May 2026 16:00:13 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission14.mail.transip.nl (Postfix) with ESMTPA id 4gSMLP1vjgz3RgQXr; Sat, 30 May 2026 16:00:13 +0200 (CEST) From: Herman van Hazendonk To: Conor Dooley , devicetree@vger.kernel.org, Georgi Djakov , Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Rob Herring Subject: [PATCH 1/2] dt-bindings: interconnect: qcom: add msm8660 fabric IDs Date: Sat, 30 May 2026 16:00:11 +0200 Message-ID: <4f08904fd8e8279abfacde03a0f7663dec674c74.1780148149.git.github.com@herrie.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: ClueGetter at submission14.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149613; h=from:subject:to:references: in-reply-to:date:mime-version; bh=WHmruaTq2s12LtiMejU0BWfYOqcgbgyX8/Nk2hvR4y8=; b=T1zCa6H13pTUH+udukzI4AqhF2RypCqiu4Q9BIGWPYkXASA3PlHDrAliVUH0LnPsXPh8EO aVo7mpnuXX1la4DdHs/8+P6dUUISdDJ+xi9lopJdXe3ImP8ouy6IAOl9R2QlLCLa53ySkf cZbFobFPckb73RU03eEsXNFBSjmX+evH1eNWmRxqyBgXo/96tg51FfuhbU0FfVGkiq084p 9D+glouXNC44oDTTofOwVKGwuy2Fa7DcmzLlHvcKKm8TXXN7sjtpBExVLSbTPCQOWr7WeZ aQkHr+i1trXolBrONgqIyHZ/SOikxTZpqF5Gx8l+lerPHEk+faLRzja+ge5W4w== X-Report-Abuse-To: abuse@transip.nl Content-Type: text/plain; charset="utf-8" Add the dt-binding interconnect master/slave ID header for the MSM8x60 family (MSM8260/MSM8660/APQ8060) fabric mesh. The chip's NoC fabric is split into multiple sub-fabrics that the qnoc-msm8660 driver models: AFAB - Applications fabric (Scorpion CPU + L2) SFAB - System fabric (DMA, SPS, security) MMFAB - Multimedia fabric (MDP, GPU, camera, video, rotator) DFAB - Daytona fabric (SDC, ADM master/slave) SFPB - System Fast Peripheral Bridge (RPM, MPM, PMIC SSBI) CFPB - CPU Subsystem Fast Peripheral Bus (GSBI UART/QUP, USB FS, TSIF, TSSC, PDM, PRNG) IDs derived from the legacy webOS msm_bus_board_8660.c master/slave enums, normalised to the upstream interconnect-framework naming. Signed-off-by: Herman van Hazendonk --- .../dt-bindings/interconnect/qcom,msm8660.h | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 include/dt-bindings/interconnect/qcom,msm8660.h diff --git a/include/dt-bindings/interconnect/qcom,msm8660.h b/include/dt-b= indings/interconnect/qcom,msm8660.h new file mode 100644 index 000000000000..d2639f737a54 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8660.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Qualcomm MSM8x60 family (MSM8260/MSM8660/APQ8060) interconnect IDs + * + * Copyright (c) 2026, Herman van Hazendonk + * + * Based on webOS kernel msm_bus_board_8660.c + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8660_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8660_H + +/* + * MSM8x60 has a fabric-based bus architecture: + * - APPSS Fabric: CPU and memory interface + * - System Fabric: System peripherals and DMA + * - MMSS Fabric: Multimedia subsystem (display, camera, video) + * - Daytona Fabric: Peripheral bus (SDCC, ADM DMA) + * - System FPB: System Fast Peripheral Bus + * - CPSS FPB: CPU Subsystem Fast Peripheral Bus + */ + +/* APPSS Fabric - Apps processor fabric */ +#define AFAB_MAS_AMPSS_M0 0 +#define AFAB_MAS_AMPSS_M1 1 +#define AFAB_SLV_EBI_CH0 2 +#define AFAB_SLV_AMPSS_L2 3 +#define AFAB_TO_MMSS 4 +#define AFAB_TO_SYSTEM 5 + +/* System Fabric - System bus */ +#define SFAB_MAS_APPSS 0 +#define SFAB_MAS_SPS 1 +#define SFAB_MAS_ADM0_PORT0 2 +#define SFAB_MAS_ADM0_PORT1 3 +#define SFAB_MAS_ADM1_PORT0 4 +#define SFAB_MAS_ADM1_PORT1 5 +#define SFAB_MAS_LPASS_PROC 6 +#define SFAB_MAS_MSS_PROCI 7 +#define SFAB_MAS_MSS_PROCD 8 +#define SFAB_MAS_MSS_MDM_PORT0 9 +#define SFAB_MAS_LPASS 10 +#define SFAB_MAS_MMSS_FPB 11 +#define SFAB_MAS_ADM1_CI 12 +#define SFAB_MAS_ADM0_CI 13 +#define SFAB_MAS_MSS_MDM_PORT1 14 +#define SFAB_MAS_USB_HS 15 +#define SFAB_TO_APPSS 16 +#define SFAB_TO_SYSTEM_FPB 17 +#define SFAB_TO_CPSS_FPB 18 +#define SFAB_SLV_SPS 19 +#define SFAB_SLV_SYSTEM_IMEM 20 +#define SFAB_SLV_AMPSS 21 +#define SFAB_SLV_MSS 22 +#define SFAB_SLV_LPASS 23 +#define SFAB_SLV_MMSS_FPB 24 +#define SFAB_TO_DFAB 25 + +/* MMSS Fabric - Multimedia subsystem */ +#define MMFAB_MAS_MDP_PORT0 0 +#define MMFAB_MAS_MDP_PORT1 1 +#define MMFAB_MAS_ADM1_PORT0 2 +#define MMFAB_MAS_ROTATOR 3 +#define MMFAB_MAS_GRAPHICS_3D 4 +#define MMFAB_MAS_JPEG_DEC 5 +#define MMFAB_MAS_GRAPHICS_2D_CORE0 6 +#define MMFAB_MAS_VFE 7 +#define MMFAB_MAS_VPE 8 +#define MMFAB_MAS_JPEG_ENC 9 +#define MMFAB_MAS_GRAPHICS_2D_CORE1 10 +#define MMFAB_MAS_HD_CODEC_PORT0 11 +#define MMFAB_MAS_HD_CODEC_PORT1 12 +#define MMFAB_TO_APPSS 13 +#define MMFAB_SLV_SMI 14 +#define MMFAB_SLV_MM_IMEM 15 + +/* + * Daytona Fabric (DFAB) - Peripheral bus + * + * DFAB connects slower peripherals (SDCC, ADM DMA) to the system fabric. + * The webOS kernel managed DFAB bandwidth via voter clocks (dfab_sdc*_clk, + * dfab_usb_hs_clk). In mainline, this is handled by the interconnect fram= ework. + * + * USB HS is included as a DFAB voter for compatibility with the legacy cl= ock + * voting mechanism. The webOS kernel comment said: "if usb link is in sps + * there is no need for usb pclk as daytona fabric clock will be used inst= ead". + * This keeps DFAB clock stable when USB is active. + */ +#define DFAB_MAS_SDC1 0 +#define DFAB_MAS_SDC2 1 +#define DFAB_MAS_SDC3 2 +#define DFAB_MAS_SDC4 3 +#define DFAB_MAS_SDC5 4 +#define DFAB_MAS_ADM0_MASTER 5 +#define DFAB_MAS_ADM1_MASTER 6 +#define DFAB_TO_SFAB 7 +#define DFAB_SLV_SDC1 8 +#define DFAB_SLV_SDC2 9 +#define DFAB_SLV_SDC3 10 +#define DFAB_SLV_SDC4 11 +#define DFAB_SLV_SDC5 12 +#define DFAB_MAS_USB_HS 13 +#define DFAB_MAS_DSPS 14 + +/* System FPB - Slow peripheral bus for system */ +#define SFPB_MAS_SYSTEM 0 +#define SFPB_MAS_SPDM 1 +#define SFPB_MAS_RPM 2 +#define SFPB_SLV_SPDM 3 +#define SFPB_SLV_RPM 4 +#define SFPB_SLV_RPM_MSG_RAM 5 +#define SFPB_SLV_MPM 6 +#define SFPB_SLV_PMIC1_SSBI1_A 7 +#define SFPB_SLV_PMIC1_SSBI1_B 8 +#define SFPB_SLV_PMIC1_SSBI1_C 9 +#define SFPB_SLV_PMIC2_SSBI2_A 10 +#define SFPB_SLV_PMIC2_SSBI2_B 11 + +/* CPSS FPB - CPU subsystem fast peripheral bus */ +#define CFPB_MAS_SYSTEM 0 +#define CFPB_SLV_GSBI1_UART 1 +#define CFPB_SLV_GSBI2_UART 2 +#define CFPB_SLV_GSBI3_UART 3 +#define CFPB_SLV_GSBI4_UART 4 +#define CFPB_SLV_GSBI5_UART 5 +#define CFPB_SLV_GSBI6_UART 6 +#define CFPB_SLV_GSBI7_UART 7 +#define CFPB_SLV_GSBI8_UART 8 +#define CFPB_SLV_GSBI9_UART 9 +#define CFPB_SLV_GSBI10_UART 10 +#define CFPB_SLV_GSBI11_UART 11 +#define CFPB_SLV_GSBI12_UART 12 +#define CFPB_SLV_GSBI1_QUP 13 +#define CFPB_SLV_GSBI2_QUP 14 +#define CFPB_SLV_GSBI3_QUP 15 +#define CFPB_SLV_GSBI4_QUP 16 +#define CFPB_SLV_GSBI5_QUP 17 +#define CFPB_SLV_GSBI6_QUP 18 +#define CFPB_SLV_GSBI7_QUP 19 +#define CFPB_SLV_GSBI8_QUP 20 +#define CFPB_SLV_GSBI9_QUP 21 +#define CFPB_SLV_GSBI10_QUP 22 +#define CFPB_SLV_GSBI11_QUP 23 +#define CFPB_SLV_GSBI12_QUP 24 +#define CFPB_SLV_EBI2_NAND 25 +#define CFPB_SLV_USB_FS1 26 +#define CFPB_SLV_USB_FS2 27 +#define CFPB_SLV_TSIF 28 +#define CFPB_SLV_MSM_TSSC 29 +#define CFPB_SLV_MSM_PDM 30 +#define CFPB_SLV_MSM_DIMEM 31 +#define CFPB_SLV_MSM_TCSR 32 +#define CFPB_SLV_MSM_PRNG 33 + +#endif /* __DT_BINDINGS_INTERCONNECT_QCOM_MSM8660_H */ --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound1.mail.transip.nl (outbound1.mail.transip.nl [149.210.149.72]) (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 AF70E17A2F6; Sat, 30 May 2026 14:03:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=149.210.149.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149826; cv=none; b=grqcXh7MTPuZ1Xdto+faM4xIhrjHb9d6JRU506D9dz6DJ4ZarCt+DdW7bo3EirZvKvm6b2kHgxRiPI2LiVFv+TXTERMWZxtsaMu41a014f44Swg7CgRtqVB4twSf5oTQTD2PrUQNpiilpOZUmtf6dBGltLyoHya6Ct5bruojcQg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149826; c=relaxed/simple; bh=7GVEU4din37FjDdBmZSuCuPSKPUqbKeNE/zhEIn25T8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qUoQbGm+J7O8jn6YuYs1/GShWvUicfupoC23N3X65yHXwif3YWkAzxTSLF8Rl+CeF45p59UgKc91qn7Sj0k1ZISoQxDwQC3HJV4p8oIbQBtTUXtH3L2MJfqTYuUzphb3LcESvSqyYuJ4JnsrhBm2ADMbgQj7RSOn+M58Z89REb8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=Cx6EN28M; arc=none smtp.client-ip=149.210.149.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="Cx6EN28M" Received: from submission5.mail.transip.nl (unknown [10.103.8.156]) by outbound1.mail.transip.nl (Postfix) with ESMTP id 4gSMJH0vP8zRjSp; Sat, 30 May 2026 15:58:23 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission5.mail.transip.nl (Postfix) with ESMTPA id 4gSMJG3zntz2pRDjH; Sat, 30 May 2026 15:58:22 +0200 (CEST) From: Herman van Hazendonk To: Bjorn Andersson , Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-clk@vger.kernel.org, devicetree@vger.kernel.org Cc: Herman van Hazendonk Subject: [PATCH 1/3] dt-bindings: clock: qcom: add mmcc-msm8660 clock IDs Date: Sat, 30 May 2026 15:58:10 +0200 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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 X-Scanned-By: ClueGetter at submission5.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149502; h=from:subject:to:cc: references:in-reply-to:date:mime-version:content-type; bh=S+oVwmky282VbH+PAA7JnEpBKm9iw52aNdvDCKXBJ+w=; b=Cx6EN28MXFjlqnFZq/KMHlHO6f0T5l9vIwlYokX3IV4Ru1jJGRMwbNeMPMI9QxNJxYAM0C 5S4cVVu+kfmFGRSTqba20e6fUKH8Bt3SI3nngqV6eaXHeuhHkoGOpPVpoaClRZjl6myo8J mfotERtaanUj9P3OQ8smVRcfFC6mifjrfKhXtnFasbT7ab3URJAVz51vi06PliV7Se975F adJIkT5/Bm9zkZGaWK83Kyerabk39e/OVnI0v3tUKMznppxxL7dySJ0Ra/JkIASsPuLTeQ xEIFj1PL9Tfr6HRs5Cxl2U1e5W948L2J8ZKtIhyNAICCQZzx+BEIs8X1sW0WnQ== X-Report-Abuse-To: abuse@transip.nl Add the dt-binding clock-ID header for the MSM8x60 family (MSM8260/MSM8660/APQ8060) Multimedia Clock Controller (MMCC). The header enumerates the clocks and power-domains consumed by the multimedia subsystem (MDP4 display, Adreno A220 GPU, CAMSS image pipeline, VFE, Gemini JPEG, video codec, rotator, VPE and the GFX2D Z180 cores). IDs intentionally match the numeric values used by the original shared mmcc-msm8960.h so the driver's clk array indexing is preserved; only the clocks actually implemented by mmcc-msm8660.c are defined. Signed-off-by: Herman van Hazendonk --- include/dt-bindings/clock/qcom,mmcc-msm8660.h | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,mmcc-msm8660.h diff --git a/include/dt-bindings/clock/qcom,mmcc-msm8660.h b/include/dt-bin= dings/clock/qcom,mmcc-msm8660.h new file mode 100644 index 000000000000..00c3a75e8b71 --- /dev/null +++ b/include/dt-bindings/clock/qcom,mmcc-msm8660.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Clock and power-domain bindings for the MSM8x60 family (MSM8260/MSM8660= /APQ8060) + * Multimedia Clock Controller (MMCC). + * + * MSM8260, MSM8660 and APQ8060 are the same Scorpion-class MSM8x60 SoC + * with different bin/feature labels. MSM8960 is a newer generation (Krait) + * =E2=80=94 its bindings live in + * and must not be reused here. + * + * IDs below intentionally match the numeric values used by the original + * shared mmcc-msm8960.h so the driver's clk array indexing is preserved; + * only the clocks actually implemented by mmcc-msm8660.c are defined. + */ + +#ifndef _DT_BINDINGS_CLK_MSM_MMCC_8660_H +#define _DT_BINDINGS_CLK_MSM_MMCC_8660_H + +#define TV_ENC_AHB_CLK 3 +#define AMP_AHB_CLK 4 +#define JPEGD_AHB_CLK 6 +#define GFX2D0_AHB_CLK 7 +#define DSI_S_AHB_CLK 8 +#define VPE_AHB_CLK 10 +#define SMMU_AHB_CLK 11 +#define HDMI_M_AHB_CLK 12 +#define VFE_AHB_CLK 13 +#define ROT_AHB_CLK 14 +#define VCODEC_AHB_CLK 15 +#define MDP_AHB_CLK 16 +#define DSI_M_AHB_CLK 17 +#define CSI0_AHB_CLK 18 +#define MMSS_IMEM_AHB_CLK 19 +#define IJPEG_AHB_CLK 20 +#define HDMI_S_AHB_CLK 21 +#define GFX3D_AHB_CLK 22 +#define GFX2D1_AHB_CLK 23 +#define JPEGD_AXI_CLK 28 +#define GMEM_AXI_CLK 29 +#define MDP_AXI_CLK 30 +#define MMSS_IMEM_AXI_CLK 31 +#define IJPEG_AXI_CLK 32 +#define GFX3D_AXI_CLK 33 +#define VCODEC_AXI_CLK 34 +#define VFE_AXI_CLK 35 +#define VPE_AXI_CLK 36 +#define ROT_AXI_CLK 37 +#define VCODEC_AXI_A_CLK 38 +#define VCODEC_AXI_B_CLK 39 +#define CSI0_SRC 47 +#define CSI0_CLK 48 +#define CSI0_PHY_CLK 49 +#define CSI1_SRC 50 +#define CSI1_CLK 51 +#define CSI1_PHY_CLK 52 +#define DSI_SRC 56 +#define DSI_CLK 57 +#define CSI_PIX_CLK 58 +#define CSI_RDI_CLK 59 +#define MDP_VSYNC_CLK 60 +#define HDMI_APP_CLK 62 +#define GFX2D0_SRC 66 +#define GFX2D0_CLK 67 +#define GFX2D1_SRC 68 +#define GFX2D1_CLK 69 +#define GFX3D_SRC 70 +#define GFX3D_CLK 71 +#define IJPEG_SRC 72 +#define IJPEG_CLK 73 +#define JPEGD_SRC 74 +#define JPEGD_CLK 75 +#define MDP_SRC 76 +#define MDP_CLK 77 +#define MDP_LUT_CLK 78 +#define DSI1_BYTE_SRC 83 +#define DSI1_BYTE_CLK 84 +#define DSI1_ESC_SRC 87 +#define DSI1_ESC_CLK 88 +#define ROT_SRC 91 +#define ROT_CLK 92 +#define TV_ENC_CLK 93 +#define TV_DAC_CLK 94 +#define HDMI_TV_CLK 95 +#define MDP_TV_CLK 96 +#define TV_SRC 97 +#define VCODEC_SRC 98 +#define VCODEC_CLK 99 +#define VFE_SRC 100 +#define VFE_CLK 101 +#define VFE_CSI0_CLK 102 +#define VPE_SRC 103 +#define VPE_CLK 104 +#define DSI_PIXEL_SRC 105 +#define DSI_PIXEL_CLK 106 +#define CAMCLK0_SRC 107 +#define CAMCLK0_CLK 108 +#define CAMCLK1_SRC 109 +#define CAMCLK1_CLK 110 +#define CSIPHYTIMER_SRC 113 +#define CSIPHY1_TIMER_CLK 115 +#define CSIPHY0_TIMER_CLK 116 +#define PLL2 118 +#define MDP_PIXEL_SRC 129 +#define MDP_PIXEL_CLK 130 +#define MDP_LCDC_CLK 131 +#define VFE_CSI1_CLK 132 +#define CSI1_AHB_CLK 133 + +/* + * MSM8x60 legacy footswitch power domains. + * Used with the MMCC power-domain provider (#power-domain-cells =3D <1>). + * Numbering is independent of the clock ID space above. + */ +#define GFX2D0_GDSC 0 +#define GFX2D1_GDSC 1 +#define GFX3D_GDSC 2 +#define IJPEG_GDSC 3 +#define MDP_GDSC 4 +#define ROT_GDSC 5 +#define VED_GDSC 6 +#define VFE_GDSC 7 +#define VPE_GDSC 8 + +#endif --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound0.mail.transip.nl (outbound0.mail.transip.nl [149.210.149.69]) (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 C35FE3C2D; Sat, 30 May 2026 14:00:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=149.210.149.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149630; cv=none; b=Z0qVe2ICp7cGA9gWmNQZWUI1d2F6NrtKhylN2OR0cjGBu2AQZUp+HYdkw/KYO3ilX7bYFA2GKA/zSL+60XhHjs+DL458SObkR1t3Hs0qM06v+GM2gK5e5Aj6X4AYblt6iu4esXdn4yIa1+xg5r1N1WsUDYLXa4hpc1f6Og9UMtA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149630; c=relaxed/simple; bh=Yb+0/p09x1DfCW4pKBdS97kKZFb1oKkKoNJzx9qMEW0=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=DTjYGCZmkuzKnNzirUnTiMRPreVwVpon7IySAP8Vhb4KGgrDQwPVQLaH8Uvlc57lvdwI5awyycDyf2j7GW5qQMcFnCQ01Abh4TppGLnKOTfxVXuUEhpsigQDAZvpako+9G9nZ5GYqThnTTjVm+o0MqFwZ6R+hDXcU02iw3wm42s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=R2ZU+Zmh; arc=none smtp.client-ip=149.210.149.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="R2ZU+Zmh" Received: from submission13.mail.transip.nl (unknown [10.103.8.164]) by outbound0.mail.transip.nl (Postfix) with ESMTP id 4gSMLc6qdPzxNpt; Sat, 30 May 2026 16:00:24 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission13.mail.transip.nl (Postfix) with ESMTPA id 4gSMLc0wJ8z3fCxJ9; Sat, 30 May 2026 16:00:24 +0200 (CEST) From: Herman van Hazendonk To: Bjorn Andersson , Conor Dooley , devicetree@vger.kernel.org, Konrad Dybcio , Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, Rob Herring , Thomas Gleixner , van Hazendonk Subject: [PATCH 1/2] dt-bindings: interrupt-controller: qcom: add msm8660-mpm Date: Sat, 30 May 2026 16:00:22 +0200 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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 X-Scanned-By: ClueGetter at submission13.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149624; h=from:subject:to:references: in-reply-to:date:mime-version:content-type; bh=4zDvoJdsXNhZm0T2YJYAS1qLjeW2yZwyVMRRS0bJuW8=; b=R2ZU+ZmhuwNy1rWGfrPq6uIhOwqqx/ZHqKp+hqEUt4xtiRMHytTQkUL2ZkkbbleoQJyN6v yikgiJR1PWW9aMqmZOyGu6p9CGO9n4meHiQCNA+Z7FYVg/vGu4UJ5ahQYRQJ4+y6+0ixzT szPadFXqST6qeO1xGmKCeEMKK7GbMDhZY2TwQTCbYLFcFxZdR1TIi1t3S9rtbcFnaV3JjF 1KvyOg8iEBVUpdOAcX623qunnMLs4dMcEFKjp0j7OIYcZjESSqxQ1aWAEH+tNQLYNMaeW8 0xq5fNtbnPnATWJMRDSY0WnLjaT1Xz0luM9uO/2phld58OXcaJmCn0jGgRuDag== X-Report-Abuse-To: abuse@transip.nl Add the binding for the Modem Power Manager (MPM) interrupt controller on the MSM8x60 family (MSM8260/MSM8660/APQ8060). The MPM is a small wake-capable interrupt aggregator that lets the SoC stay in low-power states while a small set of GIC SPIs continues to be monitored and can wake the system on an edge. The binding describes its register region (in the RPM syscon), the GIC SPI it raises on a wake event, the GIC mailbox it shares with the RPM, and the per-pin wake-source mapping table. Signed-off-by: Herman van Hazendonk --- .../qcom,msm8660-mpm.yaml | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/= qcom,msm8660-mpm.yaml diff --git a/Documentation/devicetree/bindings/interrupt-controller/qcom,ms= m8660-mpm.yaml b/Documentation/devicetree/bindings/interrupt-controller/qco= m,msm8660-mpm.yaml new file mode 100644 index 000000000000..72f9c2be03b2 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/qcom,msm8660-m= pm.yaml @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/qcom,msm8660-mpm.y= aml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8x60 MPM wakeup interrupt controller + +maintainers: + - Herman van Hazendonk + +description: | + The MSM Power Manager (MPM) on the MSM8x60 family (MSM8260/MSM8660/ + APQ8060) is an always-on hardware block that keeps a set of wake + interrupts alive while the application processor is powered down. + On MSM8x60 the vMPM registers live INSIDE the RPM control block + (request regs at RPM_BASE + 0x9d8, status at RPM_BASE + 0xdf8), with + notification of new wake-source configuration delivered via the + qcom-apcs-ipc mailbox (writing GCC + 0x008 bit 1). + + This binding is MSM8x60-specific. Newer Qualcomm SoCs (sm6375, etc.) + use the qcom,mpm binding which assumes a dedicated MPM SRAM region + and an IPCC mailbox =E2=80=94 neither holds on MSM8x60. The driver lives= at + drivers/irqchip/irq-msm8660-mpm.c. + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + const: qcom,msm8660-mpm + + interrupts: + maxItems: 1 + description: + IPC IRQ raised by MPM when one of the enabled wake sources fires. + On MSM8x60 this is wired to GIC SPI 2. + + qcom,rpm-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the RPM control block exposed as a syscon. The vMPM + registers are accessed at offsets within this block. + + qcom,mpm-offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Offset of the vMPM register window within the RPM syscon. On + MSM8x60 this is 0x9d8. Status registers are at this offset + 0x420 + (=3D=3D 0xdf8 from RPM base). + + mboxes: + maxItems: 1 + description: + Mailbox channel used to notify MPM that the vMPM request + registers have been updated. On MSM8x60 this is the + qcom-apcs-ipc mailbox channel 1 (writes GCC + 0x008 bit 1). + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + description: + First cell is the MPM pin number / parent GIC SPI; second cell + is the trigger type. + + qcom,mpm-pin-count: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Total number of MPM pins exposed. + + qcom,mpm-pin-map: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: MPM pin number + - description: GIC SPI number this pin maps to + description: | + List of (MPM-pin, GIC-SPI) tuples for wake sources that have a + corresponding GIC IRQ. Consumers route their interrupts through + the MPM hierarchical irqdomain to take advantage of these + mappings. + + Raw wake pins like SDC3/4 DATx (pins 21-24) are NOT listed + here =E2=80=94 they have no GIC SPI mapping and are accessed through + the msm8660_mpm_set_pin_wake() / msm8660_mpm_enable_pin() C API + by consumer drivers. + +required: + - compatible + - interrupts + - qcom,rpm-syscon + - qcom,mpm-offset + - mboxes + - interrupt-controller + - '#interrupt-cells' + - qcom,mpm-pin-count + +additionalProperties: false + +examples: + - | + #include + + soc { + #address-cells =3D <1>; + #size-cells =3D <1>; + + msm8660_mpm: interrupt-controller { + compatible =3D "qcom,msm8660-mpm"; + qcom,rpm-syscon =3D <&rpm>; + qcom,mpm-offset =3D <0x9d8>; + interrupts =3D ; + mboxes =3D <&gcc_ipc 1>; + interrupt-controller; + #interrupt-cells =3D <2>; + interrupt-parent =3D <&intc>; + qcom,mpm-pin-count =3D <64>; + qcom,mpm-pin-map =3D <25 100>, + <26 50>, + <27 79>; + }; + }; --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound10.mail.transip.nl (outbound10.mail.transip.nl [136.144.136.17]) (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 531323C2D; Sat, 30 May 2026 14:00:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.17 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149645; cv=none; b=WfhanK9oCxv7Aove5dM3/tyeWj5m11qBW9H6Sy1ojc/wNiiy5a0XQM7rsvjsWclcZkt1ZAZjZzgh28rzNUb/eBXwKcaOyPtkNYn9maAbt/v/zp4UIjNRMyb29+g+U5UuWrYw/+N7ti02mAxk5Zm6giq26J0ZCqm8753IlTMLBIQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149645; c=relaxed/simple; bh=QkJA79Oa/BfdmoMoCt+60OKH9R+eQABEywYnRJyQVWA=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=oSO1JFenD57jLzBhP+/q1eBjuIm+OBboYUhIrQ/lfQczfhCfIVLAOD9vv/Wy/hw1NPuQBmbPS8uUkKJB69OUrTBO7SJ16zTZupOMA0b10aGeSfM2nYvuY0oFj1HJHH58zVaYGtQ7lnCq1ArVOhQylKkffiWtqsvwONDrRTAeYGw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=D6IqTu8P; arc=none smtp.client-ip=136.144.136.17 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="D6IqTu8P" Received: from submission1.mail.transip.nl (unknown [10.100.4.70]) by outbound10.mail.transip.nl (Postfix) with ESMTP id 4gSMLy3BSfzTPPDm; Sat, 30 May 2026 16:00:42 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission1.mail.transip.nl (Postfix) with ESMTPA id 4gSMLx3VDyzJjhXx; Sat, 30 May 2026 16:00:41 +0200 (CEST) From: Herman van Hazendonk To: Amit Kucheria , Conor Dooley , Daniel Lezcano , devicetree@vger.kernel.org, Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Lukasz Luba , "Rafael J. Wysocki" , Rob Herring , Thara Gopinath , van Hazendonk , Zhang Rui Subject: [PATCH 1/2] dt-bindings: thermal: qcom: add pm8901-temp-alarm Date: Sat, 30 May 2026 16:00:39 +0200 Message-ID: <386f3cef9d50d61f588f99706d9f979c56f8bab8.1780148149.git.github.com@herrie.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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 X-Scanned-By: ClueGetter at submission1.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149641; h=from:subject:to:references: in-reply-to:date:mime-version:content-type; bh=DHgPgSc9IYtPxtaNkKbuQyCXlh0YF9woSduy3LC3tjY=; b=D6IqTu8Pt6BoBEWAx5ZiczoMj09ZBRU2W0j12xjC2n/olABK5anstRHDBiNT1R6+9acYs3 wZ3asD8WZONHlMoxqVQtCKMpoGEFoCeFA6tGPWjWNepHI7C9YvvTK2m8qAwMVtCuIgOrgY a9SRP9H9uWmTLYnef0bNrW7piNBEinDrLUMhIAq6SjSJLC7IOUijRyzpEUPZZX6rtAHrhz tNrI4TGCBjaK6bST+P5GNbB5ioVsVf7Ogqr4AyJUz+4AtTur+F6yhhlUcmvuqwJbf9ajYu wmmeOGpfVyLelHOWZBwdywNlFCmct/kEg8dhg9xJ+0lutqEMBSGTmtDJQ5P6/w== X-Report-Abuse-To: abuse@transip.nl Add the binding for the temperature-alarm block inside the Qualcomm PM8901 PMIC (companion to the PM8058 on MSM8x60). The driver exposes the PM8901 die-temperature trip stages (105 / 125 / 145 C) to the thermal framework via a thermal-zone, with stage 3 wired as a critical trip so the kernel issues orderly_poweroff() when the part overheats. The binding describes the SSBI sub-node address, the GIC interrupt the alarm raises on a stage transition, and the parent PMIC reference. Signed-off-by: Herman van Hazendonk --- .../thermal/qcom,pm8901-temp-alarm.yaml | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/qcom,pm8901-t= emp-alarm.yaml diff --git a/Documentation/devicetree/bindings/thermal/qcom,pm8901-temp-ala= rm.yaml b/Documentation/devicetree/bindings/thermal/qcom,pm8901-temp-alarm.= yaml new file mode 100644 index 000000000000..569943b4aedc --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/qcom,pm8901-temp-alarm.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/qcom,pm8901-temp-alarm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm PM8901 PMIC Temperature Alarm + +maintainers: + - Herman van Hazendonk + +description: | + PM8901 is a secondary PMIC paired with PM8058 on MSM8x60 family + (MSM8260/MSM8660/APQ8060) platforms. It exposes an over-temperature + alarm block at SSBI offset + 0x23 (CTRL) / 0x24 (PWM) with four selectable thresholds and three + escalating stages. Unlike PM8058, there is no raw die-temperature + ADC channel =E2=80=94 the driver decodes the stage + threshold pair into= a + representative millicelsius value. + + Two PMIC-internal interrupts are exposed: a stage-transition alarm + (TEMP_ALARM, PM8901 IRQ block 6 bit 4 =3D=3D 52) and a hi-temp alarm + (TEMP_HI_ALARM, block 6 bit 5 =3D=3D 53). + + The driver registers a thermal-of sensor; board DTs declare trip + points and a critical-trip action against it. + +properties: + compatible: + const: qcom,pm8901-temp-alarm + + reg: + description: SSBI offset of the temp-alarm CTRL register. + maxItems: 1 + + interrupts: + items: + - description: Stage-transition alarm interrupt (TEMP_ALARM). + - description: Hi-temperature alarm interrupt (TEMP_HI_ALARM). + + interrupt-names: + items: + - const: alarm + - const: hi-alarm + + "#thermal-sensor-cells": + const: 0 + +required: + - compatible + - reg + - interrupts + - interrupt-names + - "#thermal-sensor-cells" + +additionalProperties: false + +examples: + - | + #include + + ssbi { + pmic { + compatible =3D "qcom,pm8901"; + #interrupt-cells =3D <2>; + interrupt-controller; + #address-cells =3D <1>; + #size-cells =3D <0>; + + pm8901_temp: temp-alarm@23 { + compatible =3D "qcom,pm8901-temp-alarm"; + reg =3D <0x23>; + interrupts =3D <52 IRQ_TYPE_EDGE_RISING>, + <53 IRQ_TYPE_EDGE_RISING>; + interrupt-names =3D "alarm", "hi-alarm"; + #thermal-sensor-cells =3D <0>; + }; + }; + }; --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound9.mail.transip.nl (outbound9.mail.transip.nl [136.144.136.11]) (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 27E192D9EDB; Sat, 30 May 2026 14:05:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149916; cv=none; b=dhQSFMjBmK6HD+l30qf3jqw8q83unel6BAjr9yJa4qVEMeCNa1zebNn5irA8VqZRJOcQaF8ISGhTLIFjpsM1m/RuXA2mLcKHLi4vU2duPycsGMF2ioHRvKGsBN0rpQI41MsN2HrKnnnlKC12mkfbdABwddIxeT1T/eajEZ61H5E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149916; c=relaxed/simple; bh=0pF0rhg2hHdPbQ2Luyn/7WqsTAcxwqew8Vs8I2dTWc4=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=quaH8HvcED3FHXrNg2KVPSt7RpSEKUb0vE3Gje36ELKhSnYDOANzPalQycDWQLejYEgihQYo8izHOhoPm4r7rLkUG6DSCaNtTbAzs/iESpOManAfiNzxvtG1V73m4WrW8KWRI9mtEQA7vuykGtaJcIntgo6PevG2ha72qvMrfuA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=AyUkFqRb; arc=none smtp.client-ip=136.144.136.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="AyUkFqRb" Received: from submission5.mail.transip.nl (unknown [10.103.8.156]) by outbound9.mail.transip.nl (Postfix) with ESMTP id 4gSML84Wk7zTPP3W; Sat, 30 May 2026 16:00:00 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission5.mail.transip.nl (Postfix) with ESMTPA id 4gSML80SrBz2pRDjH; Sat, 30 May 2026 16:00:00 +0200 (CEST) From: Herman van Hazendonk To: Bjorn Andersson , Conor Dooley , devicetree@vger.kernel.org, Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org, Michael Turquette , Rob Herring , Stephen Boyd Subject: [PATCH 1/2] dt-bindings: clock: qcom: add lcc-msm8660 LPASS clock IDs Date: Sat, 30 May 2026 15:59:57 +0200 Message-ID: <942e4109a23de49ebd660272b9c6a5ac1677637f.1780148149.git.github.com@herrie.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: ClueGetter at submission5.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149600; h=from:subject:to:references: in-reply-to:date:mime-version; bh=rW2SLXzGi1/eJwT7d4iDn2tWeHOxsLuq9eRAWU8zmV8=; b=AyUkFqRberhpjMCHQli6VpzyfqOpvXzceFyYzh86uBWWEwtH3UHke6fwk2kD2OTCW1SqG/ 6U0Wa/4s4ZqjdB23lIykXXqa2BP/bJSz5AJsTR516RjDnudHp26yHRHC3rqU5qAa/4kBXi axsfCuHcvFGUaAGoC5YRjIqoCvs4jLUk1eBMkM7TcnN27wtCTp5R/6TXm7zcZlSW5yklAZ M+VckS3VznF53rqEna7l48b0lRmIxVz0+V2dyuJbsLulKBoLHhUkaK9XmAOZu7p8dWFpaB O+7DMDIBg2h7JWt5E7t8qX+kWFqjemilArVPxN9xSgKpnjqEUWDVfDKi4dqM5w== X-Report-Abuse-To: abuse@transip.nl Content-Type: text/plain; charset="utf-8" Add the dt-binding clock-ID header for the MSM8x60 family (MSM8260/MSM8660/APQ8060) Low Power Audio SubSystem Clock Controller (LCC). The header enumerates the LPASS clocks consumed by the qcom,apq8060-lpaif sound card and the codec/AIF nodes downstream of it. It mirrors the format and ID range of the existing LCC headers for newer Qualcomm SoCs (lcc-msm8960, lcc-msm8974) so the drivers/clk/qcom/lcc-msm8660.c driver can be hooked up the same way once it lands. Signed-off-by: Herman van Hazendonk --- include/dt-bindings/clock/qcom,lcc-msm8660.h | 48 ++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,lcc-msm8660.h diff --git a/include/dt-bindings/clock/qcom,lcc-msm8660.h b/include/dt-bind= ings/clock/qcom,lcc-msm8660.h new file mode 100644 index 000000000000..d5d9b0d71a78 --- /dev/null +++ b/include/dt-bindings/clock/qcom,lcc-msm8660.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLK_LCC_MSM8660_H +#define _DT_BINDINGS_CLK_LCC_MSM8660_H + +/* + * MSM8x60 family (MSM8260/MSM8660/APQ8060) LPASS Clock Controller (LCC) + * clock IDs. These are compatible with MSM8960 LCC as MSM8x60 and + * MSM8960 share the same audio subsystem clock architecture. + */ + +#define PLL4 0 +#define MI2S_OSR_SRC 1 +#define MI2S_OSR_CLK 2 +#define MI2S_DIV_CLK 3 +#define MI2S_BIT_DIV_CLK 4 +#define MI2S_BIT_CLK 5 +#define PCM_SRC 6 +#define PCM_CLK_OUT 7 +#define PCM_CLK 8 +#define SLIMBUS_SRC 9 +#define AUDIO_SLIMBUS_CLK 10 +#define SPS_SLIMBUS_CLK 11 +#define CODEC_I2S_MIC_OSR_SRC 12 +#define CODEC_I2S_MIC_OSR_CLK 13 +#define CODEC_I2S_MIC_DIV_CLK 14 +#define CODEC_I2S_MIC_BIT_DIV_CLK 15 +#define CODEC_I2S_MIC_BIT_CLK 16 +#define SPARE_I2S_MIC_OSR_SRC 17 +#define SPARE_I2S_MIC_OSR_CLK 18 +#define SPARE_I2S_MIC_DIV_CLK 19 +#define SPARE_I2S_MIC_BIT_DIV_CLK 20 +#define SPARE_I2S_MIC_BIT_CLK 21 +#define CODEC_I2S_SPKR_OSR_SRC 22 +#define CODEC_I2S_SPKR_OSR_CLK 23 +#define CODEC_I2S_SPKR_DIV_CLK 24 +#define CODEC_I2S_SPKR_BIT_DIV_CLK 25 +#define CODEC_I2S_SPKR_BIT_CLK 26 +#define SPARE_I2S_SPKR_OSR_SRC 27 +#define SPARE_I2S_SPKR_OSR_CLK 28 +#define SPARE_I2S_SPKR_DIV_CLK 29 +#define SPARE_I2S_SPKR_BIT_DIV_CLK 30 +#define SPARE_I2S_SPKR_BIT_CLK 31 + +#endif --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound3.mail.transip.nl (outbound3.mail.transip.nl [136.144.136.12]) (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 F0D4B2F1FD0; Sat, 30 May 2026 14:00:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149647; cv=none; b=li17P5IukoNJK9cflSrGNkvkrzgI9+KYw69LI6zyeeEFjdUQZgTJdlv2mZJ5+21pVfNWX+G6aVDjcC3qTAtTVbvR8J7i4uWmIZ1prCIXlaw7psLc2Hrtg94tsXlf57L8GJvePW0YsC7v40hxIJqpOJHEnU0IONigmR0HGjgFvpw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149647; c=relaxed/simple; bh=Q1TQrZ9h1EUDkMdzhnWAIa05yiMWWL/5s31KtaBOd5c=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=pvf+fzZTduzuPsJ8y/nlMleuclXDR2IsqKNyJteg8KaumbRGRtTyQmtimZgfJeBzjsLypgG5jNsR0C0ZVokNGQG8HBt/RkrczE4KDdmpH4J+VjSGRuyCZ9dNeW3+LTB0TAtN4FyAcvkh6aJ/dRkO+DFOdRdlrJrJYY1lvrPwaHM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=MIpkgfoj; arc=none smtp.client-ip=136.144.136.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="MIpkgfoj" Received: from submission1.mail.transip.nl (unknown [10.100.4.70]) by outbound3.mail.transip.nl (Postfix) with ESMTP id 4gSMLz1mTWzrJ4b; Sat, 30 May 2026 16:00:43 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission1.mail.transip.nl (Postfix) with ESMTPA id 4gSMLy3zbLzJjhXv; Sat, 30 May 2026 16:00:42 +0200 (CEST) From: Herman van Hazendonk To: Amit Kucheria , Conor Dooley , Daniel Lezcano , devicetree@vger.kernel.org, Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Lukasz Luba , "Rafael J. Wysocki" , Rob Herring , Thara Gopinath , van Hazendonk , Zhang Rui Subject: [PATCH 2/2] thermal: qcom: add PM8901 PMIC temperature-alarm driver Date: Sat, 30 May 2026 16:00:40 +0200 Message-ID: <1b3f3853d374d9f65e3043de27f390f7ed97a0e5.1780148149.git.github.com@herrie.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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 X-Scanned-By: ClueGetter at submission1.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149642; h=from:subject:to:references: in-reply-to:date:mime-version:content-type; bh=U/pLuBQbU2rhc92R0TkMoiu/MQTqd6dO/2LFHKZLUFo=; b=MIpkgfojrMn1sxaPS6bHIAO8aKKXIalxnZM84rT+r/7vvDJW2btGOp9hvnVN2dUjtPy2qo el9c3UL/V1c8R1G5MYIy9ciX50X5ZwIKsiQGe7ecKK5Puyt+q8Lst4+D1+rf4c1jRRf94m zBWObck/8zswXgyRqvCG279e1K/XaFCbpk4DXf2T6kKsWdO3ZvKDQOe992C2sLCqdHH5mp GaY3mhcFt6q7NXx0CJEhps1ejsB/15EBx7bt+BnqmTcpjMYymQAeWkD6QrDZEh600IzyyU zsym0dUC/xzpQAx/ft07GX3Y4obPTZj8GwlDHnLJUyc25ELv+n0vcR99gW0Urg== X-Report-Abuse-To: abuse@transip.nl Add a thermal-of sensor driver for the temperature-alarm block inside the Qualcomm PM8901 PMIC. PM8901 is a secondary PMIC paired with PM8058 on the MSM8x60 family (MSM8260/MSM8660/APQ8060). It exposes an over-temperature alarm at SSBI offset 0x23/0x24 with three escalating stages (105/125/145 C); the driver decodes the stage + threshold pair into a millicelsius reading and registers two PMIC- internal interrupts (TEMP_ALARM at block 6 bit 4, TEMP_HI_ALARM at block 6 bit 5). Used by board thermal-zones for the orderly_poweroff path on the HP TouchPad. Signed-off-by: Herman van Hazendonk --- drivers/thermal/qcom/Kconfig | 12 + drivers/thermal/qcom/Makefile | 1 + drivers/thermal/qcom/qcom-pm8901-tm.c | 341 ++++++++++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 drivers/thermal/qcom/qcom-pm8901-tm.c diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index a6bb01082ec6..af099032f1e6 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -32,6 +32,18 @@ config QCOM_SPMI_TEMP_ALARM real time die temperature if an ADC is present or an estimate of the temperature based upon the over temperature stage value. =20 +config QCOM_PM8901_TEMP_ALARM + tristate "Qualcomm PM8901 PMIC Temperature Alarm" + depends on MFD_PM8XXX || COMPILE_TEST + depends on THERMAL_OF + help + This enables the thermal driver for the PM8901 PMIC over-temperature + alarm block. PM8901 exposes a stage-based alarm (no raw ADC) with + four selectable thresholds and three escalating stages. The driver + registers a thermal-of sensor so a board device tree can declare + trip points and a critical-trip action (orderly_poweroff). Used on + HP TouchPad (APQ8060) where PM8901 supplies the secondary PMIC die. + config QCOM_LMH tristate "Qualcomm Limits Management Hardware" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index 0fa2512042e7..90dc05151e33 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -5,4 +5,5 @@ qcom_tsens-y +=3D tsens.o tsens-v2.o tsens-v1.o tsens-v0_= 1.o \ tsens-8960.o obj-$(CONFIG_QCOM_SPMI_ADC_TM5) +=3D qcom-spmi-adc-tm5.o obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) +=3D qcom-spmi-temp-alarm.o +obj-$(CONFIG_QCOM_PM8901_TEMP_ALARM) +=3D qcom-pm8901-tm.o obj-$(CONFIG_QCOM_LMH) +=3D lmh.o diff --git a/drivers/thermal/qcom/qcom-pm8901-tm.c b/drivers/thermal/qcom/q= com-pm8901-tm.c new file mode 100644 index 000000000000..d174d6897921 --- /dev/null +++ b/drivers/thermal/qcom/qcom-pm8901-tm.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Qualcomm PM8901 PMIC Thermal-Alarm Driver + * + * Mainline port of the legacy 2.6.35-palm drivers/thermal/pmic8901-tm.c. + * PM8901 exposes a stage-based over-temperature alarm (no raw ADC) with + * four selectable thresholds and three escalating stages. This driver + * mirrors the legacy programming exactly (threshold-set 0, software + * override enabled, PWM gating at 8 Hz) and registers a thermal-of + * sensor so a board DT can declare trip points and a critical action. + * + * Copyright (c) 2010-2011, Code Aurora Forum. + * Copyright (c) 2026, HP TouchPad mainline port. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SSBI registers (offsets from the per-instance reg base) */ +#define PM8901_TM_REG_CTRL 0x00 /* CTRL/STATUS (base + 0) */ +#define PM8901_TM_REG_PWM 0x01 /* PWM gating (base + 1) */ + +/* CTRL register fields */ +#define CTRL_ST3_SD BIT(7) +#define CTRL_ST2_SD BIT(6) +#define CTRL_STATUS_MASK GENMASK(5, 4) +#define CTRL_THRESH_MASK GENMASK(3, 2) +#define CTRL_OVRD_ST3 BIT(1) +#define CTRL_OVRD_ST2 BIT(0) +#define CTRL_OVRD_MASK GENMASK(1, 0) + +/* PWM register fields */ +#define PWM_EN BIT(7) +#define PWM_PER_PRE_MASK GENMASK(5, 3) +#define PWM_PER_DIV_MASK GENMASK(2, 0) + +/* Temperature math (from legacy pmic8901-tm.c) */ +#define PM8901_TEMP_STAGE_STEP 20000 /* 20 deg C between stages */ +#define PM8901_TEMP_STAGE_HYSTERESIS 2000 /* 2 deg C transition guard */ +#define PM8901_TEMP_THRESH_MIN 105000 /* threshold 0 base =3D 105 C */ +#define PM8901_TEMP_THRESH_STEP 5000 /* 5 deg C per threshold step */ + +/* + * PM8901 has no real die ADC; when stage =3D=3D 0 ("below threshold") we + * report a plausible idle estimate matching the legacy DEFAULT_NO_ADC_TEM= P. + */ +#define PM8901_TEMP_NO_ALARM 37000 + +struct pm8901_tm_chip { + struct device *dev; + struct regmap *map; + struct thermal_zone_device *tz_dev; + struct mutex lock; + unsigned int base; /* SSBI offset, from DT reg */ + unsigned int stage; + unsigned int thresh; + int temp; + bool initialised; +}; + +static int pm8901_tm_read_ctrl(struct pm8901_tm_chip *chip, u8 *val) +{ + unsigned int v; + int ret; + + ret =3D regmap_read(chip->map, chip->base + PM8901_TM_REG_CTRL, &v); + if (!ret) + *val =3D v; + return ret; +} + +static int pm8901_tm_write_ctrl(struct pm8901_tm_chip *chip, u8 val) +{ + return regmap_write(chip->map, chip->base + PM8901_TM_REG_CTRL, val); +} + +static int pm8901_tm_write_pwm(struct pm8901_tm_chip *chip, u8 val) +{ + return regmap_write(chip->map, chip->base + PM8901_TM_REG_PWM, val); +} + +/* + * Decode the (stage, threshold) pair into a single millicelsius value. + * Logic matches the legacy pmic8901-tm.c hysteresis selection: + * - on a rising stage transition, use the lower bound of the new stage + * plus +HYSTERESIS so we don't bounce + * - on a falling stage transition, use the upper bound of the new stage + * minus -HYSTERESIS + * - on the first read after probe (initialised =3D=3D false), pick a + * representative point: midpoint of the stage range, or + * PM8901_TEMP_NO_ALARM when stage =3D=3D 0. + */ +static int pm8901_tm_update_temp_locked(struct pm8901_tm_chip *chip) +{ + unsigned int new_stage; + u8 reg; + int ret; + + ret =3D pm8901_tm_read_ctrl(chip, ®); + if (ret) + return ret; + + new_stage =3D FIELD_GET(CTRL_STATUS_MASK, reg); + chip->thresh =3D FIELD_GET(CTRL_THRESH_MASK, reg); + + if (!chip->initialised) { + if (new_stage) + chip->temp =3D PM8901_TEMP_THRESH_MIN + + chip->thresh * PM8901_TEMP_THRESH_STEP + + (new_stage - 1) * PM8901_TEMP_STAGE_STEP; + else + chip->temp =3D PM8901_TEMP_NO_ALARM; + chip->initialised =3D true; + } else if (new_stage > chip->stage) { + chip->temp =3D PM8901_TEMP_THRESH_MIN + + chip->thresh * PM8901_TEMP_THRESH_STEP + + (new_stage - 1) * PM8901_TEMP_STAGE_STEP + + PM8901_TEMP_STAGE_HYSTERESIS; + } else if (new_stage < chip->stage) { + chip->temp =3D PM8901_TEMP_THRESH_MIN + + chip->thresh * PM8901_TEMP_THRESH_STEP + + new_stage * PM8901_TEMP_STAGE_STEP - + PM8901_TEMP_STAGE_HYSTERESIS; + } + + chip->stage =3D new_stage; + return 0; +} + +static int pm8901_tm_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct pm8901_tm_chip *chip =3D thermal_zone_device_priv(tz); + int ret; + + if (!temp) + return -EINVAL; + + mutex_lock(&chip->lock); + ret =3D pm8901_tm_update_temp_locked(chip); + if (!ret) + *temp =3D chip->temp; + mutex_unlock(&chip->lock); + + return ret; +} + +static const struct thermal_zone_device_ops pm8901_tm_zone_ops =3D { + .get_temp =3D pm8901_tm_get_temp, +}; + +/* + * Shared ISR for both TEMP_ALARM (stage-transition) and TEMP_HI_ALARM + * (hi-temp) interrupts. Updates the cached temperature, clears any + * latched ST2_SD / ST3_SD shutdown bits so the next stage transition + * can be observed, and pokes the thermal core which then re-reads + * temp and walks trips (a critical-trip cross triggers orderly_poweroff + * via the kernel's standard machinery). + */ +static irqreturn_t pm8901_tm_isr(int irq, void *data) +{ + struct pm8901_tm_chip *chip =3D data; + u8 reg; + int ret; + + mutex_lock(&chip->lock); + + ret =3D pm8901_tm_update_temp_locked(chip); + if (ret) { + mutex_unlock(&chip->lock); + return IRQ_HANDLED; + } + + ret =3D pm8901_tm_read_ctrl(chip, ®); + if (!ret && (reg & (CTRL_ST2_SD | CTRL_ST3_SD))) { + reg &=3D ~(CTRL_ST2_SD | CTRL_ST3_SD | CTRL_STATUS_MASK); + pm8901_tm_write_ctrl(chip, reg); + } + + dev_dbg(chip->dev, "alarm irq=3D%d stage=3D%u thresh=3D%u temp=3D%d\n", + irq, chip->stage, chip->thresh, chip->temp); + + mutex_unlock(&chip->lock); + + thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED); + return IRQ_HANDLED; +} + +/* + * Program PM8901 to the legacy default: threshold-set 0 (105 / 125 / 145 = C), + * software override enabled (kernel handles shutdown, PMIC does not auto-= cut), + * PWM at 8 Hz (legacy "cut down on unnecessary interrupts" rate). + */ +static int pm8901_tm_init_hw(struct pm8901_tm_chip *chip) +{ + int ret; + u8 reg; + + mutex_lock(&chip->lock); + + ret =3D pm8901_tm_read_ctrl(chip, ®); + if (ret) + goto out; + + /* + * Enable software override so PMIC does NOT auto-shut-down on stage 3. + * Critical-trip orderly_poweroff is delivered by the kernel thermal + * core via the DT thermal-zone trip with type =3D "critical". + */ + reg =3D (reg & ~(CTRL_OVRD_MASK | CTRL_STATUS_MASK | CTRL_THRESH_MASK)) | + CTRL_OVRD_ST3 | CTRL_OVRD_ST2; + ret =3D pm8901_tm_write_ctrl(chip, reg); + if (ret) + goto out; + + chip->thresh =3D 0; + + /* PWM @ 8 Hz: PWM_EN | PRE=3D3 | DIV=3D3 =E2=80=94 verbatim from legacy.= */ + reg =3D PWM_EN | FIELD_PREP(PWM_PER_PRE_MASK, 3) | + FIELD_PREP(PWM_PER_DIV_MASK, 3); + ret =3D pm8901_tm_write_pwm(chip, reg); + if (ret) + goto out; + + /* Prime the cached temperature from current hardware state. */ + chip->initialised =3D false; + ret =3D pm8901_tm_update_temp_locked(chip); + +out: + mutex_unlock(&chip->lock); + return ret; +} + +static int pm8901_tm_probe(struct platform_device *pdev) +{ + struct pm8901_tm_chip *chip; + int ret, irq_alarm, irq_hi_alarm; + u32 res; + + chip =3D devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev =3D &pdev->dev; + mutex_init(&chip->lock); + + chip->map =3D dev_get_regmap(pdev->dev.parent, NULL); + if (!chip->map) + return dev_err_probe(&pdev->dev, -ENXIO, + "no regmap on PM8901 parent\n"); + + ret =3D of_property_read_u32(pdev->dev.of_node, "reg", &res); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "missing reg property\n"); + chip->base =3D res; + + irq_alarm =3D platform_get_irq_byname(pdev, "alarm"); + if (irq_alarm < 0) + return irq_alarm; + irq_hi_alarm =3D platform_get_irq_byname(pdev, "hi-alarm"); + if (irq_hi_alarm < 0) + return irq_hi_alarm; + + ret =3D pm8901_tm_init_hw(chip); + if (ret) + return dev_err_probe(&pdev->dev, ret, "hw init failed\n"); + + chip->tz_dev =3D devm_thermal_of_zone_register(&pdev->dev, 0, chip, + &pm8901_tm_zone_ops); + if (IS_ERR(chip->tz_dev)) + return dev_err_probe(&pdev->dev, PTR_ERR(chip->tz_dev), + "thermal zone register failed\n"); + + ret =3D devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL, + pm8901_tm_isr, IRQF_ONESHOT, + "pm8901-tm-alarm", chip); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "alarm IRQ request failed\n"); + + ret =3D devm_request_threaded_irq(&pdev->dev, irq_hi_alarm, NULL, + pm8901_tm_isr, IRQF_ONESHOT, + "pm8901-tm-hi-alarm", chip); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "hi-alarm IRQ request failed\n"); + + platform_set_drvdata(pdev, chip); + thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED); + + dev_info(&pdev->dev, + "PM8901 thermal alarm: base=3D0x%x stage=3D%u thresh=3D%u temp=3D%d\n", + chip->base, chip->stage, chip->thresh, chip->temp); + + return 0; +} + +static void pm8901_tm_remove(struct platform_device *pdev) +{ + struct pm8901_tm_chip *chip =3D platform_get_drvdata(pdev); + u8 reg; + + /* + * Disable software override on the way out so the PMIC reverts to + * its hardware auto-cut behaviour if the kernel is no longer the + * shutdown agent. Best-effort: ignore errors. + */ + mutex_lock(&chip->lock); + if (!pm8901_tm_read_ctrl(chip, ®)) { + reg &=3D ~CTRL_OVRD_MASK; + pm8901_tm_write_ctrl(chip, reg); + } + mutex_unlock(&chip->lock); +} + +static const struct of_device_id pm8901_tm_match_table[] =3D { + { .compatible =3D "qcom,pm8901-temp-alarm" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8901_tm_match_table); + +static struct platform_driver pm8901_tm_driver =3D { + .driver =3D { + .name =3D "pm8901-temp-alarm", + .of_match_table =3D pm8901_tm_match_table, + }, + .probe =3D pm8901_tm_probe, + .remove =3D pm8901_tm_remove, +}; +module_platform_driver(pm8901_tm_driver); + +MODULE_ALIAS("platform:pm8901-temp-alarm"); +MODULE_DESCRIPTION("Qualcomm PM8901 PMIC Thermal Alarm driver"); +MODULE_LICENSE("GPL v2"); --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound7.mail.transip.nl (outbound7.mail.transip.nl [136.144.136.7]) (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 049D22F39B5; Sat, 30 May 2026 14:08:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780150111; cv=none; b=JcTlLLWfx4onrv32ZZFzbMdRrnDrG0X6FkI6Ni4kx7eAbRZouhtvEYq8cR0YBZVKaOSXnwOkJt4iWWSYfzk5uigXceo05E1+Y7u6eIusqC8ONar95Ys4sg8ZiseFGhkd+yVMaUZ26Mn9g4CHX+14zcDR49TP0UJexS/5Ld+uwSM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780150111; c=relaxed/simple; bh=kYxL+qyGFYq+Eu5PRSx8j1K3TZspk7ZFarI/hL6/obo=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rx3A3m0h0itdr3kKQ3QXuC4PL57ngxyFTNGYOFrh0DL4gK65L6stMf2YJmyb22O75ThxNMWGKChQ9Z7D4clpZSi1f3mtnZ/BwXJ22KV18sJ+fBV41mnq31nMR6DbYLJmaXIBaPmCI38mRZEsdAVBOwRY4m2YiiOkKmSC/S4hp8Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=KeQNqPCA; arc=none smtp.client-ip=136.144.136.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="KeQNqPCA" Received: from submission5.mail.transip.nl (unknown [10.103.8.156]) by outbound7.mail.transip.nl (Postfix) with ESMTP id 4gSML91zjNzQvvLB; Sat, 30 May 2026 16:00:01 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission5.mail.transip.nl (Postfix) with ESMTPA id 4gSML84hJBz2pRDjK; Sat, 30 May 2026 16:00:00 +0200 (CEST) From: Herman van Hazendonk To: Bjorn Andersson , Conor Dooley , devicetree@vger.kernel.org, Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org, Michael Turquette , Rob Herring , Stephen Boyd Subject: [PATCH 2/2] clk: qcom: add MSM8x60 LCC (LPASS) driver Date: Sat, 30 May 2026 15:59:58 +0200 Message-ID: <10180e08af4d55e776963932a4c5e60f0f197a96.1780148149.git.github.com@herrie.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: ClueGetter at submission5.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149600; h=from:subject:to:references: in-reply-to:date:mime-version; bh=i8BLZW/TmTrd0Zazw7/R+GodYvJYbHCORFIZKVETotY=; b=KeQNqPCAjex9ZYCdZkKLIQqlcgwVLXZ+8A4yDa6ylvTGW+7POVCI+c6ebfSH3i+pP9pNxA woYPpONvoelSV28y3F2u1EowG/gym8AeEvGiK9bjRZTZSStbxntwgfZMDE/WzUCK/8uM1b ceauqXs1v1UaqGIfRHWQp966yI+q+DcaUEjV6D5oTfic3Cumvduv0P6u9z2KO/2t7QE+Dt Jcjxhl3GAPpoUH+eWP4qkO1UUl9lFSY3zoifOJ6LJ+GbE4Rp6XIvwChquLXUCjhSGWrmHv SJbSYdxfM+N7O7/2us+IrQeZD9d1RzrpBtLz+i1ugiGJ5PrAwvaxSocAeq0Wxw== X-Report-Abuse-To: abuse@transip.nl Content-Type: text/plain; charset="utf-8" Add a clock driver for the LPASS (Low Power Audio SubSystem) Clock Controller on the MSM8x60 family (MSM8260/MSM8660/APQ8060) - the Scorpion-class generation that preceded MSM8960's Krait CPUs. The register layout, parent muxing and divider topology of the LPASS PLL/clk fabric differ from MSM8960's LCC enough that a clean separate driver is simpler than parameterising mmcc-msm8960.c. Both drivers can coexist in tree (different KConfig options, different match table, different device-tree compatible). Used on the HP TouchPad (Tenderloin) where the LPASS Q6 audio DSP needs functional MI2S / SLIMBus / PCM clocks before audio playback or capture works. Signed-off-by: Herman van Hazendonk --- drivers/clk/qcom/Kconfig | 9 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/lcc-msm8660.c | 517 +++++++++++++++++++++++++++++++++ 3 files changed, 527 insertions(+) create mode 100644 drivers/clk/qcom/lcc-msm8660.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index d9cff5b0281d..2ea95f69355e 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -547,6 +547,15 @@ config MSM_LCC_8960 Say Y if you want to use audio devices such as i2s, pcm, SLIMBus, etc. =20 +config MSM_LCC_8660 + tristate "MSM8x60 LPASS Clock Controller" + depends on ARM || COMPILE_TEST + help + Support for the LPASS clock controller on the MSM8x60 family + (MSM8260/MSM8660/APQ8060). MSM8960 is the newer + Krait-based generation handled separately by MSM_LCC_8960. + Say Y if you want to use audio devices such as i2s, pcm, SLIMBus. + config MDM_GCC_9607 tristate "MDM9607 Global Clock Controller" depends on ARM || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index e100cfd6a52d..41c973d7db59 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_MSM_GCC_8974) +=3D gcc-msm8974.o obj-$(CONFIG_MSM_GCC_8976) +=3D gcc-msm8976.o obj-$(CONFIG_MSM_GCC_8994) +=3D gcc-msm8994.o obj-$(CONFIG_MSM_GCC_8996) +=3D gcc-msm8996.o +obj-$(CONFIG_MSM_LCC_8660) +=3D lcc-msm8660.o obj-$(CONFIG_MSM_LCC_8960) +=3D lcc-msm8960.o obj-$(CONFIG_MSM_GCC_8998) +=3D gcc-msm8998.o obj-$(CONFIG_MSM_GPUCC_8998) +=3D gpucc-msm8998.o diff --git a/drivers/clk/qcom/lcc-msm8660.c b/drivers/clk/qcom/lcc-msm8660.c new file mode 100644 index 000000000000..7f13279467f9 --- /dev/null +++ b/drivers/clk/qcom/lcc-msm8660.c @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + Herman van Hazendonk + * + * Qualcomm MSM8x60 family (MSM8260/MSM8660/APQ8060) LPASS Clock Controlle= r driver. + * + * Split from lcc-msm8960.c because the MSM8x60 family is a separate + * SoC generation (Scorpion) from MSM8960 (Krait). The clock topology is + * compatible but PLL4 runs at a different rate (540.672 MHz, L=3D22) and = the + * driver has no need for the MDM9615 CXO patch or the 492 MHz frequency p= lan. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" + +static struct clk_pll pll4 =3D { + .l_reg =3D 0x4, + .m_reg =3D 0x8, + .n_reg =3D 0xc, + .config_reg =3D 0x14, + .mode_reg =3D 0x0, + .status_reg =3D 0x18, + .status_bit =3D 16, + .clkr.hw.init =3D &(struct clk_init_data){ + .name =3D "pll4", + .parent_data =3D &(const struct clk_parent_data){ + .fw_name =3D "pxo", .name =3D "pxo_board", + }, + .num_parents =3D 1, + .ops =3D &clk_pll_ops, + }, +}; + +enum { + P_PXO, + P_PLL4, +}; + +static const struct parent_map lcc_pxo_pll4_map[] =3D { + { P_PXO, 0 }, + { P_PLL4, 2 } +}; + +static const struct clk_parent_data lcc_pxo_pll4[] =3D { + { .fw_name =3D "pxo", .name =3D "pxo_board" }, + { .fw_name =3D "pll4_vote", .name =3D "pll4_vote" }, +}; + +/* + * MSM8x60 PLL4 runs at 540.672 MHz (24.576 MHz * 22, L=3D0x16). + * Divisors taken from the legacy webOS clock-8x60.c driver. + * AIF_OSR has an 8-bit M/N counter, so 512000 Hz is not achievable with + * this PLL frequency and is intentionally omitted from the 540 MHz tables. + */ +static const struct freq_tbl clk_tbl_aif_osr_540[] =3D { + { 768000, P_PLL4, 4, 1, 176 }, + { 1024000, P_PLL4, 4, 1, 132 }, + { 1536000, P_PLL4, 4, 1, 88 }, + { 2048000, P_PLL4, 4, 1, 66 }, + { 3072000, P_PLL4, 4, 1, 44 }, + { 4096000, P_PLL4, 4, 1, 33 }, + { 6144000, P_PLL4, 4, 1, 22 }, + { 8192000, P_PLL4, 2, 1, 33 }, + { 12288000, P_PLL4, 4, 1, 11 }, + { 24576000, P_PLL4, 2, 1, 11 }, + { 27000000, P_PXO, 1, 0, 0 }, + { } +}; + +static const struct freq_tbl clk_tbl_aif_osr_393[] =3D { + { 512000, P_PLL4, 4, 1, 192 }, + { 768000, P_PLL4, 4, 1, 128 }, + { 1024000, P_PLL4, 4, 1, 96 }, + { 1536000, P_PLL4, 4, 1, 64 }, + { 2048000, P_PLL4, 4, 1, 48 }, + { 3072000, P_PLL4, 4, 1, 32 }, + { 4096000, P_PLL4, 4, 1, 24 }, + { 6144000, P_PLL4, 4, 1, 16 }, + { 8192000, P_PLL4, 4, 1, 12 }, + { 12288000, P_PLL4, 4, 1, 8 }, + { 24576000, P_PLL4, 4, 1, 4 }, + { 27000000, P_PXO, 1, 0, 0 }, + { } +}; + +#define CLK_AIF_OSR_SRC(prefix, _ns, _md) \ +static struct clk_rcg prefix##_osr_src =3D { \ + .ns_reg =3D _ns, \ + .md_reg =3D _md, \ + .mn =3D { \ + .mnctr_en_bit =3D 8, \ + .mnctr_reset_bit =3D 7, \ + .mnctr_mode_shift =3D 5, \ + .n_val_shift =3D 24, \ + .m_val_shift =3D 8, \ + .width =3D 8, \ + }, \ + .p =3D { \ + .pre_div_shift =3D 3, \ + .pre_div_width =3D 2, \ + }, \ + .s =3D { \ + .src_sel_shift =3D 0, \ + .parent_map =3D lcc_pxo_pll4_map, \ + }, \ + .freq_tbl =3D clk_tbl_aif_osr_393, \ + .clkr =3D { \ + .enable_reg =3D _ns, \ + .enable_mask =3D BIT(9), \ + .hw.init =3D &(struct clk_init_data){ \ + .name =3D #prefix "_osr_src", \ + .parent_data =3D lcc_pxo_pll4, \ + .num_parents =3D ARRAY_SIZE(lcc_pxo_pll4), \ + .ops =3D &clk_rcg_ops, \ + .flags =3D CLK_SET_RATE_GATE, \ + }, \ + }, \ +}; \ + +#define CLK_AIF_OSR_CLK(prefix, _ns, hr, en_bit) \ +static struct clk_branch prefix##_osr_clk =3D { \ + .halt_reg =3D hr, \ + .halt_bit =3D 1, \ + .halt_check =3D BRANCH_HALT_ENABLE, \ + .clkr =3D { \ + .enable_reg =3D _ns, \ + .enable_mask =3D BIT(en_bit), \ + .hw.init =3D &(struct clk_init_data){ \ + .name =3D #prefix "_osr_clk", \ + .parent_hws =3D (const struct clk_hw*[]){ \ + &prefix##_osr_src.clkr.hw, \ + }, \ + .num_parents =3D 1, \ + .ops =3D &clk_branch_ops, \ + .flags =3D CLK_SET_RATE_PARENT, \ + }, \ + }, \ +}; \ + +#define CLK_AIF_OSR_DIV_CLK(prefix, _ns, _width) \ +static struct clk_regmap_div prefix##_div_clk =3D { \ + .reg =3D _ns, \ + .shift =3D 10, \ + .width =3D _width, \ + .clkr =3D { \ + .hw.init =3D &(struct clk_init_data){ \ + .name =3D #prefix "_div_clk", \ + .parent_hws =3D (const struct clk_hw*[]){ \ + &prefix##_osr_src.clkr.hw, \ + }, \ + .num_parents =3D 1, \ + .ops =3D &clk_regmap_div_ops, \ + }, \ + }, \ +}; \ + +#define CLK_AIF_OSR_BIT_DIV_CLK(prefix, _ns, hr, en_bit) \ +static struct clk_branch prefix##_bit_div_clk =3D { \ + .halt_reg =3D hr, \ + .halt_bit =3D 0, \ + .halt_check =3D BRANCH_HALT_ENABLE, \ + .clkr =3D { \ + .enable_reg =3D _ns, \ + .enable_mask =3D BIT(en_bit), \ + .hw.init =3D &(struct clk_init_data){ \ + .name =3D #prefix "_bit_div_clk", \ + .parent_hws =3D (const struct clk_hw*[]){ \ + &prefix##_div_clk.clkr.hw, \ + }, \ + .num_parents =3D 1, \ + .ops =3D &clk_branch_ops, \ + .flags =3D CLK_SET_RATE_PARENT, \ + }, \ + }, \ +}; \ + +#define CLK_AIF_OSR_BIT_CLK(prefix, _ns, _shift) \ +static struct clk_regmap_mux prefix##_bit_clk =3D { \ + .reg =3D _ns, \ + .shift =3D _shift, \ + .width =3D 1, \ + .clkr =3D { \ + .hw.init =3D &(struct clk_init_data){ \ + .name =3D #prefix "_bit_clk", \ + .parent_data =3D (const struct clk_parent_data[]){ \ + { .hw =3D &prefix##_bit_div_clk.clkr.hw, }, \ + { .fw_name =3D #prefix "_codec_clk", \ + .name =3D #prefix "_codec_clk", }, \ + }, \ + .num_parents =3D 2, \ + .ops =3D &clk_regmap_mux_closest_ops, \ + .flags =3D CLK_SET_RATE_PARENT, \ + }, \ + }, \ +}; + +CLK_AIF_OSR_SRC(mi2s, 0x48, 0x4c) +CLK_AIF_OSR_CLK(mi2s, 0x48, 0x50, 17) +CLK_AIF_OSR_DIV_CLK(mi2s, 0x48, 4) +CLK_AIF_OSR_BIT_DIV_CLK(mi2s, 0x48, 0x50, 15) +CLK_AIF_OSR_BIT_CLK(mi2s, 0x48, 14) + +/* + * CLK_AIF_OSR_DIV - Audio Interface with divider clocks. + * Enable bits per legacy MSM8660 kernel: + * - OSR branch enable: BIT(17) + * - BIT_DIV branch enable: BIT(15) + */ +#define CLK_AIF_OSR_DIV(prefix, _ns, _md, hr) \ + CLK_AIF_OSR_SRC(prefix, _ns, _md) \ + CLK_AIF_OSR_CLK(prefix, _ns, hr, 17) \ + CLK_AIF_OSR_DIV_CLK(prefix, _ns, 8) \ + CLK_AIF_OSR_BIT_DIV_CLK(prefix, _ns, hr, 15) \ + CLK_AIF_OSR_BIT_CLK(prefix, _ns, 18) + +CLK_AIF_OSR_DIV(codec_i2s_mic, 0x60, 0x64, 0x68); +CLK_AIF_OSR_DIV(spare_i2s_mic, 0x78, 0x7c, 0x80); +CLK_AIF_OSR_DIV(codec_i2s_spkr, 0x6c, 0x70, 0x74); +CLK_AIF_OSR_DIV(spare_i2s_spkr, 0x84, 0x88, 0x8c); + +/* PCM frequency table for MSM8x60 with PLL4 at 540.672 MHz */ +static const struct freq_tbl clk_tbl_pcm_540[] =3D { + { 256000, P_PLL4, 4, 1, 528 }, + { 512000, P_PLL4, 4, 1, 264 }, + { 768000, P_PLL4, 4, 1, 176 }, + { 1024000, P_PLL4, 4, 1, 132 }, + { 1536000, P_PLL4, 4, 1, 88 }, + { 2048000, P_PLL4, 4, 1, 66 }, + { 3072000, P_PLL4, 4, 1, 44 }, + { 4096000, P_PLL4, 4, 1, 33 }, + { 6144000, P_PLL4, 4, 1, 22 }, + { 8192000, P_PLL4, 2, 1, 33 }, + { 12288000, P_PLL4, 4, 1, 11 }, + { 24576000, P_PLL4, 2, 1, 11 }, + { 27000000, P_PXO, 1, 0, 0 }, + { } +}; + +static const struct freq_tbl clk_tbl_pcm_393[] =3D { + { 256000, P_PLL4, 4, 1, 384 }, + { 512000, P_PLL4, 4, 1, 192 }, + { 768000, P_PLL4, 4, 1, 128 }, + { 1024000, P_PLL4, 4, 1, 96 }, + { 1536000, P_PLL4, 4, 1, 64 }, + { 2048000, P_PLL4, 4, 1, 48 }, + { 3072000, P_PLL4, 4, 1, 32 }, + { 4096000, P_PLL4, 4, 1, 24 }, + { 6144000, P_PLL4, 4, 1, 16 }, + { 8192000, P_PLL4, 4, 1, 12 }, + { 12288000, P_PLL4, 4, 1, 8 }, + { 24576000, P_PLL4, 4, 1, 4 }, + { } +}; + +static struct clk_rcg pcm_src =3D { + .ns_reg =3D 0x54, + .md_reg =3D 0x58, + .mn =3D { + .mnctr_en_bit =3D 8, + .mnctr_reset_bit =3D 7, + .mnctr_mode_shift =3D 5, + .n_val_shift =3D 16, + .m_val_shift =3D 16, + .width =3D 16, + }, + .p =3D { + .pre_div_shift =3D 3, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D lcc_pxo_pll4_map, + }, + .freq_tbl =3D clk_tbl_pcm_393, + .clkr =3D { + .enable_reg =3D 0x54, + .enable_mask =3D BIT(9), + .hw.init =3D &(struct clk_init_data){ + .name =3D "pcm_src", + .parent_data =3D lcc_pxo_pll4, + .num_parents =3D ARRAY_SIZE(lcc_pxo_pll4), + .ops =3D &clk_rcg_ops, + .flags =3D CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch pcm_clk_out =3D { + .halt_reg =3D 0x5c, + .halt_bit =3D 0, + .halt_check =3D BRANCH_HALT_ENABLE, + .clkr =3D { + .enable_reg =3D 0x54, + .enable_mask =3D BIT(11), + .hw.init =3D &(struct clk_init_data){ + .name =3D "pcm_clk_out", + .parent_hws =3D (const struct clk_hw*[]){ + &pcm_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_regmap_mux pcm_clk =3D { + .reg =3D 0x54, + .shift =3D 10, + .width =3D 1, + .clkr =3D { + .hw.init =3D &(struct clk_init_data){ + .name =3D "pcm_clk", + .parent_data =3D (const struct clk_parent_data[]){ + { .hw =3D &pcm_clk_out.clkr.hw }, + { .fw_name =3D "pcm_codec_clk", .name =3D "pcm_codec_clk" }, + }, + .num_parents =3D 2, + .ops =3D &clk_regmap_mux_closest_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg slimbus_src =3D { + .ns_reg =3D 0xcc, + .md_reg =3D 0xd0, + .mn =3D { + .mnctr_en_bit =3D 8, + .mnctr_reset_bit =3D 7, + .mnctr_mode_shift =3D 5, + .n_val_shift =3D 24, + .m_val_shift =3D 8, + .width =3D 8, + }, + .p =3D { + .pre_div_shift =3D 3, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D lcc_pxo_pll4_map, + }, + .freq_tbl =3D clk_tbl_aif_osr_393, + .clkr =3D { + .enable_reg =3D 0xcc, + .enable_mask =3D BIT(9), + .hw.init =3D &(struct clk_init_data){ + .name =3D "slimbus_src", + .parent_data =3D lcc_pxo_pll4, + .num_parents =3D ARRAY_SIZE(lcc_pxo_pll4), + .ops =3D &clk_rcg_ops, + .flags =3D CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch audio_slimbus_clk =3D { + .halt_reg =3D 0xd4, + .halt_bit =3D 0, + .halt_check =3D BRANCH_HALT_ENABLE, + .clkr =3D { + .enable_reg =3D 0xcc, + .enable_mask =3D BIT(10), + .hw.init =3D &(struct clk_init_data){ + .name =3D "audio_slimbus_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &slimbus_src.clkr.hw, + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch sps_slimbus_clk =3D { + .halt_reg =3D 0xd4, + .halt_bit =3D 1, + .halt_check =3D BRANCH_HALT_ENABLE, + .clkr =3D { + .enable_reg =3D 0xcc, + .enable_mask =3D BIT(12), + .hw.init =3D &(struct clk_init_data){ + .name =3D "sps_slimbus_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &slimbus_src.clkr.hw, + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_regmap *lcc_msm8660_clks[] =3D { + [PLL4] =3D &pll4.clkr, + [MI2S_OSR_SRC] =3D &mi2s_osr_src.clkr, + [MI2S_OSR_CLK] =3D &mi2s_osr_clk.clkr, + [MI2S_DIV_CLK] =3D &mi2s_div_clk.clkr, + [MI2S_BIT_DIV_CLK] =3D &mi2s_bit_div_clk.clkr, + [MI2S_BIT_CLK] =3D &mi2s_bit_clk.clkr, + [PCM_SRC] =3D &pcm_src.clkr, + [PCM_CLK_OUT] =3D &pcm_clk_out.clkr, + [PCM_CLK] =3D &pcm_clk.clkr, + [SLIMBUS_SRC] =3D &slimbus_src.clkr, + [AUDIO_SLIMBUS_CLK] =3D &audio_slimbus_clk.clkr, + [SPS_SLIMBUS_CLK] =3D &sps_slimbus_clk.clkr, + [CODEC_I2S_MIC_OSR_SRC] =3D &codec_i2s_mic_osr_src.clkr, + [CODEC_I2S_MIC_OSR_CLK] =3D &codec_i2s_mic_osr_clk.clkr, + [CODEC_I2S_MIC_DIV_CLK] =3D &codec_i2s_mic_div_clk.clkr, + [CODEC_I2S_MIC_BIT_DIV_CLK] =3D &codec_i2s_mic_bit_div_clk.clkr, + [CODEC_I2S_MIC_BIT_CLK] =3D &codec_i2s_mic_bit_clk.clkr, + [SPARE_I2S_MIC_OSR_SRC] =3D &spare_i2s_mic_osr_src.clkr, + [SPARE_I2S_MIC_OSR_CLK] =3D &spare_i2s_mic_osr_clk.clkr, + [SPARE_I2S_MIC_DIV_CLK] =3D &spare_i2s_mic_div_clk.clkr, + [SPARE_I2S_MIC_BIT_DIV_CLK] =3D &spare_i2s_mic_bit_div_clk.clkr, + [SPARE_I2S_MIC_BIT_CLK] =3D &spare_i2s_mic_bit_clk.clkr, + [CODEC_I2S_SPKR_OSR_SRC] =3D &codec_i2s_spkr_osr_src.clkr, + [CODEC_I2S_SPKR_OSR_CLK] =3D &codec_i2s_spkr_osr_clk.clkr, + [CODEC_I2S_SPKR_DIV_CLK] =3D &codec_i2s_spkr_div_clk.clkr, + [CODEC_I2S_SPKR_BIT_DIV_CLK] =3D &codec_i2s_spkr_bit_div_clk.clkr, + [CODEC_I2S_SPKR_BIT_CLK] =3D &codec_i2s_spkr_bit_clk.clkr, + [SPARE_I2S_SPKR_OSR_SRC] =3D &spare_i2s_spkr_osr_src.clkr, + [SPARE_I2S_SPKR_OSR_CLK] =3D &spare_i2s_spkr_osr_clk.clkr, + [SPARE_I2S_SPKR_DIV_CLK] =3D &spare_i2s_spkr_div_clk.clkr, + [SPARE_I2S_SPKR_BIT_DIV_CLK] =3D &spare_i2s_spkr_bit_div_clk.clkr, + [SPARE_I2S_SPKR_BIT_CLK] =3D &spare_i2s_spkr_bit_clk.clkr, +}; + +static const struct regmap_config lcc_msm8660_regmap_config =3D { + .reg_bits =3D 32, + .reg_stride =3D 4, + .val_bits =3D 32, + .max_register =3D 0xfc, + .fast_io =3D true, +}; + +static const struct qcom_cc_desc lcc_msm8660_desc =3D { + .config =3D &lcc_msm8660_regmap_config, + .clks =3D lcc_msm8660_clks, + .num_clks =3D ARRAY_SIZE(lcc_msm8660_clks), +}; + +static const struct of_device_id lcc_msm8660_match_table[] =3D { + { .compatible =3D "qcom,lcc-msm8660" }, + { .compatible =3D "qcom,lcc-apq8060" }, + { } +}; +MODULE_DEVICE_TABLE(of, lcc_msm8660_match_table); + +static int lcc_msm8660_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + u32 val; + + regmap =3D qcom_cc_map(pdev, &lcc_msm8660_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* + * MSM8x60 should always boot with PLL4 L=3D22 (540.672 MHz). + * Detect anyway so a board with a non-standard L value still gets a + * coherent frequency plan instead of silently producing wrong rates. + */ + regmap_read(regmap, 0x4, &val); + if (val =3D=3D 0x16) { + dev_info(&pdev->dev, + "PLL4 L=3D0x%x, using 540MHz frequency plan\n", val); + slimbus_src.freq_tbl =3D clk_tbl_aif_osr_540; + mi2s_osr_src.freq_tbl =3D clk_tbl_aif_osr_540; + codec_i2s_mic_osr_src.freq_tbl =3D clk_tbl_aif_osr_540; + spare_i2s_mic_osr_src.freq_tbl =3D clk_tbl_aif_osr_540; + codec_i2s_spkr_osr_src.freq_tbl =3D clk_tbl_aif_osr_540; + spare_i2s_spkr_osr_src.freq_tbl =3D clk_tbl_aif_osr_540; + pcm_src.freq_tbl =3D clk_tbl_pcm_540; + } else { + dev_info(&pdev->dev, + "PLL4 L=3D0x%x, using fallback 393MHz frequency plan\n", + val); + } + + /* Enable PLL4 source on the LPASS Primary PLL Mux */ + regmap_write(regmap, 0xc4, 0x1); + + return qcom_cc_really_probe(&pdev->dev, &lcc_msm8660_desc, regmap); +} + +static struct platform_driver lcc_msm8660_driver =3D { + .probe =3D lcc_msm8660_probe, + .driver =3D { + .name =3D "lcc-msm8660", + .of_match_table =3D lcc_msm8660_match_table, + }, +}; +module_platform_driver(lcc_msm8660_driver); + +MODULE_DESCRIPTION("Qualcomm MSM8x60 LPASS Clock Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lcc-msm8660"); --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound10.mail.transip.nl (outbound10.mail.transip.nl [136.144.136.17]) (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 6E5062F6596; Sat, 30 May 2026 14:05:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.17 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149941; cv=none; b=eZxpG8hsI5Hu7AA4A9tpvKtfYeXUBpmujQri5YYehUYVCCtSPP7Q8BTnNT249lTR2MwMcD+hi6R6SQzGrNHS7zXbyKpMd0hd1mzrWG/WeGglO/YVPwKNS90L6uMFS0nw3UmgNYYqoPgeaOubO3BqMu2Nj2mn9ZDOaydMXnVCvd4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149941; c=relaxed/simple; bh=+QjWqsNdohmngZFGvCvaNMAp/lHvrU1j4TeLxIbajXk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=m+NqcZyA7oi1zoFSl4wtTd9xUcleYLFz0nKrbap3DlvTWzWP04T5lxNfvZrTN99+J+oakEGSr18L7MrwdI3i39LJfSSVL/4Rzfw1nGThz2n0HIzagkuzxxQOmbQ2OQB+6Pbq9ihV9Px+BJ7tVmlvOPbUNUYYI+FUCiHEd6IOd6M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=Ny0ZDdKK; arc=none smtp.client-ip=136.144.136.17 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="Ny0ZDdKK" Received: from submission5.mail.transip.nl (unknown [10.103.8.156]) by outbound10.mail.transip.nl (Postfix) with ESMTP id 4gSMJJ5494zTPNWK; Sat, 30 May 2026 15:58:24 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission5.mail.transip.nl (Postfix) with ESMTPA id 4gSMJJ1C0jz2pRDjK; Sat, 30 May 2026 15:58:24 +0200 (CEST) From: Herman van Hazendonk To: Philipp Zabel , Rob Herring , Krzysztof Kozlowski , Conor Dooley , linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org Cc: Herman van Hazendonk Subject: [PATCH 2/3] dt-bindings: reset: qcom: add mmcc-msm8660 reset IDs Date: Sat, 30 May 2026 15:58:11 +0200 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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 X-Scanned-By: ClueGetter at submission5.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149504; h=from:subject:to:cc: references:in-reply-to:date:mime-version:content-type; bh=d7Er86HmzNP/oc6nFqyXAKLhnVQcyD+0JXnIczziOdo=; b=Ny0ZDdKKZa/vWwvJkjAvn/5E8r1j1+hZMItLCNtndysCy60OCrxjFz+bkiQrzxPkYuVeLS FGYSXkUQghLuXTM2OnhU1FHuRqP+dV/IzsOXHjBM/KzPcSOtt1XSNGdNvkuVuB3Vt/aj3a Yb7zdFzaXjm70hYN5p0VyggIfIFE8WYVfTjbGVE4gPhGUuAGvBynHxMHokNPPNj8nforCe uNGL4ebtKDCTlGanJb5/7noK8ZVbu9yv1Y7lum/xHXUulPSn4RUOXPdpODMDt9dYn9GiNQ oDkT82qVtGmi6ljvGwBtuHwrXoKX0I18eROAWhGVtZpdP0Tk15i2DEOJCJimbA== X-Report-Abuse-To: abuse@transip.nl Add the dt-binding reset-ID header for the MSM8x60 family (MSM8260/MSM8660/APQ8060) MMCC reset lines. The MMCC exposes per-IP software-reset bits used by MDP4 (display), CAMSS / VFE (camera), Gemini (JPEG), rotator, VPE and the GFX2D / GFX3D blocks; each gets a matching reset-controller ID here. IDs match the numeric values used by the original shared mmcc-msm8960.h reset bindings; only the resets actually implemented by mmcc-msm8660.c are defined. Signed-off-by: Herman van Hazendonk --- include/dt-bindings/reset/qcom,mmcc-msm8660.h | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 include/dt-bindings/reset/qcom,mmcc-msm8660.h diff --git a/include/dt-bindings/reset/qcom,mmcc-msm8660.h b/include/dt-bin= dings/reset/qcom,mmcc-msm8660.h new file mode 100644 index 000000000000..c3ffd57834c9 --- /dev/null +++ b/include/dt-bindings/reset/qcom,mmcc-msm8660.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Reset bindings for the MSM8x60 family (MSM8260/MSM8660/APQ8060) Multime= dia Clock + * Controller (MMCC). + * + * MSM8260, MSM8660 and APQ8060 are the same Scorpion-class MSM8x60 SoC + * with different bin/feature labels. MSM8960 is a newer generation (Krait) + * =E2=80=94 its reset bindings live in + * and must not be reused here. + * + * IDs intentionally match the numeric values used by the original shared + * mmcc-msm8960.h so the driver's qcom_reset_map array indexing is preserv= ed; + * only the resets actually implemented by mmcc-msm8660.c are defined. + */ + +#ifndef _DT_BINDINGS_RESET_MSM_MMCC_8660_H +#define _DT_BINDINGS_RESET_MSM_MMCC_8660_H + +#define VPE_AXI_RESET 0 +#define IJPEG_AXI_RESET 1 +#define MPD_AXI_RESET 2 +#define VFE_AXI_RESET 3 +#define SP_AXI_RESET 4 +#define VCODEC_AXI_RESET 5 +#define ROT_AXI_RESET 6 +#define VCODEC_AXI_A_RESET 7 +#define VCODEC_AXI_B_RESET 8 +#define FAB_S3_AXI_RESET 9 +#define FAB_S2_AXI_RESET 10 +#define FAB_S1_AXI_RESET 11 +#define FAB_S0_AXI_RESET 12 +#define SMMU_GFX3D_ABH_RESET 13 +#define SMMU_VPE_AHB_RESET 14 +#define SMMU_VFE_AHB_RESET 15 +#define SMMU_ROT_AHB_RESET 16 +#define SMMU_VCODEC_B_AHB_RESET 17 +#define SMMU_VCODEC_A_AHB_RESET 18 +#define SMMU_MDP1_AHB_RESET 19 +#define SMMU_MDP0_AHB_RESET 20 +#define SMMU_JPEGD_AHB_RESET 21 +#define SMMU_IJPEG_AHB_RESET 22 +#define APU_AHB_RESET 25 +#define CSI_AHB_RESET 26 +#define TV_ENC_AHB_RESET 27 +#define VPE_AHB_RESET 28 +#define FABRIC_AHB_RESET 29 +#define GFX2D0_AHB_RESET 30 +#define GFX2D1_AHB_RESET 31 +#define GFX3D_AHB_RESET 32 +#define HDMI_AHB_RESET 33 +#define MSSS_IMEM_AHB_RESET 34 +#define IJPEG_AHB_RESET 35 +#define DSI_M_AHB_RESET 36 +#define DSI_S_AHB_RESET 37 +#define JPEGD_AHB_RESET 38 +#define MDP_AHB_RESET 39 +#define ROT_AHB_RESET 40 +#define VCODEC_AHB_RESET 41 +#define VFE_AHB_RESET 42 +#define CSIPHY0_RESET 47 +#define CSIPHY1_RESET 48 +#define VFE_CSI_RESET 50 +#define MDP_RESET 51 +#define AMP_RESET 52 +#define JPEGD_RESET 53 +#define CSI1_RESET 54 +#define VPE_RESET 55 +#define MMSS_FABRIC_RESET 56 +#define VFE_RESET 57 +#define GFX3D_RESET 60 +#define HDMI_RESET 61 +#define MMSS_IMEM_RESET 62 +#define IJPEG_RESET 63 +#define CSI0_RESET 64 +#define DSI_RESET 65 +#define VCODEC_RESET 66 +#define MDP_TV_RESET 67 +#define MDP_VSYNC_RESET 68 +#define ROT_RESET 69 +#define TV_HDMI_RESET 70 +#define TV_ENC_RESET 71 +#define GFX3D_AXI_RESET 75 +#define CSI_RDI_RESET 79 +#define CSI_PIX_RESET 80 + +#endif --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound3.mail.transip.nl (outbound3.mail.transip.nl [136.144.136.12]) (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 74DEE263C8F; Sat, 30 May 2026 14:00:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149624; cv=none; b=oYcAdIuY13E4ZxS3xoAnAUarjcGCN0/8zdMKw0C4smFcw41IFqC0ilxGLCuAcPpkvQLNwpY6zY/VJXuwQUxPuMS2WW4s7cdXfe9M9FSkrTtzdcjOAXbpX+e/FCL1YZOcX5C73PmhT4Eu46JyXxaIIbkkHo6lTTlobr3YdzGfwT8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149624; c=relaxed/simple; bh=6uFo648GjSes8cwceja6oC0kgtpMdCdYW+BjR1mK5Oc=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=NthETVmuIuglT24HAa4BbJKxNjj/0JaHLEpi+jdXqdt/wYb0JPl9W0vL7cluzctUx5uOV6/0wL58P2mvfMzGKfepC/ylLeM3LolP3k8fARs7/Sb0QewY1Itzk9QQY6HhEPHAg0bpR19NHaUU+wHY46/MRbKf6K+FIpa/S3DZnWw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=TbaqllsE; arc=none smtp.client-ip=136.144.136.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="TbaqllsE" Received: from submission14.mail.transip.nl (unknown [10.103.8.165]) by outbound3.mail.transip.nl (Postfix) with ESMTP id 4gSMLQ4Qy9zrJ4c; Sat, 30 May 2026 16:00:14 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission14.mail.transip.nl (Postfix) with ESMTPA id 4gSMLP5YfPz3RgQXs; Sat, 30 May 2026 16:00:13 +0200 (CEST) From: Herman van Hazendonk To: Conor Dooley , devicetree@vger.kernel.org, Georgi Djakov , Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Rob Herring Subject: [PATCH 2/2] interconnect: qcom: add MSM8x60 NoC driver Date: Sat, 30 May 2026 16:00:12 +0200 Message-ID: <208381c551886a53541b162e69b5c1e13aeec531.1780148149.git.github.com@herrie.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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 X-Scanned-By: ClueGetter at submission14.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149613; h=from:subject:to:references: in-reply-to:date:mime-version:content-type; bh=0mpSme5TGm9qJMnph77ukirWGvXYx0QYRlZjg9v9u5A=; b=TbaqllsEXBqlsHnrfqQBBO0ZX3N0AENCK8L+2eOLo6igLJInMyWtig/zeFeISMAiE3KuII +ZU7VMMn+qMKE2bH2Naf5hnnOdazO3BKzcEffcUHAF3ZxX1eJBJ7vw+tIKyNep655jRJV1 Bacpa6NxSKHC84VaScFTiJuOe0RKTx1njUalh8+Q5JKidNCLtSpZZ425j7Reg2sqVPycPH R9Z+GqMc1b6FAA/o9CYboEy7Dnxq+voLb03iATZwIhr4Sx2/zgBJVMrZqtvR/v4dseikqu CCONQkUAslPBKici3VzqYBxfLk5pQ2w8N96YZ4EtQPTXaiNqeMVjW8dryx/Ymg== X-Report-Abuse-To: abuse@transip.nl Add a Qualcomm interconnect driver for the MSM8x60 family (MSM8260/MSM8660/APQ8060), modelling the four fabrics that connect masters and slaves on these Scorpion-class SoCs: - AFAB : Application/CPU fabric - SFAB : System fabric (peripherals, USB, SDCC, etc.) - MMFAB : Multimedia fabric (MDP, VFE, VIDC, GPU, JPEG, VPE, ROT) - DFAB : Daytona fabric (low-bandwidth peripherals) The driver implements the interconnect-provider API so that consumer drivers (display, camera, video, GPU, USB, MMC) can request bandwidth between specific masters and slaves via icc_set_bw(), letting the firmware-managed bus-scaling decide actual NoC clock rates. Used on the HP TouchPad (Tenderloin) and on the Pre3 / Veer form-factors; without it, the multimedia and storage paths are starved of bandwidth and run at minimum NoC clocks. Signed-off-by: Herman van Hazendonk --- drivers/interconnect/qcom/Kconfig | 10 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/msm8660.c | 1008 +++++++++++++++++++++++++++ 3 files changed, 1020 insertions(+) create mode 100644 drivers/interconnect/qcom/msm8660.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/= Kconfig index 786b4eda44b4..17364be522c7 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -80,6 +80,16 @@ config INTERCONNECT_QCOM_MSM8953 This is a driver for the Qualcomm Network-on-Chip on msm8953-based platforms. =20 +config INTERCONNECT_QCOM_MSM8660 + tristate "Qualcomm MSM8x60 interconnect driver" + depends on INTERCONNECT_QCOM + depends on MFD_QCOM_RPM + help + This is a driver for the Qualcomm fabric-based bus interconnect + on MSM8x60 family (MSM8260/MSM8660/APQ8060) platforms (e.g., HP TouchPa= d). + The driver manages APPSS, System, and MMSS fabrics and sends + per-port bandwidth arbitration requests to RPM firmware. + config INTERCONNECT_QCOM_MSM8974 tristate "Qualcomm MSM8974 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom= /Makefile index cdf2c6c9fbf3..0c849c30a907 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -13,6 +13,7 @@ qnoc-msm8916-objs :=3D msm8916.o qnoc-msm8937-objs :=3D msm8937.o qnoc-msm8939-objs :=3D msm8939.o qnoc-msm8953-objs :=3D msm8953.o +qnoc-msm8660-objs :=3D msm8660.o qnoc-msm8974-objs :=3D msm8974.o qnoc-msm8976-objs :=3D msm8976.o qnoc-msm8996-objs :=3D msm8996.o @@ -58,6 +59,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) +=3D qnoc-msm8916= .o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8937) +=3D qnoc-msm8937.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8939) +=3D qnoc-msm8939.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8953) +=3D qnoc-msm8953.o +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8660) +=3D qnoc-msm8660.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) +=3D qnoc-msm8974.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8976) +=3D qnoc-msm8976.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8996) +=3D qnoc-msm8996.o diff --git a/drivers/interconnect/qcom/msm8660.c b/drivers/interconnect/qco= m/msm8660.c new file mode 100644 index 000000000000..eab5b928f681 --- /dev/null +++ b/drivers/interconnect/qcom/msm8660.c @@ -0,0 +1,1008 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm MSM8x60 family (MSM8260/MSM8660/APQ8060) interconnect driver + * +-2026 Herman van Hazendonk + * + * Based on msm8974.c by Brian Masney + * and webOS kernel msm_bus_board_8660.c / msm_bus_fabric.c + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * MSM8x60 has a fabric-based bus architecture: + * + * +------------------+ + * | APPSS Fabric | (CPU, L2, Memory) + * +--------+---------+ + * | + * +-------------+-------------+ + * | | + * +------+------+ +-------+-------+ + * | MMSS Fabric | | System Fabric | + * | (Display, | | (Peripherals, | + * | Camera, | | DMA, etc) | + * | Video) | +-------+-------+ + * +-------------+ | + * +---------+---------+ + * | | + * +------+------+ +------+------+ + * | System FPB | | CPSS FPB | + * | (RPM, PMIC) | | (GSBI, USB) | + * +-------------+ +-------------+ + * + * Each fabric has an RPM arbitration interface that programs per-port + * bandwidth and priority tier via MM_FABRIC_ARB / SYS_FABRIC_ARB / + * APPS_FABRIC_ARB registers. The webOS kernel sent these as packed + * u16 arrays (bwsum + arb) through msm_rpm_set(). This driver uses + * the mainline qcom_rpm_write() interface to do the same. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Internal node IDs - these map to the DT binding IDs plus fabric offset = */ +enum { + /* APPSS Fabric nodes */ + MSM8660_AFAB_MAS_AMPSS_M0 =3D 1, + MSM8660_AFAB_MAS_AMPSS_M1, + MSM8660_AFAB_SLV_EBI_CH0, + MSM8660_AFAB_SLV_AMPSS_L2, + MSM8660_AFAB_TO_MMSS, + MSM8660_AFAB_TO_SYSTEM, + + /* System Fabric nodes */ + MSM8660_SFAB_MAS_APPSS, + MSM8660_SFAB_MAS_SPS, + MSM8660_SFAB_MAS_ADM0_PORT0, + MSM8660_SFAB_MAS_ADM0_PORT1, + MSM8660_SFAB_MAS_ADM1_PORT0, + MSM8660_SFAB_MAS_ADM1_PORT1, + MSM8660_SFAB_MAS_LPASS_PROC, + MSM8660_SFAB_MAS_MSS_PROCI, + MSM8660_SFAB_MAS_MSS_PROCD, + MSM8660_SFAB_MAS_MSS_MDM_PORT0, + MSM8660_SFAB_MAS_LPASS, + MSM8660_SFAB_MAS_MMSS_FPB, + MSM8660_SFAB_MAS_ADM1_CI, + MSM8660_SFAB_MAS_ADM0_CI, + MSM8660_SFAB_MAS_MSS_MDM_PORT1, + MSM8660_SFAB_MAS_USB_HS, + MSM8660_SFAB_TO_APPSS, + MSM8660_SFAB_TO_SYSTEM_FPB, + MSM8660_SFAB_TO_CPSS_FPB, + MSM8660_SFAB_SLV_SPS, + MSM8660_SFAB_SLV_SYSTEM_IMEM, + MSM8660_SFAB_SLV_AMPSS, + MSM8660_SFAB_SLV_MSS, + MSM8660_SFAB_SLV_LPASS, + MSM8660_SFAB_SLV_MMSS_FPB, + MSM8660_SFAB_TO_DFAB, + + /* Daytona Fabric nodes (DFAB) - peripheral bus */ + MSM8660_DFAB_MAS_SDC1, + MSM8660_DFAB_MAS_SDC2, + MSM8660_DFAB_MAS_SDC3, + MSM8660_DFAB_MAS_SDC4, + MSM8660_DFAB_MAS_SDC5, + MSM8660_DFAB_MAS_ADM0_MASTER, + MSM8660_DFAB_MAS_ADM1_MASTER, + MSM8660_DFAB_TO_SFAB, + MSM8660_DFAB_SLV_SDC1, + MSM8660_DFAB_SLV_SDC2, + MSM8660_DFAB_SLV_SDC3, + MSM8660_DFAB_SLV_SDC4, + MSM8660_DFAB_SLV_SDC5, + MSM8660_DFAB_MAS_USB_HS, /* USB HS DFAB voter */ + MSM8660_DFAB_MAS_DSPS, /* DSPS DFAB voter */ + + /* MMSS Fabric nodes */ + MSM8660_MMFAB_MAS_MDP_PORT0, + MSM8660_MMFAB_MAS_MDP_PORT1, + MSM8660_MMFAB_MAS_ADM1_PORT0, + MSM8660_MMFAB_MAS_ROTATOR, + MSM8660_MMFAB_MAS_GRAPHICS_3D, + MSM8660_MMFAB_MAS_JPEG_DEC, + MSM8660_MMFAB_MAS_GRAPHICS_2D_CORE0, + MSM8660_MMFAB_MAS_VFE, + MSM8660_MMFAB_MAS_VPE, + MSM8660_MMFAB_MAS_JPEG_ENC, + MSM8660_MMFAB_MAS_GRAPHICS_2D_CORE1, + MSM8660_MMFAB_MAS_HD_CODEC_PORT0, + MSM8660_MMFAB_MAS_HD_CODEC_PORT1, + MSM8660_MMFAB_TO_APPSS, + MSM8660_MMFAB_SLV_SMI, + MSM8660_MMFAB_SLV_MM_IMEM, +}; + +#define to_msm8660_icc_provider(_provider) \ + container_of(_provider, struct msm8660_icc_provider, provider) + +/* + * Minimum fabric clock rate to prevent bus starvation. + * + * When no consumers request bandwidth, the rate calculation yields 0, + * causing fabric clocks to drop to minimum. This creates bimodal + * performance: fast when other subsystems (like display) happen to + * request bandwidth, slow otherwise. + * + * 384 MHz keeps fabric fast during concurrent MDP display scanout + * and USB gadget traffic. webOS docs: "AXI bus frequency needs to be + * kept at maximum value while USB data transfers are happening." + * 266 MHz was insufficient - USB crashed during display activity. + */ +#define MSM8660_FABRIC_MIN_RATE 384000000UL /* 384 MHz */ + +/* + * Maximum RPM ARB buffer size across all fabrics. + * MM fabric is largest at 23 u32 words. + */ +#define MSM8660_MAX_RPM_BUF 23 + +/* + * RPM fabric arbitration data format (from webOS msm_bus_fabric.c): + * + * Each u16 arb entry: bit 15 =3D tier (1=3DTIER1 high priority), bits 14-= 0 =3D BW + * Bandwidth is in 128KB units (bytes >> 17). + * Two u16 values are packed into each u32 RPM register word. + * + * Buffer layout: [bwsum pairs] [arb pairs] + * bwsum[slave_port] =3D total bandwidth to that slave + * arb[(tier-1)*nmasters + master_port] =3D per-master arbitration entry + */ +#define ARB_BWMASK 0x7FFF +#define ARB_TIERMASK 0x8000 +#define ARB_TIER1 1 +#define ARB_TIER2 2 + +static const struct clk_bulk_data msm8660_afab_clocks[] =3D { + { .id =3D "bus" }, + { .id =3D "bus_a" }, + { .id =3D "ebi1" }, + { .id =3D "ebi1_a" }, +}; + +static const struct clk_bulk_data msm8660_sfab_clocks[] =3D { + { .id =3D "bus" }, + { .id =3D "bus_a" }, +}; + +static const struct clk_bulk_data msm8660_mmfab_clocks[] =3D { + { .id =3D "bus" }, + { .id =3D "bus_a" }, + { .id =3D "smi" }, + { .id =3D "smi_a" }, +}; + +static const struct clk_bulk_data msm8660_dfab_clocks[] =3D { + { .id =3D "bus" }, + { .id =3D "bus_a" }, +}; + +/** + * struct msm8660_icc_node - MSM8660 specific interconnect nodes + * @name: the node name used in debugfs + * @id: a unique node identifier + * @links: an array of nodes where we can go next while traversing + * @num_links: the total number of @links + * @buswidth: width of the interconnect between a node and the bus (bytes) + * @rate: current bus clock rate in Hz + * @mas_port: master port index for RPM ARB (-1 if not a master) + * @slv_port: slave port index for RPM bwsum (-1 if not a slave) + * @mas_tier: master priority tier (ARB_TIER1 or ARB_TIER2, 0 if N/A) + */ +struct msm8660_icc_node { + unsigned char *name; + u16 id; +#define MSM8660_ICC_MAX_LINKS 3 + u16 links[MSM8660_ICC_MAX_LINKS]; + u16 num_links; + u16 buswidth; + u64 rate; + s8 mas_port; + s8 slv_port; + u8 mas_tier; +}; + +/** + * struct msm8660_icc_desc - Fabric descriptor + * @nodes: array of node pointers + * @num_nodes: number of nodes + * @bus_clks: clock definitions + * @num_clks: number of clocks + * @rpm_resource: QCOM_RPM_*_FABRIC_ARB constant, or -1 for no ARB + * @nmasters: number of master ports in this fabric (for ARB array sizing) + * @nslaves: number of slave ports in this fabric (for bwsum array sizing) + * @ntieredslaves: number of tiered slaves (ARB rows) + * @default_tiered_slave: 1-based index of default tiered slave for masters + * @rpm_buf_size: number of u32 words for RPM write + */ +struct msm8660_icc_desc { + struct msm8660_icc_node * const *nodes; + size_t num_nodes; + const struct clk_bulk_data *bus_clks; + size_t num_clks; + int rpm_resource; + u8 nmasters; + u8 nslaves; + u8 ntieredslaves; + u8 default_tiered_slave; + u8 rpm_buf_size; +}; + +/** + * struct msm8660_icc_provider - MSM8660 specific interconnect provider + * @provider: generic interconnect provider + * @bus_clks: the clk_bulk_data table of bus clocks + * @num_clks: the total number of clk_bulk_data entries + * @rpm: RPM handle for fabric arbitration writes + * @desc: fabric descriptor with RPM metadata + * @arb: pre-allocated arbitration array (nmasters * ntieredslaves u16 ent= ries) + * @bwsum: pre-allocated bandwidth sum array (nslaves u16 entries) + * @rpm_buf: pre-allocated RPM write buffer (rpm_buf_size u32 entries) + */ +struct msm8660_icc_provider { + struct icc_provider provider; + struct clk_bulk_data *bus_clks; + int num_clks; + struct qcom_rpm *rpm; + const struct msm8660_icc_desc *desc; + u16 *arb; + u16 *bwsum; + u32 *rpm_buf; +}; + +/* + * Node definitions with RPM port mapping. + * + * DEFINE_QNODE(_name, _id, _buswidth, _mas_port, _slv_port, _tier, links.= ..) + * + * _mas_port: master port index for RPM ARB array (-1 if not a master) + * _slv_port: slave port index for RPM bwsum array (-1 if not a slave) + * _tier: master priority tier (ARB_TIER1=3D1, ARB_TIER2=3D2, 0 if N/A) + */ +#define DEFINE_QNODE(_name, _id, _buswidth, _mas, _slv, _tier, ...) \ + static struct msm8660_icc_node _name =3D { \ + .name =3D #_name, \ + .id =3D _id, \ + .buswidth =3D _buswidth, \ + .num_links =3D COUNT_ARGS(__VA_ARGS__), \ + .links =3D { __VA_ARGS__ }, \ + .mas_port =3D _mas, \ + .slv_port =3D _slv, \ + .mas_tier =3D _tier, \ + } + +/* + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * APPSS Fabric nodes + * + * 4 masters, 4 slaves, 2 tiered slaves + * Master ports: SMPSS_M0=3D0, SMPSS_M1=3D1, FAB_MMSS=3D2, FAB_SYSTEM=3D3 + * Slave ports: EBI_CH0=3D0, SMPSS_L2=3D1, MMSS_FAB=3D2, SYSTEM_FAB=3D3 + * Default target: tiered slave 1 (EBI_CH0) + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + */ +DEFINE_QNODE(mas_ampss_m0, MSM8660_AFAB_MAS_AMPSS_M0, 8, 0, -1, ARB_TIER2, + MSM8660_AFAB_SLV_EBI_CH0, MSM8660_AFAB_TO_MMSS, MSM8660_AFAB_TO_SYST= EM); +DEFINE_QNODE(mas_ampss_m1, MSM8660_AFAB_MAS_AMPSS_M1, 8, 1, -1, ARB_TIER2, + MSM8660_AFAB_SLV_EBI_CH0, MSM8660_AFAB_TO_MMSS, MSM8660_AFAB_TO_SYST= EM); +DEFINE_QNODE(slv_ebi_ch0, MSM8660_AFAB_SLV_EBI_CH0, 8, -1, 0, 0); +DEFINE_QNODE(slv_ampss_l2, MSM8660_AFAB_SLV_AMPSS_L2, 8, -1, 1, 0); +/* + * Gateway nodes need links to both the cross-fabric gateway AND the memory + * slave to enable cross-fabric paths. Without link to EBI_CH0, path_find() + * can't route from MMSS/System fabric masters to main memory. + * + * AFAB_TO_MMSS doubles as AFAB master port 2 (the FAB_MMSS master). MDP + * scanout and GPU traffic enter AFAB through this gateway. Mark it + * ARB_TIER1 so display/multimedia traffic keeps priority over CPU L2 + * misses inside the APPSS fabric =E2=80=94 without this, MDP TIER1 priori= ty + * earned in MMFAB is dropped at the AFAB boundary and MDP fetches lose + * arbitration to CPU traffic, producing PRIMARY_INTF_UDERRUN. + */ +DEFINE_QNODE(afab_to_mmss, MSM8660_AFAB_TO_MMSS, 8, 2, 2, ARB_TIER1, + MSM8660_MMFAB_TO_APPSS, MSM8660_AFAB_SLV_EBI_CH0); +DEFINE_QNODE(afab_to_system, MSM8660_AFAB_TO_SYSTEM, 8, 3, 3, ARB_TIER2, + MSM8660_SFAB_TO_APPSS, MSM8660_AFAB_SLV_EBI_CH0); + +static struct msm8660_icc_node * const msm8660_afab_nodes[] =3D { + [AFAB_MAS_AMPSS_M0] =3D &mas_ampss_m0, + [AFAB_MAS_AMPSS_M1] =3D &mas_ampss_m1, + [AFAB_SLV_EBI_CH0] =3D &slv_ebi_ch0, + [AFAB_SLV_AMPSS_L2] =3D &slv_ampss_l2, + [AFAB_TO_MMSS] =3D &afab_to_mmss, + [AFAB_TO_SYSTEM] =3D &afab_to_system, +}; + +static const struct msm8660_icc_desc msm8660_afab =3D { + .nodes =3D msm8660_afab_nodes, + .num_nodes =3D ARRAY_SIZE(msm8660_afab_nodes), + .bus_clks =3D msm8660_afab_clocks, + .num_clks =3D ARRAY_SIZE(msm8660_afab_clocks), + .rpm_resource =3D QCOM_RPM_APPS_FABRIC_ARB, + .nmasters =3D 4, + .nslaves =3D 4, + .ntieredslaves =3D 2, + .default_tiered_slave =3D 1, /* EBI_CH0 */ + .rpm_buf_size =3D 6, +}; + +/* + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * System Fabric nodes + * + * 17 masters, 9 slaves, 2 tiered slaves + * Master ports: see enum msm_bus_8660_master_ports_type in webOS + * Slave ports: APPSS_FAB=3D0, SPS=3D1, SYSTEM_IMEM=3D2, SMPSS=3D3, MSS= =3D4, + * LPASS=3D5, CPSS_FPB=3D6, SYSTEM_FPB=3D7, MMSS_FPB=3D8 + * Default target: tiered slave 1 (APPSS gateway) + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + */ +DEFINE_QNODE(sfab_mas_appss, MSM8660_SFAB_MAS_APPSS, 8, 0, -1, ARB_TIER2, + MSM8660_AFAB_TO_SYSTEM); +DEFINE_QNODE(sfab_mas_sps, MSM8660_SFAB_MAS_SPS, 8, 1, -1, ARB_TIER2, + MSM8660_SFAB_SLV_SPS); +/* + * ADM DMA masters - route through SFAB_TO_APPSS to reach EBI memory. + * Path: ADM -> SFAB_TO_APPSS -> AFAB_TO_SYSTEM -> AFAB_SLV_EBI_CH0 + * This enables proper EBI bandwidth voting for DMA operations. + */ +DEFINE_QNODE(sfab_mas_adm0_port0, MSM8660_SFAB_MAS_ADM0_PORT0, 8, 2, -1, A= RB_TIER2, + MSM8660_SFAB_TO_APPSS); +DEFINE_QNODE(sfab_mas_adm0_port1, MSM8660_SFAB_MAS_ADM0_PORT1, 8, 3, -1, A= RB_TIER2, + MSM8660_SFAB_TO_APPSS); +DEFINE_QNODE(sfab_mas_adm1_port0, MSM8660_SFAB_MAS_ADM1_PORT0, 8, 4, -1, A= RB_TIER2, + MSM8660_SFAB_TO_APPSS); +DEFINE_QNODE(sfab_mas_adm1_port1, MSM8660_SFAB_MAS_ADM1_PORT1, 8, 5, -1, A= RB_TIER2, + MSM8660_SFAB_TO_APPSS); +DEFINE_QNODE(sfab_mas_lpass_proc, MSM8660_SFAB_MAS_LPASS_PROC, 8, 6, -1, A= RB_TIER2); +DEFINE_QNODE(sfab_mas_mss_proci, MSM8660_SFAB_MAS_MSS_PROCI, 8, 7, -1, ARB= _TIER2); +DEFINE_QNODE(sfab_mas_mss_procd, MSM8660_SFAB_MAS_MSS_PROCD, 8, 8, -1, ARB= _TIER2); +DEFINE_QNODE(sfab_mas_mss_mdm_port0, MSM8660_SFAB_MAS_MSS_MDM_PORT0, 8, 9,= -1, ARB_TIER2); +DEFINE_QNODE(sfab_mas_lpass, MSM8660_SFAB_MAS_LPASS, 8, 10, -1, ARB_TIER2); +DEFINE_QNODE(sfab_mas_mmss_fpb, MSM8660_SFAB_MAS_MMSS_FPB, 8, 13, -1, ARB_= TIER2); +DEFINE_QNODE(sfab_mas_adm1_ci, MSM8660_SFAB_MAS_ADM1_CI, 8, 14, -1, ARB_TI= ER2); +DEFINE_QNODE(sfab_mas_adm0_ci, MSM8660_SFAB_MAS_ADM0_CI, 8, 15, -1, ARB_TI= ER2); +DEFINE_QNODE(sfab_mas_mss_mdm_port1, MSM8660_SFAB_MAS_MSS_MDM_PORT1, 8, 16= , -1, ARB_TIER2); +/* USB HS has no dedicated master port in webOS SFAB - bandwidth voting on= ly */ +DEFINE_QNODE(sfab_mas_usb_hs, MSM8660_SFAB_MAS_USB_HS, 8, -1, -1, 0, + MSM8660_SFAB_TO_APPSS); +DEFINE_QNODE(sfab_to_appss, MSM8660_SFAB_TO_APPSS, 8, -1, 0, 0, + MSM8660_AFAB_TO_SYSTEM); +DEFINE_QNODE(sfab_to_system_fpb, MSM8660_SFAB_TO_SYSTEM_FPB, 4, -1, 7, 0); +DEFINE_QNODE(sfab_to_cpss_fpb, MSM8660_SFAB_TO_CPSS_FPB, 4, -1, 6, 0); +DEFINE_QNODE(sfab_slv_sps, MSM8660_SFAB_SLV_SPS, 8, -1, 1, 0); +DEFINE_QNODE(sfab_slv_system_imem, MSM8660_SFAB_SLV_SYSTEM_IMEM, 8, -1, 2,= 0); +DEFINE_QNODE(sfab_slv_ampss, MSM8660_SFAB_SLV_AMPSS, 8, -1, 3, 0); +DEFINE_QNODE(sfab_slv_mss, MSM8660_SFAB_SLV_MSS, 8, -1, 4, 0); +DEFINE_QNODE(sfab_slv_lpass, MSM8660_SFAB_SLV_LPASS, 8, -1, 5, 0); +DEFINE_QNODE(sfab_slv_mmss_fpb, MSM8660_SFAB_SLV_MMSS_FPB, 8, -1, 8, 0); +/* + * Gateway to DFAB: links to DFAB_TO_SFAB for path traversal. + * Also links to SFAB_TO_APPSS to enable DFAB->SFAB->AFAB->memory paths. + * No slave port in webOS SFAB config (DFAB is separate fabric). + */ +DEFINE_QNODE(sfab_to_dfab, MSM8660_SFAB_TO_DFAB, 8, -1, -1, 0, + MSM8660_DFAB_TO_SFAB, MSM8660_SFAB_TO_APPSS); + +static struct msm8660_icc_node * const msm8660_sfab_nodes[] =3D { + [SFAB_MAS_APPSS] =3D &sfab_mas_appss, + [SFAB_MAS_SPS] =3D &sfab_mas_sps, + [SFAB_MAS_ADM0_PORT0] =3D &sfab_mas_adm0_port0, + [SFAB_MAS_ADM0_PORT1] =3D &sfab_mas_adm0_port1, + [SFAB_MAS_ADM1_PORT0] =3D &sfab_mas_adm1_port0, + [SFAB_MAS_ADM1_PORT1] =3D &sfab_mas_adm1_port1, + [SFAB_MAS_LPASS_PROC] =3D &sfab_mas_lpass_proc, + [SFAB_MAS_MSS_PROCI] =3D &sfab_mas_mss_proci, + [SFAB_MAS_MSS_PROCD] =3D &sfab_mas_mss_procd, + [SFAB_MAS_MSS_MDM_PORT0] =3D &sfab_mas_mss_mdm_port0, + [SFAB_MAS_LPASS] =3D &sfab_mas_lpass, + [SFAB_MAS_MMSS_FPB] =3D &sfab_mas_mmss_fpb, + [SFAB_MAS_ADM1_CI] =3D &sfab_mas_adm1_ci, + [SFAB_MAS_ADM0_CI] =3D &sfab_mas_adm0_ci, + [SFAB_MAS_MSS_MDM_PORT1] =3D &sfab_mas_mss_mdm_port1, + [SFAB_MAS_USB_HS] =3D &sfab_mas_usb_hs, + [SFAB_TO_APPSS] =3D &sfab_to_appss, + [SFAB_TO_SYSTEM_FPB] =3D &sfab_to_system_fpb, + [SFAB_TO_CPSS_FPB] =3D &sfab_to_cpss_fpb, + [SFAB_SLV_SPS] =3D &sfab_slv_sps, + [SFAB_SLV_SYSTEM_IMEM] =3D &sfab_slv_system_imem, + [SFAB_SLV_AMPSS] =3D &sfab_slv_ampss, + [SFAB_SLV_MSS] =3D &sfab_slv_mss, + [SFAB_SLV_LPASS] =3D &sfab_slv_lpass, + [SFAB_SLV_MMSS_FPB] =3D &sfab_slv_mmss_fpb, + [SFAB_TO_DFAB] =3D &sfab_to_dfab, +}; + +static const struct msm8660_icc_desc msm8660_sfab =3D { + .nodes =3D msm8660_sfab_nodes, + .num_nodes =3D ARRAY_SIZE(msm8660_sfab_nodes), + .bus_clks =3D msm8660_sfab_clocks, + .num_clks =3D ARRAY_SIZE(msm8660_sfab_clocks), + .rpm_resource =3D QCOM_RPM_SYS_FABRIC_ARB, + .nmasters =3D 17, + .nslaves =3D 9, + .ntieredslaves =3D 2, + .default_tiered_slave =3D 1, /* APPSS gateway */ + .rpm_buf_size =3D 22, +}; + +/* + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * MMSS Fabric nodes - Multimedia subsystem (MDP, camera, video, GPU) + * + * 14 masters, 4 slaves, 3 tiered slaves + * Master ports: MDP0=3D0, MDP1=3D1, ADM1=3D2, ROT=3D3, 3D=3D4, JPEG_DEC= =3D5, + * 2D_CORE0=3D6, VFE=3D7, VPE=3D8, JPEG_ENC=3D9, 2D_CORE1=3D= 10, + * (APPS_FAB=3D11), HD_CODEC0=3D12, HD_CODEC1=3D13 + * Slave ports: SMI=3D0, APPSS_FAB=3D1, (APPSS_FAB_1=3D2), MM_IMEM=3D3 + * Tiered slaves: SMI=3D1, APPSS_FAB=3D2, MM_IMEM=3D3 + * Default target: tiered slave 2 (APPSS gateway -> main memory) + * + * MDP ports get TIER1 (high priority) for guaranteed display refresh. + * All other masters get TIER2 (default priority). + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + */ +DEFINE_QNODE(mmfab_mas_mdp_port0, MSM8660_MMFAB_MAS_MDP_PORT0, 16, 0, -1, = ARB_TIER1, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_mdp_port1, MSM8660_MMFAB_MAS_MDP_PORT1, 16, 1, -1, = ARB_TIER1, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_adm1_port0, MSM8660_MMFAB_MAS_ADM1_PORT0, 8, 2, -1,= ARB_TIER2); +DEFINE_QNODE(mmfab_mas_rotator, MSM8660_MMFAB_MAS_ROTATOR, 16, 3, -1, ARB_= TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_graphics_3d, MSM8660_MMFAB_MAS_GRAPHICS_3D, 16, 4, = -1, ARB_TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_jpeg_dec, MSM8660_MMFAB_MAS_JPEG_DEC, 16, 5, -1, AR= B_TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_graphics_2d_core0, MSM8660_MMFAB_MAS_GRAPHICS_2D_CO= RE0, 16, + 6, -1, ARB_TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_vfe, MSM8660_MMFAB_MAS_VFE, 16, 7, -1, ARB_TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_vpe, MSM8660_MMFAB_MAS_VPE, 16, 8, -1, ARB_TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_jpeg_enc, MSM8660_MMFAB_MAS_JPEG_ENC, 16, 9, -1, AR= B_TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_graphics_2d_core1, MSM8660_MMFAB_MAS_GRAPHICS_2D_CO= RE1, 16, + 10, -1, ARB_TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_hd_codec_port0, MSM8660_MMFAB_MAS_HD_CODEC_PORT0, 1= 6, + 12, -1, ARB_TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +DEFINE_QNODE(mmfab_mas_hd_codec_port1, MSM8660_MMFAB_MAS_HD_CODEC_PORT1, 1= 6, + 13, -1, ARB_TIER2, + MSM8660_MMFAB_SLV_SMI, MSM8660_MMFAB_TO_APPSS); +/* + * Gateway from APPSS into MMSS: slave (port 1) for outbound traffic + * leaving MMSS, AND master (port 2) for inbound traffic arriving from + * APPSS (e.g. CPU memremap_wc accesses to SMI BOs). Without the master + * port and the forward links into MMFAB slaves (SMI / MM_IMEM), the + * ICC path-finder has no route from AMPSS_M0 to MMFAB_SLV_SMI; the + * cross-fabric gateway only worked for outbound traffic (MMSS masters + * reaching APPSS slaves like EBI). + * + * ARB_TIER1 keeps AMPSS->SMI traffic high-priority within MMFAB so + * CPU mmap reads/writes to SMI BOs don't get starved by MDP scanout. + */ +DEFINE_QNODE(mmfab_to_appss, MSM8660_MMFAB_TO_APPSS, 8, 2, 1, ARB_TIER1, + MSM8660_AFAB_TO_MMSS, + MSM8660_MMFAB_SLV_SMI, + MSM8660_MMFAB_SLV_MM_IMEM); +DEFINE_QNODE(mmfab_slv_smi, MSM8660_MMFAB_SLV_SMI, 16, -1, 0, 0); +DEFINE_QNODE(mmfab_slv_mm_imem, MSM8660_MMFAB_SLV_MM_IMEM, 8, -1, 3, 0); + +static struct msm8660_icc_node * const msm8660_mmfab_nodes[] =3D { + [MMFAB_MAS_MDP_PORT0] =3D &mmfab_mas_mdp_port0, + [MMFAB_MAS_MDP_PORT1] =3D &mmfab_mas_mdp_port1, + [MMFAB_MAS_ADM1_PORT0] =3D &mmfab_mas_adm1_port0, + [MMFAB_MAS_ROTATOR] =3D &mmfab_mas_rotator, + [MMFAB_MAS_GRAPHICS_3D] =3D &mmfab_mas_graphics_3d, + [MMFAB_MAS_JPEG_DEC] =3D &mmfab_mas_jpeg_dec, + [MMFAB_MAS_GRAPHICS_2D_CORE0] =3D &mmfab_mas_graphics_2d_core0, + [MMFAB_MAS_VFE] =3D &mmfab_mas_vfe, + [MMFAB_MAS_VPE] =3D &mmfab_mas_vpe, + [MMFAB_MAS_JPEG_ENC] =3D &mmfab_mas_jpeg_enc, + [MMFAB_MAS_GRAPHICS_2D_CORE1] =3D &mmfab_mas_graphics_2d_core1, + [MMFAB_MAS_HD_CODEC_PORT0] =3D &mmfab_mas_hd_codec_port0, + [MMFAB_MAS_HD_CODEC_PORT1] =3D &mmfab_mas_hd_codec_port1, + [MMFAB_TO_APPSS] =3D &mmfab_to_appss, + [MMFAB_SLV_SMI] =3D &mmfab_slv_smi, + [MMFAB_SLV_MM_IMEM] =3D &mmfab_slv_mm_imem, +}; + +static const struct msm8660_icc_desc msm8660_mmfab =3D { + .nodes =3D msm8660_mmfab_nodes, + .num_nodes =3D ARRAY_SIZE(msm8660_mmfab_nodes), + .bus_clks =3D msm8660_mmfab_clocks, + .num_clks =3D ARRAY_SIZE(msm8660_mmfab_clocks), + .rpm_resource =3D QCOM_RPM_MM_FABRIC_ARB, + .nmasters =3D 14, + .nslaves =3D 4, + .ntieredslaves =3D 3, + .default_tiered_slave =3D 2, /* APPSS gateway */ + .rpm_buf_size =3D 23, +}; + +/* + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * Daytona Fabric (DFAB) nodes - peripheral bus for SDCC and ADM DMA + * + * DFAB connects slower peripherals to SFAB via the DFAB_TO_SFAB gateway. + * SDCC controllers (eMMC, SD card) connect here. + * + * No RPM ARB for DFAB - it's a simple peripheral bus with clock-only cont= rol. + * + * USB HS is included as a DFAB voter for compatibility with the legacy + * webOS kernel clock voting mechanism. + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + */ +DEFINE_QNODE(dfab_mas_sdc1, MSM8660_DFAB_MAS_SDC1, 8, -1, -1, 0, + MSM8660_DFAB_TO_SFAB); +DEFINE_QNODE(dfab_mas_sdc2, MSM8660_DFAB_MAS_SDC2, 8, -1, -1, 0, + MSM8660_DFAB_TO_SFAB); +DEFINE_QNODE(dfab_mas_sdc3, MSM8660_DFAB_MAS_SDC3, 8, -1, -1, 0, + MSM8660_DFAB_TO_SFAB); +DEFINE_QNODE(dfab_mas_sdc4, MSM8660_DFAB_MAS_SDC4, 8, -1, -1, 0, + MSM8660_DFAB_TO_SFAB); +DEFINE_QNODE(dfab_mas_sdc5, MSM8660_DFAB_MAS_SDC5, 8, -1, -1, 0, + MSM8660_DFAB_TO_SFAB); +DEFINE_QNODE(dfab_mas_adm0_master, MSM8660_DFAB_MAS_ADM0_MASTER, 8, -1, -1= , 0, + MSM8660_DFAB_TO_SFAB); +DEFINE_QNODE(dfab_mas_adm1_master, MSM8660_DFAB_MAS_ADM1_MASTER, 8, -1, -1= , 0, + MSM8660_DFAB_TO_SFAB); +DEFINE_QNODE(dfab_to_sfab, MSM8660_DFAB_TO_SFAB, 8, -1, -1, 0, + MSM8660_SFAB_TO_DFAB); +DEFINE_QNODE(dfab_slv_sdc1, MSM8660_DFAB_SLV_SDC1, 8, -1, -1, 0); +DEFINE_QNODE(dfab_slv_sdc2, MSM8660_DFAB_SLV_SDC2, 8, -1, -1, 0); +DEFINE_QNODE(dfab_slv_sdc3, MSM8660_DFAB_SLV_SDC3, 8, -1, -1, 0); +DEFINE_QNODE(dfab_slv_sdc4, MSM8660_DFAB_SLV_SDC4, 8, -1, -1, 0); +DEFINE_QNODE(dfab_slv_sdc5, MSM8660_DFAB_SLV_SDC5, 8, -1, -1, 0); +/* USB HS DFAB voter - keeps DFAB clock stable during USB activity */ +DEFINE_QNODE(dfab_mas_usb_hs, MSM8660_DFAB_MAS_USB_HS, 8, -1, -1, 0, + MSM8660_DFAB_TO_SFAB); +/* DSPS DFAB voter - keeps DFAB clock stable during sensor activity */ +DEFINE_QNODE(dfab_mas_dsps, MSM8660_DFAB_MAS_DSPS, 8, -1, -1, 0, + MSM8660_DFAB_TO_SFAB); + +static struct msm8660_icc_node * const msm8660_dfab_nodes[] =3D { + [DFAB_MAS_SDC1] =3D &dfab_mas_sdc1, + [DFAB_MAS_SDC2] =3D &dfab_mas_sdc2, + [DFAB_MAS_SDC3] =3D &dfab_mas_sdc3, + [DFAB_MAS_SDC4] =3D &dfab_mas_sdc4, + [DFAB_MAS_SDC5] =3D &dfab_mas_sdc5, + [DFAB_MAS_ADM0_MASTER] =3D &dfab_mas_adm0_master, + [DFAB_MAS_ADM1_MASTER] =3D &dfab_mas_adm1_master, + [DFAB_TO_SFAB] =3D &dfab_to_sfab, + [DFAB_SLV_SDC1] =3D &dfab_slv_sdc1, + [DFAB_SLV_SDC2] =3D &dfab_slv_sdc2, + [DFAB_SLV_SDC3] =3D &dfab_slv_sdc3, + [DFAB_SLV_SDC4] =3D &dfab_slv_sdc4, + [DFAB_SLV_SDC5] =3D &dfab_slv_sdc5, + [DFAB_MAS_USB_HS] =3D &dfab_mas_usb_hs, + [DFAB_MAS_DSPS] =3D &dfab_mas_dsps, +}; + +static const struct msm8660_icc_desc msm8660_dfab =3D { + .nodes =3D msm8660_dfab_nodes, + .num_nodes =3D ARRAY_SIZE(msm8660_dfab_nodes), + .bus_clks =3D msm8660_dfab_clocks, + .num_clks =3D ARRAY_SIZE(msm8660_dfab_clocks), + .rpm_resource =3D -1, /* No RPM ARB for DFAB */ +}; + +/* + * Pack bwsum[] and arb[] arrays into the u32 RPM buffer. + * + * Two u16 values are packed per u32 word: lower 16 bits first, upper 16 n= ext. + * Layout: [bwsum pairs] then [arb pairs], handling odd boundaries. + * + * This matches the webOS msm_bus_fabric_rpm_commit() packing algorithm. + */ +static void msm8660_pack_rpm_data(const u16 *bwsum, int nslaves, + const u16 *arb, int arb_size, + u32 *buf) +{ + int i, index =3D 0; + + /* Pack bwsum pairs */ + for (i =3D 0; i + 1 < nslaves; i +=3D 2) { + buf[index] =3D ((u32)bwsum[i + 1] << 16) | bwsum[i]; + index++; + } + + /* Handle boundary between bwsum and arb for odd nslaves */ + if (nslaves & 1) { + buf[index] =3D ((u32)arb[0] << 16) | bwsum[i]; + index++; + i =3D 1; /* Start arb from index 1 (index 0 already packed) */ + } else { + i =3D 0; + } + + /* Pack arb pairs */ + for (; i + 1 < arb_size; i +=3D 2) { + buf[index] =3D ((u32)arb[i + 1] << 16) | arb[i]; + index++; + } + + /* Handle odd arb entry at end */ + if (i < arb_size) { + buf[index] =3D arb[i]; + index++; + } +} + +/* + * Send fabric arbitration data to RPM. + * + * Iterates over all ICC nodes in the provider, builds bwsum/arb arrays + * from their aggregated bandwidth, and sends the packed data to RPM. + */ +static void msm8660_rpm_commit(struct msm8660_icc_provider *qp) +{ + const struct msm8660_icc_desc *desc =3D qp->desc; + struct icc_provider *provider =3D &qp->provider; + int nm =3D desc->nmasters; + int ns =3D desc->nslaves; + int nts =3D desc->ntieredslaves; + int arb_size =3D nm * nts; + int def_ts =3D desc->default_tiered_slave; + struct icc_node *n; + int ret; + + memset(qp->bwsum, 0, ns * sizeof(u16)); + memset(qp->arb, 0, arb_size * sizeof(u16)); + memset(qp->rpm_buf, 0, desc->rpm_buf_size * sizeof(u32)); + + list_for_each_entry(n, &provider->nodes, node_list) { + struct msm8660_icc_node *qn =3D n->data; + u64 bw_bytes; + u16 bw_128k; + + /* Use max of avg and peak bandwidth, convert to 128KB units */ + bw_bytes =3D max(icc_units_to_bps(n->avg_bw), + icc_units_to_bps(n->peak_bw)); + do_div(bw_bytes, 8); /* bits -> bytes */ + bw_128k =3D (u16)min_t(u64, bw_bytes >> 17, ARB_BWMASK); + + /* Set arb entry for masters at their default tiered slave */ + if (qn->mas_port >=3D 0 && qn->mas_port < nm && def_ts > 0) { + int idx =3D (def_ts - 1) * nm + qn->mas_port; + u8 tier; + + if (idx < arb_size) { + tier =3D bw_128k ? qn->mas_tier : ARB_TIER2; + qp->arb[idx] =3D (tier =3D=3D ARB_TIER1 ? ARB_TIERMASK : 0) + | (bw_128k & ARB_BWMASK); + } + } + + /* Set bwsum for slaves */ + if (qn->slv_port >=3D 0 && qn->slv_port < ns) + qp->bwsum[qn->slv_port] =3D bw_128k; + } + + msm8660_pack_rpm_data(qp->bwsum, ns, qp->arb, arb_size, qp->rpm_buf); + + ret =3D qcom_rpm_write(qp->rpm, QCOM_RPM_ACTIVE_STATE, + desc->rpm_resource, qp->rpm_buf, + desc->rpm_buf_size); + if (ret) + dev_err_ratelimited(provider->dev, + "RPM fabric ARB write failed: %d\n", ret); +} + +static int msm8660_icc_set(struct icc_node *src, struct icc_node *dst) +{ + struct msm8660_icc_node *src_qn; + struct msm8660_icc_provider *qp; + u64 sum_bw, max_peak_bw, rate; + u32 agg_avg =3D 0, agg_peak =3D 0; + struct icc_provider *provider; + struct icc_node *n; + int ret, i; + + src_qn =3D src->data; + provider =3D src->provider; + qp =3D to_msm8660_icc_provider(provider); + + list_for_each_entry(n, &provider->nodes, node_list) + provider->aggregate(n, 0, n->avg_bw, n->peak_bw, + &agg_avg, &agg_peak); + + sum_bw =3D icc_units_to_bps(agg_avg); + max_peak_bw =3D icc_units_to_bps(agg_peak); + + rate =3D max(sum_bw, max_peak_bw); + do_div(rate, src_qn->buswidth); + /* Apply minimum floor to prevent bus starvation */ + rate =3D max_t(u64, rate, MSM8660_FABRIC_MIN_RATE); + rate =3D min_t(u32, rate, INT_MAX); + + if (src_qn->rate !=3D rate) { + for (i =3D 0; i < qp->num_clks; i++) { + ret =3D clk_set_rate(qp->bus_clks[i].clk, rate); + if (ret) { + dev_err(provider->dev, + "%s clk_set_rate error: %d\n", + qp->bus_clks[i].id, ret); + ret =3D 0; + } + } + src_qn->rate =3D rate; + } + + /* Send RPM fabric arbitration if available */ + if (qp->rpm && qp->desc->rpm_resource >=3D 0) + msm8660_rpm_commit(qp); + + return 0; +} + +static int msm8660_get_bw(struct icc_node *node, u32 *avg, u32 *peak) +{ + *avg =3D 0; + *peak =3D 0; + return 0; +} + +static struct qcom_rpm *msm8660_get_rpm(struct device *dev) +{ + struct device_node *rpm_np; + struct platform_device *rpm_pdev; + struct qcom_rpm *rpm; + + rpm_np =3D of_parse_phandle(dev->of_node, "qcom,rpm", 0); + if (!rpm_np) { + dev_dbg(dev, "no qcom,rpm phandle, RPM ARB disabled\n"); + return NULL; + } + + rpm_pdev =3D of_find_device_by_node(rpm_np); + of_node_put(rpm_np); + if (!rpm_pdev) { + dev_warn(dev, "RPM device not found, ARB disabled\n"); + return NULL; + } + + rpm =3D dev_get_drvdata(&rpm_pdev->dev); + put_device(&rpm_pdev->dev); + if (!rpm) { + dev_warn(dev, "RPM not ready, ARB disabled\n"); + return NULL; + } + + return rpm; +} + +static int msm8660_icc_probe(struct platform_device *pdev) +{ + const struct msm8660_icc_desc *desc; + struct msm8660_icc_node * const *qnodes; + struct msm8660_icc_provider *qp; + struct device *dev =3D &pdev->dev; + struct icc_onecell_data *data; + struct icc_provider *provider; + struct icc_node *node; + size_t num_nodes, i; + int ret; + + desc =3D of_device_get_match_data(dev); + if (!desc) + return -EINVAL; + + qnodes =3D desc->nodes; + num_nodes =3D desc->num_nodes; + + qp =3D devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + if (!qp) + return -ENOMEM; + + data =3D devm_kzalloc(dev, struct_size(data, nodes, num_nodes), + GFP_KERNEL); + if (!data) + return -ENOMEM; + data->num_nodes =3D num_nodes; + + qp->bus_clks =3D devm_kmemdup(dev, desc->bus_clks, + desc->num_clks * sizeof(*desc->bus_clks), + GFP_KERNEL); + if (!qp->bus_clks) + return -ENOMEM; + + qp->num_clks =3D desc->num_clks; + + /* + * MSM8660 fabric clocks are managed by RPM firmware and may not be + * available in mainline Linux yet. Make them optional. + */ + ret =3D devm_clk_bulk_get_optional(dev, qp->num_clks, qp->bus_clks); + if (ret) { + dev_warn(dev, "Failed to get bus clocks: %d (continuing anyway)\n", ret); + qp->num_clks =3D 0; + } + + if (qp->num_clks) { + ret =3D clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); + if (ret) { + dev_warn(dev, "Failed to enable bus clocks: %d\n", ret); + qp->num_clks =3D 0; + } + } + + /* Set up RPM fabric arbitration */ + qp->desc =3D desc; + if (desc->rpm_resource >=3D 0) { + qp->rpm =3D msm8660_get_rpm(dev); + if (qp->rpm) { + int arb_size =3D desc->nmasters * desc->ntieredslaves; + + qp->bwsum =3D devm_kcalloc(dev, desc->nslaves, + sizeof(u16), GFP_KERNEL); + qp->arb =3D devm_kcalloc(dev, arb_size + 1, + sizeof(u16), GFP_KERNEL); + qp->rpm_buf =3D devm_kcalloc(dev, desc->rpm_buf_size, + sizeof(u32), GFP_KERNEL); + if (!qp->bwsum || !qp->arb || !qp->rpm_buf) { + dev_warn(dev, "RPM buffer alloc failed, ARB disabled\n"); + qp->rpm =3D NULL; + } else { + int rc; + + dev_info(dev, "RPM fabric ARB enabled (%d masters, %d slaves, %d tiere= d)\n", + desc->nmasters, desc->nslaves, + desc->ntieredslaves); + + /* + * One-shot sleep-context vote of zero bandwidth. + * Without an explicit SLEEP_STATE write, RPM has no + * fabric bandwidth target for deep-sleep and may + * keep the active vote applied indefinitely, + * preventing DDR from dropping its rate when CPUs + * power-collapse. The buffer is devm_kcalloc'd so + * it is all-zero at this point =E2=80=94 written before + * any consumer can drive an active vote that would + * dirty it. + * + * msm8660_rpm_commit() writes ACTIVE_STATE only; + * SLEEP_STATE remains zero for the provider's + * lifetime, so this vote does not need refreshing. + */ + rc =3D qcom_rpm_write(qp->rpm, + QCOM_RPM_SLEEP_STATE, + desc->rpm_resource, + qp->rpm_buf, + desc->rpm_buf_size); + if (rc) + dev_warn(dev, "RPM fabric sleep vote failed: %d\n", + rc); + } + } + } + + provider =3D &qp->provider; + provider->dev =3D dev; + provider->set =3D msm8660_icc_set; + provider->aggregate =3D icc_std_aggregate; + provider->xlate =3D of_icc_xlate_onecell; + provider->data =3D data; + provider->get_bw =3D msm8660_get_bw; + + icc_provider_init(provider); + + for (i =3D 0; i < num_nodes; i++) { + size_t j; + + if (!qnodes[i]) + continue; + + node =3D icc_node_create(qnodes[i]->id); + if (IS_ERR(node)) { + ret =3D PTR_ERR(node); + goto err_remove_nodes; + } + + node->name =3D qnodes[i]->name; + node->data =3D qnodes[i]; + icc_node_add(node, provider); + + dev_dbg(dev, "registered node %s\n", node->name); + + /* populate links */ + for (j =3D 0; j < qnodes[i]->num_links; j++) + icc_link_create(node, qnodes[i]->links[j]); + + data->nodes[i] =3D node; + } + + ret =3D icc_provider_register(provider); + if (ret) + goto err_remove_nodes; + + platform_set_drvdata(pdev, qp); + + dev_info(dev, "MSM8660 interconnect provider registered\n"); + + return 0; + +err_remove_nodes: + icc_nodes_remove(provider); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + + return ret; +} + +static void msm8660_icc_remove(struct platform_device *pdev) +{ + struct msm8660_icc_provider *qp =3D platform_get_drvdata(pdev); + + icc_provider_deregister(&qp->provider); + icc_nodes_remove(&qp->provider); + if (qp->num_clks) + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); +} + +static const struct of_device_id msm8660_noc_of_match[] =3D { + { .compatible =3D "qcom,msm8660-apps-fabric", .data =3D &msm8660_afab }, + { .compatible =3D "qcom,msm8660-system-fabric", .data =3D &msm8660_sfab }, + { .compatible =3D "qcom,msm8660-mmss-fabric", .data =3D &msm8660_mmfab }, + { .compatible =3D "qcom,msm8660-daytona-fabric", .data =3D &msm8660_dfab = }, + { }, +}; +MODULE_DEVICE_TABLE(of, msm8660_noc_of_match); + +static struct platform_driver msm8660_noc_driver =3D { + .probe =3D msm8660_icc_probe, + .remove =3D msm8660_icc_remove, + .driver =3D { + .name =3D "qnoc-msm8660", + .of_match_table =3D msm8660_noc_of_match, + .sync_state =3D icc_sync_state, + }, +}; +/* + * Register the NOC provider at core_initcall, matching the mainline patte= rn + * used by newer Qualcomm SoCs (sm8450, glymur, qdu1000, sc8280xp, sm8750). + * + * Why not module_platform_driver (device_initcall)? drivers/Makefile lis= ts + * drivers/interconnect/ at position 189, *after* every ICC-consumer subdir + * (clk/ @40, soc/ @46, gpu/ @68, base/mfd/ @76, spmi/ @89, usb/ @106, + * i2c/ @116, mmc/ @133, remoteproc/ @158). Within a single initcall level + * execution order =3D link order, so a device_initcall registration here = runs + * *after* every consumer has already tried to probe. Mainline relies on + * deferred-probe retry to recover from that, but in this tree some consum= er + * (apcs-msm8660 + cpufreq cascade suspected) fails to recover within + * deferred_probe_timeout=3D5 and boot dies at the Tux splash with no root= fs. + * Empirically confirmed 2026-05-29 with module_platform_driver (commits + * 99275d8a8ae9 + ca35c591854c, reverted). + * + * icc_provider_register does not require icc_init to have run first -- + * the framework's locks are statically DEFINE_MUTEX'd -- so registering + * the provider at core_initcall (before icc_init at subsys_initcall) is + * safe, same as mainline sm8450 etc. + */ +static int __init msm8660_noc_driver_init(void) +{ + return platform_driver_register(&msm8660_noc_driver); +} +core_initcall(msm8660_noc_driver_init); + +static void __exit msm8660_noc_driver_exit(void) +{ + platform_driver_unregister(&msm8660_noc_driver); +} +module_exit(msm8660_noc_driver_exit); + +MODULE_DESCRIPTION("Qualcomm MSM8x60 interconnect driver"); +MODULE_LICENSE("GPL v2"); --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound6.mail.transip.nl (outbound6.mail.transip.nl [136.144.136.128]) (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 B76BF2F549F; Sat, 30 May 2026 14:09:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.128 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780150169; cv=none; b=jtWG3LZoX5+UGumULFo5gVpQ232dQRiuWZhHLDcwUe/4Fn6cpCOJD8QUQJ3tUy5H0CVHK8vNyFlhYnathuvF7Gc5Y14ciWQIW3mSLBWkRIeLv+1Ds39Wsqx2xGP/HvqUaNa+kfuAFDBHH8WM1ODE4tT34BNHcVS6cEmna9czQow= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780150169; c=relaxed/simple; bh=c5wGkQ+XLK7Nwvye8KpxMXrtbjQpLyZtJAxtbYuRnNU=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VmD5LBCJzGKHxtBzuLlB1H4ex3jMl5NtmMp0apCoyIYnHq2WBSHETKXm1neNjE7ZSxSkCHfVTS1psPIDT+3qtrJRiRj1rjZHOAAG9TsShzefa09MWqk4+SnRfQCLxAMvcJ9L8RtEHtSFjB3JaosK1LMY2wWstE7jSlrydsdspT4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=aGoJOdAc; arc=none smtp.client-ip=136.144.136.128 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="aGoJOdAc" Received: from submission13.mail.transip.nl (unknown [10.103.8.164]) by outbound6.mail.transip.nl (Postfix) with ESMTP id 4gSMLd6JWpzwLJ9P; Sat, 30 May 2026 16:00:25 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission13.mail.transip.nl (Postfix) with ESMTPA id 4gSMLd0Pzdz3fCxJH; Sat, 30 May 2026 16:00:25 +0200 (CEST) From: Herman van Hazendonk To: Bjorn Andersson , Conor Dooley , devicetree@vger.kernel.org, Konrad Dybcio , Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, Rob Herring , Thomas Gleixner , van Hazendonk Subject: [PATCH 2/2] irqchip: add MSM8x60 MPM wakeup interrupt controller driver Date: Sat, 30 May 2026 16:00:23 +0200 Message-ID: <9777c7b553b0858d4174101487330038d770fa44.1780148149.git.github.com@herrie.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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 X-Scanned-By: ClueGetter at submission13.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149625; h=from:subject:to:references: in-reply-to:date:mime-version:content-type; bh=Cs8VUmyHkCCevznwlVU9J+mNTx7TjNIFN5Tn8ZXs+0w=; b=aGoJOdAcuEHLz+P2fvQCa4ST+G7Ir2VNxHWtbcqnQ4ArJ6gq6y8ZvSIt28kLdtasP2bECm REwAHn5c1yda6Kx3ul0gyS9a8LuF753FEiWHbYQTU4U55sJrLQqC99lp6XPGB7XQrxAvLX uHYyGi9Bb/r/18zzQuoi5vF0MDPGH7R1Scm86T89+yDPbu7l7De5dPM+Ia/T+2IoQJ75mC N3qVaLlsqgs8Td1M70iQfK/t49+ug4NAwGl02kwikRUrLCxNLpc9mQLvz+Ruc/keOsuqQx LfUHnJ+W6owhvK+lwA7dCvUiLCgJfc07NMefmGiPadYP9scuC9LnjxD/Uk5s7w== X-Report-Abuse-To: abuse@transip.nl Add a driver for the MSM Power Manager (MPM) on the MSM8x60 family (MSM8260/MSM8660/APQ8060). The MPM is a small wake-source controller implemented in the always-on power domain that latches edge-triggered interrupts during APPS power collapse and signals the RPM to wake the APPS back up. The driver: - implements an irqchip that wraps a parent GIC SPI line and overrides ->irq_set_wake() to enable the MPM mirror of the same interrupt; - exposes msm8660_mpm_set_pin_wake() etc. for consumers (e.g. mmci's SDC4 SDIO wake) that do not flow through irq_set_wake() directly; - communicates with the RPM via SMSM/syscon to apply the wake enable/clear bits and request a power collapse vote. Used on the HP TouchPad (Tenderloin) for SDIO-attached Wi-Fi and panel touch wake-from-suspend. Signed-off-by: Herman van Hazendonk --- drivers/irqchip/Kconfig | 23 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-msm8660-mpm.c | 627 ++++++++++++++++++++++++++++++ include/soc/qcom/msm8660-mpm.h | 80 ++++ 4 files changed, 731 insertions(+) create mode 100644 drivers/irqchip/irq-msm8660-mpm.c create mode 100644 include/soc/qcom/msm8660-mpm.h diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index e755a2a05209..35598a56ac79 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -525,6 +525,29 @@ config QCOM_MPM MSM Power Manager driver to manage and configure wakeup IRQs for Qualcomm Technologies Inc (QTI) mobile chips. =20 +config QCOM_MSM8660_MPM + bool "MSM8x60 MPM wakeup interrupt controller" + depends on ARCH_QCOM + depends on MFD_SYSCON + depends on MAILBOX + select IRQ_DOMAIN_HIERARCHY + default y + help + Platform driver for the MSM Power Manager (MPM) wakeup interrupt + controller on the MSM8x60 family (MSM8260/MSM8660/APQ8060). The vMPM re= gisters live inside + the RPM control block, which makes the generic QCOM_MPM driver + unusable here (it assumes a dedicated MPM SRAM region, an IPCC + mailbox, and uses IRQCHIP_DECLARE early-init that races platform + device creation). + + This driver replicates the legacy 2.6.35-palm arch/arm/mach-msm/ + mpm.c mechanism as a regular platform driver, accessing the vMPM + registers via a syscon phandle to the RPM block and signaling the + MPM via the qcom-apcs-ipc mailbox (writing to GCC + 0x008 bit 1). + + Required for cpuidle deep-sleep states (SPC/PC) and for + WoWLAN-style wake-from-suspend on MSM8x60 SoCs. + config CSKY_MPINTC bool depends on CSKY diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 26aa3b6ec99f..3e64591f0f5b 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_MESON_IRQ_GPIO) +=3D irq-meson-gpio.o obj-$(CONFIG_GOLDFISH_PIC) +=3D irq-goldfish-pic.o obj-$(CONFIG_QCOM_PDC) +=3D qcom-pdc.o obj-$(CONFIG_QCOM_MPM) +=3D irq-qcom-mpm.o +obj-$(CONFIG_QCOM_MSM8660_MPM) +=3D irq-msm8660-mpm.o obj-$(CONFIG_CSKY_MPINTC) +=3D irq-csky-mpintc.o obj-$(CONFIG_CSKY_APB_INTC) +=3D irq-csky-apb-intc.o obj-$(CONFIG_RISCV_INTC) +=3D irq-riscv-intc.o diff --git a/drivers/irqchip/irq-msm8660-mpm.c b/drivers/irqchip/irq-msm866= 0-mpm.c new file mode 100644 index 000000000000..c579d1476e01 --- /dev/null +++ b/drivers/irqchip/irq-msm8660-mpm.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MSM8x60 family (MSM8260/MSM8660/APQ8060) MPM (MSM Power Manager) wakeup= interrupt controller + * + * The MPM is an always-on hardware block that keeps a small set of wake + * sources alive while the application processor is powered down for + * cpuidle Power Collapse or suspend-to-RAM. On MSM8x60 the + * vMPM (virtual MPM) registers live INSIDE the RPM's 4 KB control block + * at: + * + * request (control) regs: RPM_BASE + 0x9d8 (ENABLE, DETECT_CTL, + * POLARITY, CLEAR) + * status (pending) regs: RPM_BASE + 0xdf8 (=3D=3D 0x9d8 + 0x420) + * + * The mainline qcom-mpm driver (drivers/irqchip/irq-qcom-mpm.c) is + * fundamentally incompatible with this layout: + * - it assumes a dedicated MPM SRAM region separate from RPM; + * - it assumes a mailbox controller (IPCC) for wake notification; + * - it uses IRQCHIP_DECLARE which runs before platform devices exist, + * so of_find_device_by_node() returns NULL and the init silently + * hangs. + * + * This driver replicates the 2.6.35-palm `arch/arm/mach-msm/mpm.c` + * mechanism as a regular platform driver: probes after platform + * infrastructure is ready, accesses RPM via a syscon phandle, and uses + * the qcom-apcs-ipc mailbox for wake notification (writing to + * GCC + 0x008 bit 1). + * + * Two consumer interfaces: + * + * 1. Hierarchical irqdomain: for MPM pins that map to GIC SPIs (USB, + * HDMI, ...). Consumers wire their interrupts through this + * controller via interrupts-extended and the kernel manages + * enable / mask / set_type via the IRQ subsystem. This is the + * mainline-recommended path for IRQ-mappable wake sources. + * + * 2. Raw-pin API: for MPM pins that do NOT correspond to a GIC IRQ + * (SDC3_DAT1=3D21, SDC3_DAT3=3D22, SDC4_DAT1=3D23, SDC4_DAT3=3D24). + * These are physical wake-signal lines monitored by MPM + * directly. Consumers (mmci for SDC4 wake) call + * msm8660_mpm_set_pin_wake() etc. to twiddle the MPM enable + * register for these "raw" pins. + * + * Copyright (c) 2026, Herman van Hazendonk + * Copyright (c) 2010-2012, The Linux Foundation (legacy mpm.c reference) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* vMPM register offsets (relative to RPM base + 0x9d8). Each register is + * a window of two 32-bit slots since MPM exposes 64 wake pins. + */ +#define MSM8660_MPM_REG_ENABLE 0x00 +#define MSM8660_MPM_REG_DETECT_CTL 0x08 +#define MSM8660_MPM_REG_POLARITY 0x10 +#define MSM8660_MPM_REG_CLEAR 0x18 + +/* Status registers at +0x420 from vMPM base (=3D=3D RPM + 0xdf8). */ +#define MSM8660_MPM_STATUS_OFFSET 0x420 + +#define MSM8660_MPM_PIN_COUNT 64 +#define MSM8660_MPM_REG_WIDTH 2 + +struct msm8660_mpm_pin { + int pin; + int hwirq; +}; + +struct msm8660_mpm { + struct device *dev; + struct regmap *regmap; + unsigned int regmap_offset; + struct irq_domain *domain; + struct msm8660_mpm_pin *pin_map; + unsigned int pin_map_count; + int parent_irq; + raw_spinlock_t lock; + struct mbox_client mbox_client; + struct mbox_chan *mbox_chan; +}; + +/* Singleton =E2=80=94 there is only one MPM instance per SoC. The pin-API + * lookup helpers (msm8660_mpm_get()) return this. + */ +static struct msm8660_mpm *msm8660_mpm_global; + +static u32 msm8660_mpm_read(struct msm8660_mpm *mpm, unsigned int reg) +{ + u32 val; + regmap_read(mpm->regmap, mpm->regmap_offset + reg, &val); + return val; +} + +static void msm8660_mpm_write(struct msm8660_mpm *mpm, unsigned int reg, + u32 val) +{ + regmap_write(mpm->regmap, mpm->regmap_offset + reg, val); +} + +static int msm8660_mpm_pin_to_hwirq(struct msm8660_mpm *mpm, int pin) +{ + int i; + + for (i =3D 0; i < mpm->pin_map_count; i++) { + if (mpm->pin_map[i].pin =3D=3D pin) + return mpm->pin_map[i].hwirq; + } + return -ENOENT; +} + +/* + * IPC handler: MPM fires this IRQ when one or more enabled wake pins + * have pending activity. Read pending status, for each pin look up the + * corresponding GIC hwirq (if mapped), replay it through the + * irqdomain, then clear the pending bit. + */ +static irqreturn_t msm8660_mpm_irq(int irq, void *data) +{ + struct msm8660_mpm *mpm =3D data; + unsigned long pending[MSM8660_MPM_REG_WIDTH]; + unsigned long enable[MSM8660_MPM_REG_WIDTH]; + int i, j; + + for (i =3D 0; i < MSM8660_MPM_REG_WIDTH; i++) { + pending[i] =3D msm8660_mpm_read(mpm, + MSM8660_MPM_STATUS_OFFSET + i * 4); + enable[i] =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_ENABLE + i * 4); + } + + for (i =3D 0; i < MSM8660_MPM_REG_WIDTH; i++) { + unsigned long bits =3D pending[i] & enable[i]; + + for_each_set_bit(j, &bits, 32) { + int pin =3D i * 32 + j; + int hwirq =3D msm8660_mpm_pin_to_hwirq(mpm, pin); + + if (hwirq >=3D 0) { + dev_dbg(mpm->dev, "wake pin %d -> hwirq %d\n", + pin, hwirq); + generic_handle_domain_irq(mpm->domain, hwirq); + } + } + + if (bits) + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_CLEAR + i * 4, bits); + } + + return IRQ_HANDLED; +} + +static void msm8660_mpm_enable_hwirq(struct irq_data *d, bool enable) +{ + struct msm8660_mpm *mpm =3D irq_data_get_irq_chip_data(d); + unsigned long flags; + int i, pin =3D -1; + u32 val; + + for (i =3D 0; i < mpm->pin_map_count; i++) { + if (mpm->pin_map[i].hwirq =3D=3D d->hwirq) { + pin =3D mpm->pin_map[i].pin; + break; + } + } + + if (pin < 0) + return; + + raw_spin_lock_irqsave(&mpm->lock, flags); + + val =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_ENABLE + (pin / 32) * 4); + if (enable) + val |=3D BIT(pin % 32); + else + val &=3D ~BIT(pin % 32); + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_ENABLE + (pin / 32) * 4, val); + + raw_spin_unlock_irqrestore(&mpm->lock, flags); +} + +static void msm8660_mpm_mask_irq(struct irq_data *d) +{ + msm8660_mpm_enable_hwirq(d, false); + irq_chip_mask_parent(d); +} + +static void msm8660_mpm_unmask_irq(struct irq_data *d) +{ + msm8660_mpm_enable_hwirq(d, true); + irq_chip_unmask_parent(d); +} + +static int msm8660_mpm_set_type(struct irq_data *d, unsigned int type) +{ + struct msm8660_mpm *mpm =3D irq_data_get_irq_chip_data(d); + unsigned long flags; + int i, pin =3D -1; + u32 detect, polarity; + + for (i =3D 0; i < mpm->pin_map_count; i++) { + if (mpm->pin_map[i].hwirq =3D=3D d->hwirq) { + pin =3D mpm->pin_map[i].pin; + break; + } + } + + if (pin < 0) + return -ENOENT; + + raw_spin_lock_irqsave(&mpm->lock, flags); + + detect =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_DETECT_CTL + (pin / 32) * 4); + polarity =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_POLARITY + (pin / 32) * 4); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + detect |=3D BIT(pin % 32); + polarity |=3D BIT(pin % 32); + break; + case IRQ_TYPE_EDGE_FALLING: + detect |=3D BIT(pin % 32); + polarity &=3D ~BIT(pin % 32); + break; + case IRQ_TYPE_LEVEL_HIGH: + detect &=3D ~BIT(pin % 32); + polarity |=3D BIT(pin % 32); + break; + case IRQ_TYPE_LEVEL_LOW: + detect &=3D ~BIT(pin % 32); + polarity &=3D ~BIT(pin % 32); + break; + default: + raw_spin_unlock_irqrestore(&mpm->lock, flags); + return -EINVAL; + } + + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_DETECT_CTL + (pin / 32) * 4, detect); + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_POLARITY + (pin / 32) * 4, polarity); + + raw_spin_unlock_irqrestore(&mpm->lock, flags); + + return irq_chip_set_type_parent(d, type); +} + +static struct irq_chip msm8660_mpm_chip =3D { + .name =3D "msm8660-mpm", + .irq_mask =3D msm8660_mpm_mask_irq, + .irq_unmask =3D msm8660_mpm_unmask_irq, + .irq_set_type =3D msm8660_mpm_set_type, + .irq_eoi =3D irq_chip_eoi_parent, + .irq_set_affinity =3D irq_chip_set_affinity_parent, + .flags =3D IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, +}; + +static int msm8660_mpm_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct msm8660_mpm *mpm =3D domain->host_data; + struct irq_fwspec *fwspec =3D data; + struct irq_fwspec parent_fwspec; + int hwirq, ret; + + if (fwspec->param_count !=3D 2) + return -EINVAL; + + hwirq =3D fwspec->param[0]; + + parent_fwspec.fwnode =3D domain->parent->fwnode; + parent_fwspec.param_count =3D 3; + parent_fwspec.param[0] =3D 0; /* GIC_SPI */ + parent_fwspec.param[1] =3D hwirq; + parent_fwspec.param[2] =3D fwspec->param[1]; + + ret =3D irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); + if (ret) + return ret; + + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, + &msm8660_mpm_chip, mpm); + + return 0; +} + +static const struct irq_domain_ops msm8660_mpm_domain_ops =3D { + .alloc =3D msm8660_mpm_domain_alloc, + .free =3D irq_domain_free_irqs_common, + .translate =3D irq_domain_translate_twocell, +}; + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * Raw-pin API for wake sources without a GIC IRQ mapping (SDC3/4 DATx) + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * These helpers operate on MPM pins directly via the ENABLE / DETECT_CTL + * / POLARITY registers. Used by consumer drivers (mmci for SDC4 wake) + * that need to monitor a physical signal line via MPM but don't have a + * corresponding GIC interrupt to route through the irqdomain. + */ + +/** + * msm8660_mpm_get() - get a handle to the MPM device + * @np: device node of the consumer (used for phandle lookup) + * @propname: phandle property name; if NULL, returns the singleton + * without phandle verification. + * + * Returns the singleton MPM handle, or ERR_PTR(-EPROBE_DEFER) if the + * MPM driver hasn't probed yet. + */ +struct msm8660_mpm *msm8660_mpm_get(struct device_node *np, + const char *propname) +{ + struct device_node *mpm_np; + + if (!msm8660_mpm_global) + return ERR_PTR(-EPROBE_DEFER); + + if (np && propname) { + mpm_np =3D of_parse_phandle(np, propname, 0); + if (!mpm_np) + return ERR_PTR(-ENOENT); + of_node_put(mpm_np); + } + + return msm8660_mpm_global; +} +EXPORT_SYMBOL_GPL(msm8660_mpm_get); + +/** + * msm8660_mpm_enable_pin() - enable/disable MPM monitoring of a pin + * @mpm: handle from msm8660_mpm_get() + * @pin: MPM pin index (0..63) + * @enable: true to monitor, false to ignore + * + * Programs the ENABLE register directly. Intended for "raw" wake pins + * (SDC3_DAT1=3D21, SDC3_DAT3=3D22, SDC4_DAT1=3D23, SDC4_DAT3=3D24) that h= ave no + * GIC IRQ mapping. For pins that DO have a GIC mapping (in + * qcom,mpm-pin-map), use the irqdomain path instead. + */ +int msm8660_mpm_enable_pin(struct msm8660_mpm *mpm, unsigned int pin, + bool enable) +{ + unsigned long flags; + u32 val; + + if (!mpm || pin >=3D MSM8660_MPM_PIN_COUNT) + return -EINVAL; + + raw_spin_lock_irqsave(&mpm->lock, flags); + val =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_ENABLE + (pin / 32) * 4); + if (enable) + val |=3D BIT(pin % 32); + else + val &=3D ~BIT(pin % 32); + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_ENABLE + (pin / 32) * 4, val); + raw_spin_unlock_irqrestore(&mpm->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(msm8660_mpm_enable_pin); + +/** + * msm8660_mpm_set_pin_wake() - mark a pin as wake-capable + * @mpm: handle + * @pin: MPM pin index + * @on: true to allow this pin to wake the system, false to clear. + * + * Equivalent to msm8660_mpm_enable_pin() on MSM8660 =E2=80=94 the hardwar= e has + * a single ENABLE register, not separate enable + wake masks. The name + * is kept for API parity with the legacy mpm.h interface so consumers + * can express the wake-source intent explicitly. + */ +int msm8660_mpm_set_pin_wake(struct msm8660_mpm *mpm, unsigned int pin, + bool on) +{ + return msm8660_mpm_enable_pin(mpm, pin, on); +} +EXPORT_SYMBOL_GPL(msm8660_mpm_set_pin_wake); + +/** + * msm8660_mpm_set_pin_type() - set trigger type for a raw MPM pin + * @mpm: handle + * @pin: MPM pin index + * @flow_type: standard IRQ_TYPE_* constants + * + * On MSM8660 the trigger config is split across DETECT_CTL (edge vs + * level) and POLARITY (rising/high vs falling/low). + */ +int msm8660_mpm_set_pin_type(struct msm8660_mpm *mpm, unsigned int pin, + unsigned int flow_type) +{ + unsigned long flags; + u32 detect, polarity; + bool edge, polarity_high; + + if (!mpm || pin >=3D MSM8660_MPM_PIN_COUNT) + return -EINVAL; + + edge =3D !!(flow_type & IRQ_TYPE_EDGE_BOTH); + polarity_high =3D !!(flow_type & (IRQ_TYPE_EDGE_RISING | + IRQ_TYPE_LEVEL_HIGH)); + + raw_spin_lock_irqsave(&mpm->lock, flags); + + detect =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_DETECT_CTL + (pin / 32) * 4); + polarity =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_POLARITY + (pin / 32) * 4); + + if (edge) + detect |=3D BIT(pin % 32); + else + detect &=3D ~BIT(pin % 32); + + if (polarity_high) + polarity |=3D BIT(pin % 32); + else + polarity &=3D ~BIT(pin % 32); + + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_DETECT_CTL + (pin / 32) * 4, detect); + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_POLARITY + (pin / 32) * 4, polarity); + + raw_spin_unlock_irqrestore(&mpm->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(msm8660_mpm_set_pin_type); + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * Platform driver + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + */ + +static int msm8660_mpm_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct device_node *np =3D dev->of_node; + struct msm8660_mpm *mpm; + struct irq_domain *parent_domain; + struct device_node *parent_np; + int ret, i; + u32 offset; + + if (msm8660_mpm_global) { + dev_err(dev, "only one MPM instance is supported\n"); + return -EBUSY; + } + + mpm =3D devm_kzalloc(dev, sizeof(*mpm), GFP_KERNEL); + if (!mpm) + return -ENOMEM; + + mpm->dev =3D dev; + raw_spin_lock_init(&mpm->lock); + + mpm->regmap =3D syscon_regmap_lookup_by_phandle(np, "qcom,rpm-syscon"); + if (IS_ERR(mpm->regmap)) { + dev_err(dev, "failed to get RPM syscon: %ld\n", + PTR_ERR(mpm->regmap)); + return PTR_ERR(mpm->regmap); + } + + ret =3D of_property_read_u32(np, "qcom,mpm-offset", &offset); + if (ret) { + dev_err(dev, "failed to read qcom,mpm-offset: %d\n", ret); + return ret; + } + mpm->regmap_offset =3D offset; + + dev_info(dev, "vMPM registers at RPM offset 0x%x\n", offset); + + /* Parse pin map (IRQ-mapped wake pins; raw pins like SDC4_DAT1 are + * not listed here =E2=80=94 they're accessed via the pin-API below). + */ + ret =3D of_property_count_u32_elems(np, "qcom,mpm-pin-map"); + if (ret < 0 || ret % 2) { + dev_err(dev, "invalid qcom,mpm-pin-map\n"); + return -EINVAL; + } + + mpm->pin_map_count =3D ret / 2; + mpm->pin_map =3D devm_kcalloc(dev, mpm->pin_map_count, + sizeof(*mpm->pin_map), GFP_KERNEL); + if (!mpm->pin_map) + return -ENOMEM; + + for (i =3D 0; i < mpm->pin_map_count; i++) { + u32 pin, hwirq; + + of_property_read_u32_index(np, "qcom,mpm-pin-map", + i * 2, &pin); + of_property_read_u32_index(np, "qcom,mpm-pin-map", + i * 2 + 1, &hwirq); + + mpm->pin_map[i].pin =3D pin; + mpm->pin_map[i].hwirq =3D hwirq; + + dev_dbg(dev, "pin map: pin %d -> hwirq %d\n", pin, hwirq); + } + + parent_np =3D of_irq_find_parent(np); + if (!parent_np) { + dev_err(dev, "failed to find parent interrupt controller\n"); + return -ENODEV; + } + + parent_domain =3D irq_find_host(parent_np); + of_node_put(parent_np); + if (!parent_domain) { + dev_err(dev, "failed to find parent IRQ domain\n"); + return -ENODEV; + } + + mpm->domain =3D irq_domain_create_hierarchy(parent_domain, 0, + MSM8660_MPM_PIN_COUNT, + of_fwnode_handle(np), + &msm8660_mpm_domain_ops, + mpm); + if (!mpm->domain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + mpm->parent_irq =3D platform_get_irq(pdev, 0); + if (mpm->parent_irq < 0) { + ret =3D mpm->parent_irq; + goto err_remove_domain; + } + + ret =3D devm_request_irq(dev, mpm->parent_irq, msm8660_mpm_irq, + IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, + "msm8660-mpm", mpm); + if (ret) { + dev_err(dev, "failed to request IRQ %d: %d\n", + mpm->parent_irq, ret); + goto err_remove_domain; + } + + /* Mailbox channel for poking MPM to re-read its config (writes to + * GCC + 0x008 bit 1 via the qcom-apcs-ipc mailbox). + */ + mpm->mbox_client.dev =3D dev; + mpm->mbox_client.knows_txdone =3D true; + mpm->mbox_chan =3D mbox_request_channel(&mpm->mbox_client, 0); + if (IS_ERR(mpm->mbox_chan)) { + dev_warn(dev, "no mailbox channel: %ld (continuing)\n", + PTR_ERR(mpm->mbox_chan)); + mpm->mbox_chan =3D NULL; + } + + platform_set_drvdata(pdev, mpm); + msm8660_mpm_global =3D mpm; + + dev_info(dev, "ready: %d pin mappings, irq=3D%d\n", + mpm->pin_map_count, mpm->parent_irq); + + return 0; + +err_remove_domain: + irq_domain_remove(mpm->domain); + return ret; +} + +static void msm8660_mpm_remove(struct platform_device *pdev) +{ + struct msm8660_mpm *mpm =3D platform_get_drvdata(pdev); + + msm8660_mpm_global =3D NULL; + if (mpm->mbox_chan) + mbox_free_channel(mpm->mbox_chan); + irq_domain_remove(mpm->domain); +} + +static const struct of_device_id msm8660_mpm_of_match[] =3D { + { .compatible =3D "qcom,msm8660-mpm" }, + {} +}; +MODULE_DEVICE_TABLE(of, msm8660_mpm_of_match); + +static struct platform_driver msm8660_mpm_driver =3D { + .probe =3D msm8660_mpm_probe, + .remove =3D msm8660_mpm_remove, + .driver =3D { + .name =3D "msm8660-mpm", + .of_match_table =3D msm8660_mpm_of_match, + }, +}; + +static int __init msm8660_mpm_init(void) +{ + return platform_driver_register(&msm8660_mpm_driver); +} +subsys_initcall(msm8660_mpm_init); + +static void __exit msm8660_mpm_exit(void) +{ + platform_driver_unregister(&msm8660_mpm_driver); +} +module_exit(msm8660_mpm_exit); + +MODULE_DESCRIPTION("Qualcomm MSM8x60 MPM wakeup interrupt controller"); +MODULE_LICENSE("GPL"); diff --git a/include/soc/qcom/msm8660-mpm.h b/include/soc/qcom/msm8660-mpm.h new file mode 100644 index 000000000000..118748ad6960 --- /dev/null +++ b/include/soc/qcom/msm8660-mpm.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Qualcomm MSM8x60 family (MSM8260/MSM8660/APQ8060) MPM wake-source consu= mer interface. + * + * The MPM driver lives at drivers/irqchip/irq-msm8660-mpm.c. It exposes + * TWO interfaces: + * + * 1. Hierarchical irqdomain (preferred). For wake sources that map to + * GIC SPIs (USB1_HS, HDMI, ...). Consumers wire their IRQ through + * the MPM via `interrupts-extended =3D <&msm8660_mpm ...>` in DT and + * the IRQ subsystem manages enable / mask / set_type via the + * irqdomain alloc path. No explicit C API call needed. + * + * 2. Raw-pin API (this header). For wake sources that do NOT have a + * GIC IRQ mapping: SDC3_DAT1=3D21, SDC3_DAT3=3D22, SDC4_DAT1=3D23, + * SDC4_DAT3=3D24. These are physical wake-signal lines that MPM + * monitors directly. mmci (for SDC4 WiFi wake) obtains a handle + * via msm8660_mpm_get() and uses the helpers below. + */ + +#ifndef __SOC_QCOM_MSM8660_MPM_H__ +#define __SOC_QCOM_MSM8660_MPM_H__ + +#include +#include +#include + +#define MSM8660_MPM_NR_PINS 64 + +/* Well-known wake-source pin indices (from the legacy 2.6.35-palm tree). = */ +#define MSM8660_MPM_PIN_SDC3_DAT1 21 +#define MSM8660_MPM_PIN_SDC3_DAT3 22 +#define MSM8660_MPM_PIN_SDC4_DAT1 23 +#define MSM8660_MPM_PIN_SDC4_DAT3 24 + +struct device_node; +struct msm8660_mpm; + +#if IS_ENABLED(CONFIG_QCOM_MSM8660_MPM) + +struct msm8660_mpm *msm8660_mpm_get(struct device_node *np, + const char *propname); + +int msm8660_mpm_set_pin_wake(struct msm8660_mpm *mpm, unsigned int pin, + bool on); +int msm8660_mpm_enable_pin(struct msm8660_mpm *mpm, unsigned int pin, + bool enable); +int msm8660_mpm_set_pin_type(struct msm8660_mpm *mpm, unsigned int pin, + unsigned int flow_type); + +#else /* !CONFIG_QCOM_MSM8660_MPM */ + +static inline struct msm8660_mpm * +msm8660_mpm_get(struct device_node *np, const char *propname) +{ + return ERR_PTR(-ENODEV); +} + +static inline int msm8660_mpm_set_pin_wake(struct msm8660_mpm *mpm, + unsigned int pin, bool on) +{ + return -ENODEV; +} + +static inline int msm8660_mpm_enable_pin(struct msm8660_mpm *mpm, + unsigned int pin, bool enable) +{ + return -ENODEV; +} + +static inline int msm8660_mpm_set_pin_type(struct msm8660_mpm *mpm, + unsigned int pin, + unsigned int flow_type) +{ + return -ENODEV; +} + +#endif /* CONFIG_QCOM_MSM8660_MPM */ + +#endif /* __SOC_QCOM_MSM8660_MPM_H__ */ --=20 2.43.0 From nobody Mon Jun 8 09:48:06 2026 Received: from outbound0.mail.transip.nl (outbound0.mail.transip.nl [149.210.149.69]) (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 9B3B717A2F6; Sat, 30 May 2026 14:04:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=149.210.149.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149855; cv=none; b=OSKEwhLwRsxN/Iz8YCizKSkfOw6XFhC/Q2VGeUJyGWFxK853AJbkUY39KDnQTR84kGt1Ffp25Me/v329ZzxIAe+qfWGrg/4iaA5Bj0kzglTOauT7ifue01WFaxZJ5kZcSfaDPFMgC8nIL3FqpVP1JdwjrdX5+4lOeCuuN8feP4E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780149855; c=relaxed/simple; bh=V6Icuw8hl37yq7qHOTuSryUWAe9gXsn6pSsVegpU/E0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=evKwfUOg34blg4t7/2AFJ+iDKH1x8z6d4tXvd1RhBZEhc2HuJ2i2tHZLrPjLpIeK5itxArVodBMNqc6/7Lc2ZCdFPfNgsSeuPlw8XT37UVdktzufUw+dLVCq1Djv1YsOCcJ2JDpw15Omavf9J31JBQ/CRgJ9hHWgeLz4ObUOiJA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=p9rgpxMK; arc=none smtp.client-ip=149.210.149.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="p9rgpxMK" Received: from submission5.mail.transip.nl (unknown [10.103.8.156]) by outbound0.mail.transip.nl (Postfix) with ESMTP id 4gSMJQ5fgQzxNnS; Sat, 30 May 2026 15:58:30 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission5.mail.transip.nl (Postfix) with ESMTPA id 4gSMJP4qmdz2pRDjH; Sat, 30 May 2026 15:58:29 +0200 (CEST) From: Herman van Hazendonk To: Bjorn Andersson , Michael Turquette , Stephen Boyd , linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-clk@vger.kernel.org Cc: Herman van Hazendonk Subject: [PATCH 3/3] clk: qcom: add MSM8x60 MMCC driver Date: Sat, 30 May 2026 15:58:12 +0200 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: 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 X-Scanned-By: ClueGetter at submission5.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780149509; h=from:subject:to:cc: references:in-reply-to:date:mime-version:content-type; bh=ZV2aehyzz5aTXbu0GiUcKaMME2LeyRPhHYoD8zM0VKE=; b=p9rgpxMKM/K6iMqCz17Zp1hZlntD/z/vtGvgMFr/1DJc/VZNDgyhsKP39RDfZ9ZOzi6rUa bYTL++M2ruAMZOEMUd6htRuMHJZ8H7vACV3wchx9p19syj8VHulfOszOuZiard2cTHOl80 W3HXOdpooH62Qv6jNOzMEubGLqtkwLo4vF3MV0LWcpnfpuhPhKTdG9vXi6+p8v7C2cNtyM ze79vGBoD9FCYsvgpIMXLfK3pWZ5AkYwDHNSB+MhqfK5wXc5LFvha6FxWen5pBFo8bwhve CS3jAABekGwyFrpvun+JF3nFvQSFxAvtoQ30smUyKSzfiMQilYqJDE0ZZ969DA== X-Report-Abuse-To: abuse@transip.nl Add a clock driver for the Multimedia Clock Controller (MMCC) on the MSM8x60 family (MSM8260/MSM8660/APQ8060) - the Scorpion-class generation that preceded MSM8960's Krait CPUs. The MMCC layout on MSM8x60 differs from MSM8960 in several ways that make a separate driver cleaner than parameterising mmcc-msm8960.c: - the pix_rdi mux requires a custom set_parent op that temporarily enables both parents during the glitch-free transition; - the IJPEG GDSC requires releasing AXI, AHB and CORE resets; - several rate-source pairs (MDP pixel, GFX2D/3D) only exist on 8x60 (e.g. PLL2-derived 228571000/266667000 for graphics); - the camera CSI / VFE / JPEG / VPE / ROT clock topology lacks the later 8960 reorganisation. Used on the HP TouchPad (Tenderloin) for graphics (Adreno A220), display (MDP4), camera (CSI/VFE), JPEG (Gemini), VIDC, VPE and rotator. Reset IDs are exposed via a separate header so consumers can reset the GDSCs and individual blocks. Signed-off-by: Herman van Hazendonk --- drivers/clk/qcom/Kconfig | 11 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/mmcc-msm8660.c | 2998 +++++++++++++++++++++++++++++++ 3 files changed, 3010 insertions(+) create mode 100644 drivers/clk/qcom/mmcc-msm8660.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index d9cff5b0281d..f64b0002c85a 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -572,6 +572,17 @@ config MSM_MMCC_8960 Say Y if you want to support multimedia devices such as display, graphics, video encode/decode, camera, etc. =20 +config MSM_MMCC_8660 + tristate "MSM8x60 Multimedia Clock Controller" + depends on ARM || COMPILE_TEST + select MSM_GCC_8660 + select QCOM_GDSC + help + Support for the multimedia clock controller on the MSM8x60 family + (MSM8260/MSM8660/APQ8060). Say Y if you want to support multimedia + devices such as display, graphics, video encode/decode, camera, etc. + on these Scorpion-class SoCs (e.g. HP TouchPad). + config MSM_GCC_8953 tristate "MSM8953 Global Clock Controller" select QCOM_GDSC diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index e100cfd6a52d..ff317fce086d 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_MSM_LCC_8960) +=3D lcc-msm8960.o obj-$(CONFIG_MSM_GCC_8998) +=3D gcc-msm8998.o obj-$(CONFIG_MSM_GPUCC_8998) +=3D gpucc-msm8998.o obj-$(CONFIG_MSM_MMCC_8960) +=3D mmcc-msm8960.o +obj-$(CONFIG_MSM_MMCC_8660) +=3D mmcc-msm8660.o obj-$(CONFIG_MSM_MMCC_8974) +=3D mmcc-msm8974.o obj-$(CONFIG_MSM_MMCC_8994) +=3D mmcc-msm8994.o obj-$(CONFIG_MSM_MMCC_8996) +=3D mmcc-msm8996.o diff --git a/drivers/clk/qcom/mmcc-msm8660.c b/drivers/clk/qcom/mmcc-msm866= 0.c new file mode 100644 index 000000000000..5e6943430d29 --- /dev/null +++ b/drivers/clk/qcom/mmcc-msm8660.c @@ -0,0 +1,2998 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + Herman van Hazendonk + * + * MSM8x60 family (MSM8260/MSM8660/APQ8060) Multimedia Clock Controller dr= iver + * + * Split from mmcc-msm8960.c to properly handle MSM8x60-specific clock + * configurations, particularly the GFX3D reset bits which differ from MSM= 8960. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" +#include "gdsc.h" + +enum { + P_PXO, + P_PLL8, + P_PLL2, + P_PLL3, + P_HDMI_PLL, + P_DSI1_PLL_DSICLK, + P_DSI2_PLL_DSICLK, + P_DSI1_PLL_BYTECLK, + P_DSI2_PLL_BYTECLK, +}; + +#define F_MN(f, s, _m, _n) { .freq =3D f, .src =3D s, .pre_div =3D 1, .m = =3D _m, .n =3D _n } +/* Pure divider+source RCG (no MND), e.g. VPE on MSM8660: NS_DIVSRC(15,12,= d,2,0,s) */ +#define F_DIV(f, s, d) { .freq =3D f, .src =3D s, .pre_div =3D d } + +static struct clk_pll pll2 =3D { + .l_reg =3D 0x320, + .m_reg =3D 0x324, + .n_reg =3D 0x328, + .config_reg =3D 0x32c, + .mode_reg =3D 0x31c, + .status_reg =3D 0x334, + .status_bit =3D 16, + .clkr.hw.init =3D &(struct clk_init_data){ + .name =3D "pll2", + .parent_data =3D (const struct clk_parent_data[]){ + { .fw_name =3D "pxo", .name =3D "pxo_board" }, + }, + .num_parents =3D 1, + .ops =3D &clk_pll_ops, + }, +}; + +static const struct parent_map mmcc_pxo_pll8_pll2_map[] =3D { + { P_PXO, 0 }, + { P_PLL8, 2 }, + { P_PLL2, 1 } +}; + +static const struct clk_parent_data mmcc_pxo_pll8_pll2[] =3D { + { .fw_name =3D "pxo", .name =3D "pxo_board" }, + { .fw_name =3D "pll8_vote", .name =3D "pll8_vote" }, + { .hw =3D &pll2.clkr.hw }, +}; + +static const struct parent_map mmcc_pxo_pll8_pll2_pll3_map[] =3D { + { P_PXO, 0 }, + { P_PLL8, 2 }, + { P_PLL2, 1 }, + { P_PLL3, 3 } +}; + +static const struct clk_parent_data mmcc_pxo_pll8_pll2_pll3[] =3D { + { .fw_name =3D "pxo", .name =3D "pxo_board" }, + { .fw_name =3D "pll8_vote", .name =3D "pll8_vote" }, + { .hw =3D &pll2.clkr.hw }, + { .fw_name =3D "pll3", .name =3D "pll3" }, +}; + +static const struct parent_map mmcc_pxo_dsi2_dsi1_map[] =3D { + { P_PXO, 0 }, + { P_DSI2_PLL_DSICLK, 1 }, + { P_DSI1_PLL_DSICLK, 3 }, +}; + +static const struct clk_parent_data mmcc_pxo_dsi2_dsi1[] =3D { + { .fw_name =3D "pxo", .name =3D "pxo_board" }, + { .fw_name =3D "dsi2pll", .name =3D "dsi2pll" }, + { .fw_name =3D "dsi1pll", .name =3D "dsi1pll" }, +}; + +static const struct parent_map mmcc_pxo_dsi1_dsi2_byte_map[] =3D { + { P_PXO, 0 }, + { P_DSI1_PLL_BYTECLK, 1 }, + { P_DSI2_PLL_BYTECLK, 2 }, +}; + +static const struct clk_parent_data mmcc_pxo_dsi1_dsi2_byte[] =3D { + { .fw_name =3D "pxo", .name =3D "pxo_board" }, + { .fw_name =3D "dsi1pllbyte", .name =3D "dsi1pllbyte" }, + { .fw_name =3D "dsi2pllbyte", .name =3D "dsi2pllbyte" }, +}; + +static const struct freq_tbl clk_tbl_cam[] =3D { + { 6000000, P_PLL8, 4, 1, 16 }, + { 8000000, P_PLL8, 4, 1, 12 }, + { 12000000, P_PLL8, 4, 1, 8 }, + { 16000000, P_PLL8, 4, 1, 6 }, + { 19200000, P_PLL8, 4, 1, 5 }, + { 24000000, P_PLL8, 4, 1, 4 }, + { 32000000, P_PLL8, 4, 1, 3 }, + { 48000000, P_PLL8, 4, 1, 2 }, + { 64000000, P_PLL8, 3, 1, 2 }, + { 96000000, P_PLL8, 4, 0, 0 }, + { 128000000, P_PLL8, 3, 0, 0 }, + { } +}; + +static struct clk_rcg camclk0_src =3D { + .ns_reg =3D 0x0148, + .md_reg =3D 0x0144, + .mn =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 8, + .reset_in_cc =3D true, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 24, + .m_val_shift =3D 8, + .width =3D 8, + }, + .p =3D { + .pre_div_shift =3D 14, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_cam, + .clkr =3D { + .enable_reg =3D 0x0140, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "camclk0_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch camclk0_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 15, + /* + * The legacy webOS kernel used halt_reg =3D NULL for this clock, + * meaning it never checked the halt status. The hardware doesn't + * properly report the clock state via the halt register. Use + * BRANCH_HALT_SKIP to avoid the "status stuck at 'off'" warning. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0140, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "camclk0_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &camclk0_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_rcg camclk1_src =3D { + .ns_reg =3D 0x015c, + .md_reg =3D 0x0158, + .mn =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 8, + .reset_in_cc =3D true, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 24, + .m_val_shift =3D 8, + .width =3D 8, + }, + .p =3D { + .pre_div_shift =3D 14, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_cam, + .clkr =3D { + .enable_reg =3D 0x0154, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "camclk1_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch camclk1_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 16, + /* Same issue as camclk0_clk - hardware doesn't report halt status */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0154, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "camclk1_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &camclk1_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + }, + }, +}; + +/* + * CSI clock frequency table for MSM8660. + * Uses simple pre-divider from PLL8 (384 MHz), NOT MND divider. + * Reference: webOS clock-8x60.c clk_tbl_csi[] + */ +static const struct freq_tbl clk_tbl_csi[] =3D { + { 192000000, P_PLL8, 2, 0, 0 }, + { 384000000, P_PLL8, 1, 0, 0 }, + { } +}; + +/* + * CSI clock for MSM8660 uses simple pre-divider, NOT MND divider. + * CC_REG =3D 0x0040, NS_REG =3D 0x0048, no MD register. + * Pre-divider is in NS_REG bits [15:12], source select in bits [2:0]. + * Reference: webOS clock-8x60.c CLK_CSI macro + */ +static struct clk_rcg csi0_src =3D { + .ns_reg =3D 0x0048, + /* No md_reg - CSI uses pre-divider only, not MND */ + .p =3D { + .pre_div_shift =3D 12, + .pre_div_width =3D 4, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_csi, + .clkr =3D { + .enable_reg =3D 0x0040, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "csi0_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch csi0_clk =3D { + .halt_reg =3D 0x01cc, + .halt_bit =3D 13, + /* + * The CSI clock halt status is unreliable when the CSI block is not + * actively receiving data. Use BRANCH_HALT_SKIP to avoid timeouts. + * This matches the behavior of camclk and vfe_ahb_clk on MSM8660. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0040, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &csi0_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "csi0_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch csi0_phy_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 9, + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0040, + .enable_mask =3D BIT(8), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &csi0_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "csi0_phy_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +/* + * CSI1 on MSM8660 shares the same source clock as CSI0. + * In webOS, both CSI0 and CSI1 are branches from a single CSI_SRC. + * Use same registers as csi0_src (CC=3D0x0040, NS=3D0x0048). + * The enable bit for csi1_src root is also BIT(2) in CC_REG. + */ +static struct clk_rcg csi1_src =3D { + .ns_reg =3D 0x0048, + /* No md_reg - CSI uses pre-divider only, not MND */ + .p =3D { + .pre_div_shift =3D 12, + .pre_div_width =3D 4, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_csi, + .clkr =3D { + .enable_reg =3D 0x0040, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "csi1_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch csi1_clk =3D { + .halt_reg =3D 0x01cc, + .halt_bit =3D 14, + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + /* CSI1 enable is in CSI_CC_REG (0x0040) BIT(7) per webOS */ + .enable_reg =3D 0x0040, + .enable_mask =3D BIT(7), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &csi1_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "csi1_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch csi1_phy_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 10, + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + /* Use CSI_CC_REG (0x0040) like csi0_phy_clk, with BIT(9) for CSI1 */ + .enable_reg =3D 0x0040, + .enable_mask =3D BIT(9), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &csi1_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "csi1_phy_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +/* + * CSI PIX/RDI clock mux support + * + * On MSM8660, the csi_pix_clk and csi_rdi_clk can source from either + * CSI0 or CSI1. The selection is controlled by bits in CSC_CC_REG (0x0058= ): + * - BIT(25): csi_pix_clk source (0=3DCSI0, 1=3DCSI1) + * - BIT(12): csi_rdi_clk source (0=3DCSI0, 1=3DCSI1) + * + * This mux is critical for HP TouchPad which uses CSI1 for the front came= ra. + */ +struct clk_pix_rdi { + u32 s_reg; + u32 s_mask; + struct clk_regmap clkr; +}; + +#define to_clk_pix_rdi(_hw) \ + container_of(to_clk_regmap(_hw), struct clk_pix_rdi, clkr) + +static int pix_rdi_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_pix_rdi *rdi =3D to_clk_pix_rdi(hw); + u32 val; + int i, ret =3D 0; + int num_parents =3D clk_hw_get_num_parents(hw); + + /* + * Turn on all parent sources during mux switch to ensure + * glitch-free transition. + */ + for (i =3D 0; i < num_parents; i++) { + struct clk_hw *p =3D clk_hw_get_parent_by_index(hw, i); + + ret =3D clk_prepare_enable(p->clk); + if (ret) + goto err; + } + + /* Select parent: 0=3DCSI0, 1=3DCSI1 */ + val =3D (index =3D=3D 1) ? rdi->s_mask : 0; + regmap_update_bits(rdi->clkr.regmap, rdi->s_reg, rdi->s_mask, val); + + /* + * Wait at least 6 cycles of slowest clock for the + * glitch-free MUX to fully switch sources. + */ + udelay(1); + + /* + * Now disable all parents that were temporarily enabled. + * The clock framework will keep the selected parent enabled + * as long as the child (csi_pix_clk/csi_rdi_clk) is enabled. + */ + for (i =3D num_parents - 1; i >=3D 0; i--) { + struct clk_hw *p =3D clk_hw_get_parent_by_index(hw, i); + + clk_disable_unprepare(p->clk); + } + + return 0; + +err: + /* On error, disable only the parents we successfully enabled */ + for (i--; i >=3D 0; i--) { + struct clk_hw *p =3D clk_hw_get_parent_by_index(hw, i); + + clk_disable_unprepare(p->clk); + } + + return ret; +} + +static u8 pix_rdi_get_parent(struct clk_hw *hw) +{ + struct clk_pix_rdi *rdi =3D to_clk_pix_rdi(hw); + u32 val; + + regmap_read(rdi->clkr.regmap, rdi->s_reg, &val); + return (val & rdi->s_mask) ? 1 : 0; +} + +static const struct clk_ops clk_ops_pix_rdi =3D { + .enable =3D clk_enable_regmap, + .disable =3D clk_disable_regmap, + .is_enabled =3D clk_is_enabled_regmap, + .set_parent =3D pix_rdi_set_parent, + .get_parent =3D pix_rdi_get_parent, + .determine_rate =3D __clk_mux_determine_rate, +}; + +static const struct clk_hw *pix_rdi_parents[] =3D { + &csi0_clk.clkr.hw, + &csi1_clk.clkr.hw, +}; + +static struct clk_pix_rdi csi_pix_clk =3D { + .s_reg =3D 0x0058, + .s_mask =3D BIT(25), + .clkr =3D { + .enable_reg =3D 0x0058, + .enable_mask =3D BIT(26), + .hw.init =3D &(struct clk_init_data){ + .name =3D "csi_pix_clk", + .parent_hws =3D pix_rdi_parents, + .num_parents =3D ARRAY_SIZE(pix_rdi_parents), + .ops =3D &clk_ops_pix_rdi, + }, + }, +}; + +static struct clk_pix_rdi csi_rdi_clk =3D { + .s_reg =3D 0x0058, + .s_mask =3D BIT(12), + .clkr =3D { + .enable_reg =3D 0x0058, + .enable_mask =3D BIT(13), + .hw.init =3D &(struct clk_init_data){ + .name =3D "csi_rdi_clk", + .parent_hws =3D pix_rdi_parents, + .num_parents =3D ARRAY_SIZE(pix_rdi_parents), + .ops =3D &clk_ops_pix_rdi, + }, + }, +}; + +static const struct freq_tbl clk_tbl_csiphytimer[] =3D { + { 85330000, P_PLL8, 1, 2, 9 }, + { 177780000, P_PLL2, 1, 2, 9 }, + { } +}; + +static struct clk_rcg csiphytimer_src =3D { + .ns_reg =3D 0x0168, + .md_reg =3D 0x0164, + .mn =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 8, + .reset_in_cc =3D true, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 24, + .m_val_shift =3D 8, + .width =3D 8, + }, + .p =3D { + .pre_div_shift =3D 14, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_csiphytimer, + .clkr =3D { + .enable_reg =3D 0x0160, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "csiphytimer_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch csiphy0_timer_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 17, + .clkr =3D { + .enable_reg =3D 0x0160, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &csiphytimer_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "csiphy0_timer_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch csiphy1_timer_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 18, + .clkr =3D { + .enable_reg =3D 0x0160, + .enable_mask =3D BIT(9), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &csiphytimer_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "csiphy1_timer_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_dsi[] =3D { + { } +}; + +static struct clk_rcg dsi1_src =3D { + .ns_reg =3D 0x0054, + .md_reg =3D 0x0050, + .mn =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 7, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 24, + .m_val_shift =3D 8, + .width =3D 8, + }, + .p =3D { + .pre_div_shift =3D 14, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_dsi2_dsi1_map, + }, + .freq_tbl =3D clk_tbl_dsi, + .clkr =3D { + .enable_reg =3D 0x004c, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi1_src", + .parent_data =3D mmcc_pxo_dsi2_dsi1, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_dsi2_dsi1), + .ops =3D &clk_rcg_bypass_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch dsi1_clk =3D { + .halt_reg =3D 0x01d0, + .halt_bit =3D 2, + .clkr =3D { + .enable_reg =3D 0x004c, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi1_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &dsi1_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi1_byte_src =3D { + .ns_reg =3D 0x00b0, + .p =3D { + .pre_div_shift =3D 12, + .pre_div_width =3D 4, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_dsi1_dsi2_byte_map, + }, + .clkr =3D { + .enable_reg =3D 0x0090, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi1_byte_src", + .parent_data =3D mmcc_pxo_dsi1_dsi2_byte, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_dsi1_dsi2_byte), + .ops =3D &clk_rcg_bypass_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch dsi1_byte_clk =3D { + .halt_reg =3D 0x01cc, + .halt_bit =3D 21, + .clkr =3D { + .enable_reg =3D 0x0090, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi1_byte_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &dsi1_byte_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi1_esc_src =3D { + .ns_reg =3D 0x00b8, + .p =3D { + .pre_div_shift =3D 12, + .pre_div_width =3D 4, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_dsi1_dsi2_byte_map, + }, + .clkr =3D { + .enable_reg =3D 0x00b4, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi1_esc_src", + .parent_data =3D mmcc_pxo_dsi1_dsi2_byte, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_dsi1_dsi2_byte), + .ops =3D &clk_rcg_bypass_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch dsi1_esc_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 1, + .clkr =3D { + .enable_reg =3D 0x00b4, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi1_esc_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &dsi1_esc_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi1_pixel_src =3D { + .ns_reg =3D 0x0138, + .md_reg =3D 0x0134, + .mn =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 7, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 16, + .m_val_shift =3D 8, + .width =3D 8, + }, + .p =3D { + .pre_div_shift =3D 12, + .pre_div_width =3D 4, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_dsi2_dsi1_map, + }, + .clkr =3D { + .enable_reg =3D 0x0130, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi1_pixel_src", + .parent_data =3D mmcc_pxo_dsi2_dsi1, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_dsi2_dsi1), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch dsi1_pixel_clk =3D { + .halt_reg =3D 0x01d0, + .halt_bit =3D 6, + .clkr =3D { + .enable_reg =3D 0x0130, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi1_pixel_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &dsi1_pixel_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_gfx2d[] =3D { + F_MN( 27000000, P_PXO, 1, 0), + F_MN( 48000000, P_PLL8, 1, 8), + F_MN( 54857000, P_PLL8, 1, 7), + F_MN( 64000000, P_PLL8, 1, 6), + F_MN( 76800000, P_PLL8, 1, 5), + F_MN( 96000000, P_PLL8, 1, 4), + F_MN(128000000, P_PLL8, 1, 3), + F_MN(145455000, P_PLL2, 2, 11), + F_MN(160000000, P_PLL2, 1, 5), + F_MN(177778000, P_PLL2, 2, 9), + F_MN(200000000, P_PLL2, 1, 4), + F_MN(228571000, P_PLL2, 2, 7), + { } +}; + +static struct clk_dyn_rcg gfx2d0_src =3D { + .ns_reg[0] =3D 0x0070, + .ns_reg[1] =3D 0x0070, + .md_reg[0] =3D 0x0064, + .md_reg[1] =3D 0x0068, + .bank_reg =3D 0x0060, + .mn[0] =3D { + .mnctr_en_bit =3D 8, + .mnctr_reset_bit =3D 25, + .mnctr_mode_shift =3D 9, + .n_val_shift =3D 20, + .m_val_shift =3D 4, + .width =3D 4, + }, + .mn[1] =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 24, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 16, + .m_val_shift =3D 4, + .width =3D 4, + }, + .s[0] =3D { + .src_sel_shift =3D 3, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .s[1] =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .mux_sel_bit =3D 11, + .freq_tbl =3D clk_tbl_gfx2d, + .clkr =3D { + .enable_reg =3D 0x0060, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx2d0_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_dyn_rcg_ops, + }, + }, +}; + +static struct clk_branch gfx2d0_clk =3D { + .halt_reg =3D 0x01c8, + .halt_bit =3D 9, + .clkr =3D { + .enable_reg =3D 0x0060, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx2d0_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &gfx2d0_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_dyn_rcg gfx2d1_src =3D { + .ns_reg[0] =3D 0x007c, + .ns_reg[1] =3D 0x007c, + .md_reg[0] =3D 0x0078, + .md_reg[1] =3D 0x006c, + .bank_reg =3D 0x0074, + .mn[0] =3D { + .mnctr_en_bit =3D 8, + .mnctr_reset_bit =3D 25, + .mnctr_mode_shift =3D 9, + .n_val_shift =3D 20, + .m_val_shift =3D 4, + .width =3D 4, + }, + .mn[1] =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 24, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 16, + .m_val_shift =3D 4, + .width =3D 4, + }, + .s[0] =3D { + .src_sel_shift =3D 3, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .s[1] =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .mux_sel_bit =3D 11, + .freq_tbl =3D clk_tbl_gfx2d, + .clkr =3D { + .enable_reg =3D 0x0074, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx2d1_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_dyn_rcg_ops, + }, + }, +}; + +static struct clk_branch gfx2d1_clk =3D { + .halt_reg =3D 0x01c8, + .halt_bit =3D 14, + .clkr =3D { + .enable_reg =3D 0x0074, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx2d1_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &gfx2d1_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_gfx3d[] =3D { + F_MN( 27000000, P_PXO, 1, 0), + F_MN( 48000000, P_PLL8, 1, 8), + F_MN( 54857000, P_PLL8, 1, 7), + F_MN( 64000000, P_PLL8, 1, 6), + F_MN( 76800000, P_PLL8, 1, 5), + F_MN( 96000000, P_PLL8, 1, 4), + F_MN(128000000, P_PLL8, 1, 3), + F_MN(145455000, P_PLL2, 2, 11), + F_MN(160000000, P_PLL2, 1, 5), + F_MN(177778000, P_PLL2, 2, 9), + F_MN(200000000, P_PLL2, 1, 4), + F_MN(228571000, P_PLL2, 2, 7), + F_MN(266667000, P_PLL2, 1, 3), + F_MN(320000000, P_PLL2, 2, 5), + { } +}; + +/* + * MSM8x60-specific GFX3D clocks + * + * MSM8660 uses different reset bits for the GFX3D banked MND divider: + * - Bank 0: mnctr_reset_bit =3D 23 (MSM8960 uses 25) + * - Bank 1: mnctr_reset_bit =3D 22 (MSM8960 uses 24) + * + * This was verified against the webOS 2.6 kernel source (clock-8x60.c). + */ +static struct clk_dyn_rcg gfx3d_src =3D { + .ns_reg[0] =3D 0x008c, + .ns_reg[1] =3D 0x008c, + .md_reg[0] =3D 0x0084, + .md_reg[1] =3D 0x0088, + .bank_reg =3D 0x0080, + .mn[0] =3D { + .mnctr_en_bit =3D 8, + .mnctr_reset_bit =3D 23, + .mnctr_mode_shift =3D 9, + .n_val_shift =3D 18, + .m_val_shift =3D 4, + .width =3D 4, + }, + .mn[1] =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 22, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 14, + .m_val_shift =3D 4, + .width =3D 4, + }, + .s[0] =3D { + .src_sel_shift =3D 3, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .s[1] =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .mux_sel_bit =3D 11, + .freq_tbl =3D clk_tbl_gfx3d, + .clkr =3D { + .enable_reg =3D 0x0080, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx3d_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_dyn_rcg_ops, + }, + }, +}; + +static struct clk_branch gfx3d_clk =3D { + .halt_reg =3D 0x01c8, + .halt_bit =3D 4, + .clkr =3D { + .enable_reg =3D 0x0080, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx3d_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &gfx3d_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_ijpeg[] =3D { + F_MN( 27000000, P_PXO, 1, 0), + F_MN( 36570000, P_PLL8, 2, 21), + F_MN( 54860000, P_PLL8, 1, 7), + F_MN( 96000000, P_PLL8, 1, 4), + F_MN(109710000, P_PLL8, 2, 7), + F_MN(128000000, P_PLL8, 1, 3), + F_MN(153600000, P_PLL8, 2, 5), + F_MN(200000000, P_PLL2, 1, 4), + F_MN(228571000, P_PLL2, 2, 7), + { } +}; + +static struct clk_rcg ijpeg_src =3D { + .ns_reg =3D 0x00a0, + .md_reg =3D 0x009c, + .mn =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 7, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 16, + .m_val_shift =3D 8, + .width =3D 8, + }, + .p =3D { + .pre_div_shift =3D 12, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_ijpeg, + .clkr =3D { + .enable_reg =3D 0x0098, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "ijpeg_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch ijpeg_clk =3D { + .halt_reg =3D 0x01c8, + .halt_bit =3D 24, + .clkr =3D { + .enable_reg =3D 0x0098, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "ijpeg_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &ijpeg_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_jpegd[] =3D { + F_MN( 64000000, P_PLL8, 1, 6), + F_MN( 76800000, P_PLL8, 1, 5), + F_MN( 96000000, P_PLL8, 1, 4), + F_MN(160000000, P_PLL2, 1, 5), + F_MN(200000000, P_PLL2, 1, 4), + { } +}; + +static struct clk_rcg jpegd_src =3D { + .ns_reg =3D 0x00ac, + .p =3D { + .pre_div_shift =3D 12, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_jpegd, + .clkr =3D { + .enable_reg =3D 0x00a4, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "jpegd_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch jpegd_clk =3D { + .halt_reg =3D 0x01c8, + .halt_bit =3D 19, + .clkr =3D { + .enable_reg =3D 0x00a4, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "jpegd_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &jpegd_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_mdp[] =3D { + F_MN( 9600000, P_PLL8, 1, 40), + F_MN( 13710000, P_PLL8, 1, 28), + F_MN( 27000000, P_PXO, 1, 0), + F_MN( 29540000, P_PLL8, 1, 13), + F_MN( 34910000, P_PLL8, 1, 11), + F_MN( 38400000, P_PLL8, 1, 10), + F_MN( 59080000, P_PLL8, 2, 13), + F_MN( 76800000, P_PLL8, 1, 5), + F_MN( 85330000, P_PLL8, 2, 9), + F_MN( 96000000, P_PLL8, 1, 4), + F_MN(128000000, P_PLL8, 1, 3), + F_MN(160000000, P_PLL2, 1, 5), + F_MN(177780000, P_PLL2, 2, 9), + F_MN(200000000, P_PLL2, 1, 4), + { } +}; + +static struct clk_dyn_rcg mdp_src =3D { + .ns_reg[0] =3D 0x00d0, + .ns_reg[1] =3D 0x00d0, + .md_reg[0] =3D 0x00c4, + .md_reg[1] =3D 0x00c8, + .bank_reg =3D 0x00c0, + .mn[0] =3D { + .mnctr_en_bit =3D 8, + .mnctr_reset_bit =3D 31, + .mnctr_mode_shift =3D 9, + .n_val_shift =3D 22, + .m_val_shift =3D 8, + .width =3D 8, + }, + .mn[1] =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 30, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 14, + .m_val_shift =3D 8, + .width =3D 8, + }, + .s[0] =3D { + .src_sel_shift =3D 3, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .s[1] =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .mux_sel_bit =3D 11, + .freq_tbl =3D clk_tbl_mdp, + .clkr =3D { + .enable_reg =3D 0x00c0, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mdp_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_dyn_rcg_ops, + }, + }, +}; + +static struct clk_branch mdp_clk =3D { + .halt_reg =3D 0x01d0, + .halt_bit =3D 10, + .clkr =3D { + .enable_reg =3D 0x00c0, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mdp_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &mdp_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch mdp_lut_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 13, + .clkr =3D { + .enable_reg =3D 0x016c, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &mdp_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "mdp_lut_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch mdp_vsync_clk =3D { + .halt_reg =3D 0x01cc, + .halt_bit =3D 22, + .clkr =3D { + .enable_reg =3D 0x0058, + .enable_mask =3D BIT(6), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mdp_vsync_clk", + .parent_data =3D (const struct clk_parent_data[]){ + { .fw_name =3D "pxo", .name =3D "pxo_board" }, + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + }, + }, +}; + +/* + * MSM8x60-specific MDP pixel clocks + * + * MSM8660 uses MD16 register format where M value is in bits [31:16] and + * D value (2*N-M) is in bits [15:0]. This differs from MSM8960 which uses + * MD8 format with M in bits [23:16] and D in bits [7:0]. + * + * NS register offset is 0x00DC (not 0x00E0 as in MSM8960). + */ +static const struct freq_tbl clk_tbl_mdp_pixel[] =3D { + /* Format: { freq, src, pre_div, m, n } - use pre_div like clk_tbl_cam */ + { 25600000, P_PLL8, 3, 1, 5 }, /* 384 / 3 * 1/5 =3D 25.6 MHz */ + { 27000000, P_PXO, 1, 0, 0 }, /* PXO direct =3D 27 MHz */ + { 42667000, P_PLL8, 1, 1, 9 }, /* 384 * 1/9 =3D 42.667 MHz */ + { 43192000, P_PLL8, 1, 64, 569 }, /* 384 * 64/569 =3D 43.192 MHz */ + { 48000000, P_PLL8, 4, 1, 2 }, /* 384 / 4 * 1/2 =3D 48 MHz */ + { 53990000, P_PLL8, 2, 169, 601 }, /* 384 / 2 * 169/601 =3D 53.99 MHz */ + { 64000000, P_PLL8, 3, 1, 2 }, /* 384 / 3 * 1/2 =3D 64 MHz */ + { 69300000, P_PLL8, 1, 231, 1280 }, /* 384 * 231/1280 =3D 69.3 MHz - HP= TouchPad panel */ + { 76800000, P_PLL8, 1, 1, 5 }, /* 384 * 1/5 =3D 76.8 MHz */ + { 85333000, P_PLL8, 1, 2, 9 }, /* 384 * 2/9 =3D 85.333 MHz */ + { 96000000, P_PLL8, 4, 0, 0 }, /* 384 / 4 =3D 96 MHz */ + { 100030000, P_PLL8, 2, 211, 405 }, /* 384 / 2 * 211/405 =3D 100.03 MHz = */ + { 106500000, P_PLL8, 1, 71, 256 }, /* 384 * 71/256 =3D 106.5 MHz */ + { 109714000, P_PLL8, 1, 2, 7 }, /* 384 * 2/7 =3D 109.714 MHz */ + { 128000000, P_PLL8, 3, 0, 0 }, /* 384 / 3 =3D 128 MHz */ + { } +}; + +static struct clk_rcg mdp_pixel_src =3D { + .ns_reg =3D 0x00dc, + .md_reg =3D 0x00d8, + .mn =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 7, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 16, + .m_val_shift =3D 8, /* Try standard 8-bit format */ + .width =3D 8, /* Try standard 8-bit width */ + }, + .p =3D { + .pre_div_shift =3D 14, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_mdp_pixel, + .clkr =3D { + .enable_reg =3D 0x00d4, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mdp_pixel_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, /* Testing with clk_tbl_cam-style freq_tbl */ + }, + }, +}; + +static struct clk_branch mdp_pixel_clk =3D { + .halt_reg =3D 0x01d0, + .halt_bit =3D 23, + .clkr =3D { + .enable_reg =3D 0x00d4, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mdp_pixel_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &mdp_pixel_src.clkr.hw, + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch mdp_lcdc_clk =3D { + .halt_reg =3D 0x01d0, + .halt_bit =3D 21, + .clkr =3D { + .enable_reg =3D 0x00d4, + .enable_mask =3D BIT(8), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mdp_lcdc_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &mdp_pixel_src.clkr.hw, + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_rot[] =3D { + F_MN( 27000000, P_PXO, 1, 0), + F_MN( 29540000, P_PLL8, 1, 13), + F_MN( 32000000, P_PLL8, 1, 12), + F_MN( 38400000, P_PLL8, 1, 10), + F_MN( 48000000, P_PLL8, 1, 8), + F_MN( 54860000, P_PLL8, 1, 7), + F_MN( 64000000, P_PLL8, 1, 6), + F_MN( 76800000, P_PLL8, 1, 5), + F_MN( 96000000, P_PLL8, 1, 4), + F_MN(100000000, P_PLL2, 1, 8), + F_MN(114290000, P_PLL2, 2, 14), + F_MN(128000000, P_PLL8, 1, 3), + F_MN(133330000, P_PLL2, 1, 6), + F_MN(160000000, P_PLL2, 1, 5), + { } +}; + +static struct clk_dyn_rcg rot_src =3D { + .ns_reg[0] =3D 0x00e8, + .ns_reg[1] =3D 0x00e8, + .md_reg[0] =3D 0x00e0, + .md_reg[1] =3D 0x00e4, + .bank_reg =3D 0x00e8, + .mn[0] =3D { + .mnctr_en_bit =3D 8, + .mnctr_reset_bit =3D 25, + .mnctr_mode_shift =3D 9, + .n_val_shift =3D 22, + .m_val_shift =3D 8, + .width =3D 8, + }, + .mn[1] =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 24, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 14, + .m_val_shift =3D 8, + .width =3D 8, + }, + .s[0] =3D { + .src_sel_shift =3D 3, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .s[1] =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .mux_sel_bit =3D 11, + .freq_tbl =3D clk_tbl_rot, + .clkr =3D { + .enable_reg =3D 0x00e0, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "rot_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_dyn_rcg_ops, + }, + }, +}; + +static struct clk_branch rot_clk =3D { + .halt_reg =3D 0x01d0, + .halt_bit =3D 15, + /* + * The rotator core branch is fed through the 'rot' GDSC, which uses + * the legacy footswitch sequence. During rotator_runtime_suspend the + * clock is disabled before genpd collapses the footswitch, and the + * branch halt status does not assert while the legacy footswitch is + * still powered =E2=80=94 so clk_branch_toggle() times out with "rot_clk + * status stuck at 'on'". The enable bit is still cleared (the clock + * is gated); only the readback is unreliable, exactly like the + * rot_axi / gfx3d_axi / vcodec_axi branches below. Skip the poll. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x00e0, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "rot_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &rot_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +#define F_TV(f, s, p_r, _m, _n) \ + { \ + .freq =3D f, \ + .src =3D s, \ + .pre_div =3D p_r, \ + .m =3D _m, \ + .n =3D _n, \ + } + +static const struct freq_tbl clk_tbl_tv[] =3D { + F_TV( 25200000, P_HDMI_PLL, 1, 0, 0), + F_TV( 27000000, P_HDMI_PLL, 1, 0, 0), + F_TV( 27030000, P_HDMI_PLL, 1, 0, 0), + F_TV( 74250000, P_HDMI_PLL, 1, 0, 0), + F_TV(148500000, P_HDMI_PLL, 1, 0, 0), + { } +}; + +static const struct parent_map mmcc_pxo_hdmi_map[] =3D { + { P_PXO, 0 }, + { P_HDMI_PLL, 3 } +}; + +static const struct clk_parent_data mmcc_pxo_hdmi[] =3D { + { .fw_name =3D "pxo", .name =3D "pxo_board" }, + { .fw_name =3D "hdmipll", .name =3D "hdmi_pll" }, +}; + +static struct clk_rcg tv_src =3D { + .ns_reg =3D 0x00f4, + .md_reg =3D 0x00f0, + .mn =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 7, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 16, + .m_val_shift =3D 8, + .width =3D 8, + }, + .p =3D { + .pre_div_shift =3D 14, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_hdmi_map, + }, + .freq_tbl =3D clk_tbl_tv, + .clkr =3D { + .enable_reg =3D 0x00ec, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "tv_src", + .parent_data =3D mmcc_pxo_hdmi, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_hdmi), + .ops =3D &clk_rcg_bypass_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch tv_enc_clk =3D { + .halt_reg =3D 0x01d4, + .halt_bit =3D 9, + .clkr =3D { + .enable_reg =3D 0x00ec, + .enable_mask =3D BIT(8), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &tv_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "tv_enc_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch tv_dac_clk =3D { + .halt_reg =3D 0x01d4, + .halt_bit =3D 10, + .clkr =3D { + .enable_reg =3D 0x00ec, + .enable_mask =3D BIT(10), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &tv_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "tv_dac_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch mdp_tv_clk =3D { + .halt_reg =3D 0x01d4, + .halt_bit =3D 12, + .clkr =3D { + .enable_reg =3D 0x00ec, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &tv_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "mdp_tv_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch hdmi_tv_clk =3D { + .halt_reg =3D 0x01d4, + .halt_bit =3D 11, + .clkr =3D { + .enable_reg =3D 0x00ec, + .enable_mask =3D BIT(12), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &tv_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "hdmi_tv_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch hdmi_app_clk =3D { + .halt_reg =3D 0x01cc, + .halt_bit =3D 25, + .clkr =3D { + .enable_reg =3D 0x005c, + .enable_mask =3D BIT(11), + .hw.init =3D &(struct clk_init_data){ + .parent_data =3D (const struct clk_parent_data[]){ + { .fw_name =3D "pxo", .name =3D "pxo_board" }, + }, + .num_parents =3D 1, + .name =3D "hdmi_app_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static const struct freq_tbl clk_tbl_vcodec[] =3D { + F_MN( 27000000, P_PXO, 1, 0), + F_MN( 32000000, P_PLL8, 1, 12), + F_MN( 48000000, P_PLL8, 1, 8), + F_MN( 54860000, P_PLL8, 1, 7), + F_MN( 96000000, P_PLL8, 1, 4), + F_MN(133330000, P_PLL2, 1, 6), + F_MN(200000000, P_PLL2, 1, 4), + F_MN(228570000, P_PLL2, 2, 7), + { } +}; + +static struct clk_dyn_rcg vcodec_src =3D { + .ns_reg[0] =3D 0x0100, + .ns_reg[1] =3D 0x0100, + .md_reg[0] =3D 0x00fc, + .md_reg[1] =3D 0x0128, + .bank_reg =3D 0x00f8, + .mn[0] =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 31, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 11, + .m_val_shift =3D 8, + .width =3D 8, + }, + .mn[1] =3D { + .mnctr_en_bit =3D 10, + .mnctr_reset_bit =3D 30, + .mnctr_mode_shift =3D 11, + .n_val_shift =3D 19, + .m_val_shift =3D 8, + .width =3D 8, + }, + .s[0] =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .s[1] =3D { + .src_sel_shift =3D 14, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .mux_sel_bit =3D 13, + .freq_tbl =3D clk_tbl_vcodec, + .clkr =3D { + .enable_reg =3D 0x00f8, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vcodec_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_dyn_rcg_ops, + }, + }, +}; + +static struct clk_branch vcodec_clk =3D { + .halt_reg =3D 0x01d0, + .halt_bit =3D 29, + .clkr =3D { + .enable_reg =3D 0x00f8, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vcodec_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &vcodec_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +/* + * VPE source is a simple divider+source RCG (no MND), per legacy clock-8x= 60.c + * (NS_DIVSRC(15,12,d,2,0,s), set_rate_nop). The divisor lives in NS[15:12] + * (4 bits); 160 MHz =3D PLL2 / 5. Using F_MN here silently forces pre_div= =3D1 and + * drops the divisor into the nonexistent MND, leaving vpe_src grossly + * misclocked -> the first VPE register access hangs the AXI bus. + */ +static const struct freq_tbl clk_tbl_vpe[] =3D { + F_DIV( 27000000, P_PXO, 1), + F_DIV( 34909000, P_PLL8, 11), + F_DIV( 38400000, P_PLL8, 10), + F_DIV( 64000000, P_PLL8, 6), + F_DIV( 76800000, P_PLL8, 5), + F_DIV( 96000000, P_PLL8, 4), + F_DIV(100000000, P_PLL2, 8), + F_DIV(160000000, P_PLL2, 5), + { } +}; + +static struct clk_rcg vpe_src =3D { + .ns_reg =3D 0x0118, + .p =3D { + .pre_div_shift =3D 12, + .pre_div_width =3D 4, /* NS[15:12], legacy BM(15,12); holds /11 */ + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_vpe, + .clkr =3D { + .enable_reg =3D 0x0110, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vpe_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch vpe_clk =3D { + .halt_reg =3D 0x01c8, + .halt_bit =3D 28, + /* + * Same as rot_clk: the VPE core branch runs through the legacy- + * footswitch 'vpe' GDSC, so its halt status is unreliable at + * runtime-suspend disable time ("vpe_clk status stuck at 'on'"). + * The enable bit still gates the clock; skip the readback poll. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0110, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vpe_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &vpe_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_vfe[] =3D { + F_MN( 13960000, P_PLL8, 2, 55), + F_MN( 27000000, P_PXO, 1, 0), + F_MN( 36570000, P_PLL8, 2, 21), + F_MN( 38400000, P_PLL8, 2, 20), + F_MN( 45180000, P_PLL8, 2, 17), + F_MN( 48000000, P_PLL8, 2, 16), + F_MN( 54860000, P_PLL8, 1, 7), + F_MN( 64000000, P_PLL8, 2, 12), + F_MN( 76800000, P_PLL8, 1, 5), + F_MN( 96000000, P_PLL8, 2, 8), + F_MN(109710000, P_PLL8, 2, 7), + F_MN(128000000, P_PLL8, 1, 3), + F_MN(153600000, P_PLL8, 2, 5), + F_MN(200000000, P_PLL2, 2, 8), + F_MN(228570000, P_PLL2, 2, 7), + F_MN(266667000, P_PLL2, 1, 3), + { } +}; + +static struct clk_rcg vfe_src =3D { + .ns_reg =3D 0x010c, + .md_reg =3D 0x0108, + .mn =3D { + .mnctr_en_bit =3D 5, + .mnctr_reset_bit =3D 7, + .mnctr_mode_shift =3D 6, + .n_val_shift =3D 16, + .m_val_shift =3D 8, + .width =3D 8, + }, + .p =3D { + .pre_div_shift =3D 10, + .pre_div_width =3D 2, + }, + .s =3D { + .src_sel_shift =3D 0, + .parent_map =3D mmcc_pxo_pll8_pll2_map, + }, + .freq_tbl =3D clk_tbl_vfe, + .clkr =3D { + .enable_reg =3D 0x0104, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vfe_src", + .parent_data =3D mmcc_pxo_pll8_pll2, + .num_parents =3D ARRAY_SIZE(mmcc_pxo_pll8_pll2), + .ops =3D &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch vfe_clk =3D { + .halt_reg =3D 0x01cc, + .halt_bit =3D 6, + /* + * VFE clock halt status is unreliable when VFE is not actively + * processing data. Use BRANCH_HALT_SKIP to avoid timeouts during + * clock enable before camera streaming starts. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0104, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vfe_clk", + .parent_hws =3D (const struct clk_hw*[]){ + &vfe_src.clkr.hw + }, + .num_parents =3D 1, + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch vfe_csi0_clk =3D { + .halt_reg =3D 0x01cc, + .halt_bit =3D 7, + /* + * The VFE CSI clock halt status is unreliable when the CSI block + * is not actively receiving data. Use BRANCH_HALT_SKIP to avoid + * timeouts during clock enable. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0104, + .enable_mask =3D BIT(12), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &vfe_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "vfe_csi0_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +/* + * VFE CSI1 clock - enables CSI1 to VFE data path. + * Legacy kernel had separate CSI0_VFE (BIT 12) and CSI1_VFE (BIT 10). + */ +static struct clk_branch vfe_csi1_clk =3D { + .halt_reg =3D 0x01cc, + .halt_bit =3D 8, + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0104, + .enable_mask =3D BIT(10), + .hw.init =3D &(struct clk_init_data){ + .parent_hws =3D (const struct clk_hw*[]){ + &vfe_src.clkr.hw + }, + .num_parents =3D 1, + .name =3D "vfe_csi1_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch gmem_axi_clk =3D { + .halt_reg =3D 0x01d8, + .halt_bit =3D 6, + .clkr =3D { + .enable_reg =3D 0x0018, + .enable_mask =3D BIT(24), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gmem_axi_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch ijpeg_axi_clk =3D { + .halt_reg =3D 0x01d8, + .halt_bit =3D 4, + .clkr =3D { + .enable_reg =3D 0x0018, + .enable_mask =3D BIT(21), + .hw.init =3D &(struct clk_init_data){ + .name =3D "ijpeg_axi_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch mmss_imem_axi_clk =3D { + .halt_reg =3D 0x01d8, + .halt_bit =3D 7, + .clkr =3D { + .enable_reg =3D 0x0018, + .enable_mask =3D BIT(22), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mmss_imem_axi_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch jpegd_axi_clk =3D { + .halt_reg =3D 0x01d8, + .halt_bit =3D 5, + .clkr =3D { + .enable_reg =3D 0x0018, + .enable_mask =3D BIT(25), + .hw.init =3D &(struct clk_init_data){ + .name =3D "jpegd_axi_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch vcodec_axi_clk =3D { + .halt_reg =3D 0x01d8, + .halt_bit =3D 3, + .clkr =3D { + .enable_reg =3D 0x0018, + .enable_mask =3D BIT(19), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vcodec_axi_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch vcodec_axi_a_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 26, + .clkr =3D { + .enable_reg =3D 0x0020, + .enable_mask =3D BIT(25), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vcodec_axi_a_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch vcodec_axi_b_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 25, + .clkr =3D { + .enable_reg =3D 0x0020, + .enable_mask =3D BIT(26), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vcodec_axi_b_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch vfe_axi_clk =3D { + .halt_reg =3D 0x01d8, + .halt_bit =3D 0, + /* + * VFE clocks don't properly report halt status when the VFE power + * domain is off. Skip halt checking to avoid enable failures. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0018, + .enable_mask =3D BIT(18), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vfe_axi_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch vpe_axi_clk =3D { + .halt_reg =3D 0x01d8, + .halt_bit =3D 1, + .clkr =3D { + .enable_reg =3D 0x0018, + .enable_mask =3D BIT(26), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vpe_axi_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_IS_CRITICAL, + }, + }, +}; + +static struct clk_branch mdp_axi_clk =3D { + .halt_reg =3D 0x01d8, + .halt_bit =3D 8, + .halt_check =3D BRANCH_HALT_DELAY, + .clkr =3D { + .enable_reg =3D 0x0018, + .enable_mask =3D BIT(23), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mdp_axi_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch rot_axi_clk =3D { + .halt_reg =3D 0x01d8, + .halt_bit =3D 2, + /* + * The rotator AXI clock shares the MMSS fabric with MDP. While MDP is + * scanning out to the display the fabric never idles, so this branch + * cannot halt and its status stays stuck at 'on'. Without skipping the + * halt check, every rotator runtime-PM suspend triggers a + * "rot_axi_clk status stuck at 'on'" WARN storm (same class as + * gfx3d_axi_clk / vcodec_axi_b_clk). Skip the halt poll. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0020, + .enable_mask =3D BIT(22), + .hw.init =3D &(struct clk_init_data){ + .name =3D "rot_axi_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch gfx3d_axi_clk =3D { + .halt_reg =3D 0x01e8, + .halt_bit =3D 21, + /* + * This branch clock shares the MMSS fabric with MDP. When MDP is + * actively scanning out to the display, the fabric never idles, + * preventing this clock from halting. Use BRANCH_HALT_SKIP to avoid + * the "status stuck at 'on'" warning during GPU runtime PM suspend. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0244, + .enable_mask =3D BIT(0), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx3d_axi_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch amp_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 18, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(24), + .hw.init =3D &(struct clk_init_data){ + .name =3D "amp_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch csi0_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 16, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(7), + .hw.init =3D &(struct clk_init_data){ + .name =3D "csi0_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +/* + * CSI1 AHB clock - separate from CSI0. + * Legacy kernel had CSI0_PCLK (BIT 7) and CSI1_PCLK (BIT 20). + */ +static struct clk_branch csi1_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 17, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(20), + .hw.init =3D &(struct clk_init_data){ + .name =3D "csi1_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch dsi_m_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 19, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(9), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi_m_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch dsi_s_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 21, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(18), + .hw.init =3D &(struct clk_init_data){ + .name =3D "dsi_s_ahb_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_IS_CRITICAL, + }, + }, +}; + +static struct clk_branch gfx2d0_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 2, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(19), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx2d0_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch gfx2d1_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 3, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(2), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx2d1_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch gfx3d_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 4, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(3), + .hw.init =3D &(struct clk_init_data){ + .name =3D "gfx3d_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch hdmi_m_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 5, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(14), + .hw.init =3D &(struct clk_init_data){ + .name =3D "hdmi_m_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch hdmi_s_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 6, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(4), + .hw.init =3D &(struct clk_init_data){ + .name =3D "hdmi_s_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch ijpeg_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 9, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(5), + .hw.init =3D &(struct clk_init_data){ + .name =3D "ijpeg_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch jpegd_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 7, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(21), + .hw.init =3D &(struct clk_init_data){ + .name =3D "jpegd_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch mdp_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 11, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(10), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mdp_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch mmss_imem_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 12, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(6), + .hw.init =3D &(struct clk_init_data){ + .name =3D "mmss_imem_ahb_clk", + .ops =3D &clk_branch_ops, + .flags =3D CLK_IS_CRITICAL, + }, + }, +}; + +static struct clk_branch rot_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 13, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(12), + .hw.init =3D &(struct clk_init_data){ + .name =3D "rot_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch smmu_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 22, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(15), + .hw.init =3D &(struct clk_init_data){ + .name =3D "smmu_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch tv_enc_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 23, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(25), + .hw.init =3D &(struct clk_init_data){ + .name =3D "tv_enc_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch vcodec_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 10, + /* + * The halt bit at 0x01dc[10] does not settle within the standard + * 200 =C2=B5s poll window when this branch is disabled (e.g. on VIDC + * runtime suspend), producing "vcodec_ahb_clk status stuck at 'on'" + * WARN traces with an EBUSY return. Skip the halt check like + * vfe_ahb_clk below =E2=80=94 the AHB bus stays clocked while other MMSS + * peripherals share it, so the halt bit can't be relied on. + */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(11), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vcodec_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch vfe_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 14, + /* Same as vfe_axi_clk - skip halt check */ + .halt_check =3D BRANCH_HALT_SKIP, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(13), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vfe_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_branch vpe_ahb_clk =3D { + .halt_reg =3D 0x01dc, + .halt_bit =3D 15, + .clkr =3D { + .enable_reg =3D 0x0008, + .enable_mask =3D BIT(16), + .hw.init =3D &(struct clk_init_data){ + .name =3D "vpe_ahb_clk", + .ops =3D &clk_branch_ops, + }, + }, +}; + +static struct clk_regmap *mmcc_msm8660_clks[] =3D { + [TV_ENC_AHB_CLK] =3D &tv_enc_ahb_clk.clkr, + [AMP_AHB_CLK] =3D &_ahb_clk.clkr, + [JPEGD_AHB_CLK] =3D &jpegd_ahb_clk.clkr, + [GFX2D0_AHB_CLK] =3D &gfx2d0_ahb_clk.clkr, + [DSI_S_AHB_CLK] =3D &dsi_s_ahb_clk.clkr, + [VPE_AHB_CLK] =3D &vpe_ahb_clk.clkr, + [SMMU_AHB_CLK] =3D &smmu_ahb_clk.clkr, + [HDMI_M_AHB_CLK] =3D &hdmi_m_ahb_clk.clkr, + [VFE_AHB_CLK] =3D &vfe_ahb_clk.clkr, + [ROT_AHB_CLK] =3D &rot_ahb_clk.clkr, + [VCODEC_AHB_CLK] =3D &vcodec_ahb_clk.clkr, + [MDP_AHB_CLK] =3D &mdp_ahb_clk.clkr, + [DSI_M_AHB_CLK] =3D &dsi_m_ahb_clk.clkr, + [CSI0_AHB_CLK] =3D &csi0_ahb_clk.clkr, + [CSI1_AHB_CLK] =3D &csi1_ahb_clk.clkr, + [MMSS_IMEM_AHB_CLK] =3D &mmss_imem_ahb_clk.clkr, + [IJPEG_AHB_CLK] =3D &ijpeg_ahb_clk.clkr, + [HDMI_S_AHB_CLK] =3D &hdmi_s_ahb_clk.clkr, + [GFX3D_AHB_CLK] =3D &gfx3d_ahb_clk.clkr, + [GFX2D1_AHB_CLK] =3D &gfx2d1_ahb_clk.clkr, + [JPEGD_AXI_CLK] =3D &jpegd_axi_clk.clkr, + [GMEM_AXI_CLK] =3D &gmem_axi_clk.clkr, + [MDP_AXI_CLK] =3D &mdp_axi_clk.clkr, + [MMSS_IMEM_AXI_CLK] =3D &mmss_imem_axi_clk.clkr, + [IJPEG_AXI_CLK] =3D &ijpeg_axi_clk.clkr, + [GFX3D_AXI_CLK] =3D &gfx3d_axi_clk.clkr, + [VCODEC_AXI_CLK] =3D &vcodec_axi_clk.clkr, + [VFE_AXI_CLK] =3D &vfe_axi_clk.clkr, + [VPE_AXI_CLK] =3D &vpe_axi_clk.clkr, + [ROT_AXI_CLK] =3D &rot_axi_clk.clkr, + [VCODEC_AXI_A_CLK] =3D &vcodec_axi_a_clk.clkr, + [VCODEC_AXI_B_CLK] =3D &vcodec_axi_b_clk.clkr, + [CSI0_SRC] =3D &csi0_src.clkr, + [CSI0_CLK] =3D &csi0_clk.clkr, + [CSI0_PHY_CLK] =3D &csi0_phy_clk.clkr, + [CSI1_SRC] =3D &csi1_src.clkr, + [CSI1_CLK] =3D &csi1_clk.clkr, + [CSI1_PHY_CLK] =3D &csi1_phy_clk.clkr, + [DSI_SRC] =3D &dsi1_src.clkr, + [DSI_CLK] =3D &dsi1_clk.clkr, + [CSI_PIX_CLK] =3D &csi_pix_clk.clkr, + [CSI_RDI_CLK] =3D &csi_rdi_clk.clkr, + [MDP_VSYNC_CLK] =3D &mdp_vsync_clk.clkr, + [HDMI_APP_CLK] =3D &hdmi_app_clk.clkr, + [GFX2D0_SRC] =3D &gfx2d0_src.clkr, + [GFX2D0_CLK] =3D &gfx2d0_clk.clkr, + [GFX2D1_SRC] =3D &gfx2d1_src.clkr, + [GFX2D1_CLK] =3D &gfx2d1_clk.clkr, + [GFX3D_SRC] =3D &gfx3d_src.clkr, + [GFX3D_CLK] =3D &gfx3d_clk.clkr, + [IJPEG_SRC] =3D &ijpeg_src.clkr, + [IJPEG_CLK] =3D &ijpeg_clk.clkr, + [JPEGD_SRC] =3D &jpegd_src.clkr, + [JPEGD_CLK] =3D &jpegd_clk.clkr, + [MDP_SRC] =3D &mdp_src.clkr, + [MDP_CLK] =3D &mdp_clk.clkr, + [MDP_LUT_CLK] =3D &mdp_lut_clk.clkr, + [MDP_PIXEL_SRC] =3D &mdp_pixel_src.clkr, + [MDP_PIXEL_CLK] =3D &mdp_pixel_clk.clkr, + [MDP_LCDC_CLK] =3D &mdp_lcdc_clk.clkr, + [DSI1_BYTE_SRC] =3D &dsi1_byte_src.clkr, + [DSI1_BYTE_CLK] =3D &dsi1_byte_clk.clkr, + [DSI1_ESC_SRC] =3D &dsi1_esc_src.clkr, + [DSI1_ESC_CLK] =3D &dsi1_esc_clk.clkr, + [ROT_SRC] =3D &rot_src.clkr, + [ROT_CLK] =3D &rot_clk.clkr, + [TV_ENC_CLK] =3D &tv_enc_clk.clkr, + [TV_DAC_CLK] =3D &tv_dac_clk.clkr, + [HDMI_TV_CLK] =3D &hdmi_tv_clk.clkr, + [MDP_TV_CLK] =3D &mdp_tv_clk.clkr, + [TV_SRC] =3D &tv_src.clkr, + [VCODEC_SRC] =3D &vcodec_src.clkr, + [VCODEC_CLK] =3D &vcodec_clk.clkr, + [VFE_SRC] =3D &vfe_src.clkr, + [VFE_CLK] =3D &vfe_clk.clkr, + [VFE_CSI0_CLK] =3D &vfe_csi0_clk.clkr, + [VFE_CSI1_CLK] =3D &vfe_csi1_clk.clkr, + [VPE_SRC] =3D &vpe_src.clkr, + [VPE_CLK] =3D &vpe_clk.clkr, + [DSI_PIXEL_SRC] =3D &dsi1_pixel_src.clkr, + [DSI_PIXEL_CLK] =3D &dsi1_pixel_clk.clkr, + [CAMCLK0_SRC] =3D &camclk0_src.clkr, + [CAMCLK0_CLK] =3D &camclk0_clk.clkr, + [CAMCLK1_SRC] =3D &camclk1_src.clkr, + [CAMCLK1_CLK] =3D &camclk1_clk.clkr, + [CSIPHYTIMER_SRC] =3D &csiphytimer_src.clkr, + [CSIPHY1_TIMER_CLK] =3D &csiphy1_timer_clk.clkr, + [CSIPHY0_TIMER_CLK] =3D &csiphy0_timer_clk.clkr, + [PLL2] =3D &pll2.clkr, +}; + +static const struct qcom_reset_map mmcc_msm8660_resets[] =3D { + [GFX3D_AXI_RESET] =3D { 0x0208, 17 }, + [VPE_AXI_RESET] =3D { 0x0208, 15 }, + [IJPEG_AXI_RESET] =3D { 0x0208, 14 }, + [MPD_AXI_RESET] =3D { 0x0208, 13 }, + [VFE_AXI_RESET] =3D { 0x0208, 9 }, + [SP_AXI_RESET] =3D { 0x0208, 8 }, + [VCODEC_AXI_RESET] =3D { 0x0208, 7 }, + [ROT_AXI_RESET] =3D { 0x0208, 6 }, + [VCODEC_AXI_A_RESET] =3D { 0x0208, 5 }, + [VCODEC_AXI_B_RESET] =3D { 0x0208, 4 }, + [FAB_S3_AXI_RESET] =3D { 0x0208, 3 }, + [FAB_S2_AXI_RESET] =3D { 0x0208, 2 }, + [FAB_S1_AXI_RESET] =3D { 0x0208, 1 }, + [FAB_S0_AXI_RESET] =3D { 0x0208 }, + [SMMU_GFX3D_ABH_RESET] =3D { 0x020c, 31 }, + [SMMU_VPE_AHB_RESET] =3D { 0x020c, 30 }, + [SMMU_VFE_AHB_RESET] =3D { 0x020c, 29 }, + [SMMU_ROT_AHB_RESET] =3D { 0x020c, 28 }, + [SMMU_VCODEC_B_AHB_RESET] =3D { 0x020c, 27 }, + [SMMU_VCODEC_A_AHB_RESET] =3D { 0x020c, 26 }, + [SMMU_MDP1_AHB_RESET] =3D { 0x020c, 25 }, + [SMMU_MDP0_AHB_RESET] =3D { 0x020c, 24 }, + [SMMU_JPEGD_AHB_RESET] =3D { 0x020c, 23 }, + [SMMU_IJPEG_AHB_RESET] =3D { 0x020c, 22 }, + [APU_AHB_RESET] =3D { 0x020c, 18 }, + [CSI_AHB_RESET] =3D { 0x020c, 17 }, + [TV_ENC_AHB_RESET] =3D { 0x020c, 15 }, + [VPE_AHB_RESET] =3D { 0x020c, 14 }, + [FABRIC_AHB_RESET] =3D { 0x020c, 13 }, + [GFX3D_AHB_RESET] =3D { 0x020c, 10 }, + [HDMI_AHB_RESET] =3D { 0x020c, 9 }, + [MSSS_IMEM_AHB_RESET] =3D { 0x020c, 8 }, + [IJPEG_AHB_RESET] =3D { 0x020c, 7 }, + [DSI_M_AHB_RESET] =3D { 0x020c, 6 }, + [DSI_S_AHB_RESET] =3D { 0x020c, 5 }, + [JPEGD_AHB_RESET] =3D { 0x020c, 4 }, + [MDP_AHB_RESET] =3D { 0x020c, 3 }, + [ROT_AHB_RESET] =3D { 0x020c, 2 }, + [VCODEC_AHB_RESET] =3D { 0x020c, 1 }, + [VFE_AHB_RESET] =3D { 0x020c, 0 }, + [CSIPHY0_RESET] =3D { 0x0210, 29 }, + [CSIPHY1_RESET] =3D { 0x0210, 28 }, + [CSI_RDI_RESET] =3D { 0x0210, 27 }, + [CSI_PIX_RESET] =3D { 0x0210, 26 }, + [VFE_CSI_RESET] =3D { 0x0210, 24 }, + [MDP_RESET] =3D { 0x0210, 21 }, + [AMP_RESET] =3D { 0x0210, 20 }, + [JPEGD_RESET] =3D { 0x0210, 19 }, + [CSI1_RESET] =3D { 0x0210, 18 }, + [VPE_RESET] =3D { 0x0210, 17 }, + [MMSS_FABRIC_RESET] =3D { 0x0210, 16 }, + [VFE_RESET] =3D { 0x0210, 15 }, + [GFX3D_RESET] =3D { 0x0210, 12 }, + [HDMI_RESET] =3D { 0x0210, 11 }, + [MMSS_IMEM_RESET] =3D { 0x0210, 10 }, + [IJPEG_RESET] =3D { 0x0210, 9 }, + [CSI0_RESET] =3D { 0x0210, 8 }, + [DSI_RESET] =3D { 0x0210, 7 }, + [VCODEC_RESET] =3D { 0x0210, 6 }, + [MDP_TV_RESET] =3D { 0x0210, 4 }, + [MDP_VSYNC_RESET] =3D { 0x0210, 3 }, + [ROT_RESET] =3D { 0x0210, 2 }, + [TV_HDMI_RESET] =3D { 0x0210, 1 }, + [TV_ENC_RESET] =3D { 0x0210, 0 }, +}; + +/* + * MSM8x60 legacy footswitches. + * These use a different register layout than modern GDSCs: + * - Bit 8: ENABLE (set to enable power) + * - Bit 5: CLAMP (set to clamp I/O) + * - No status bit, requires timed delays + */ +static struct gdsc gfx2d0_gdsc =3D { + .gdscr =3D 0x0180, + .resets =3D (unsigned int []){ GFX2D0_AHB_RESET }, + .reset_count =3D 1, + .pd =3D { + .name =3D "gfx2d0", + }, + .pwrsts =3D PWRSTS_OFF_ON, + .flags =3D LEGACY_FOOTSWITCH | SW_RESET, +}; + +static struct gdsc gfx2d1_gdsc =3D { + .gdscr =3D 0x0184, + .resets =3D (unsigned int []){ GFX2D1_AHB_RESET }, + .reset_count =3D 1, + .pd =3D { + .name =3D "gfx2d1", + }, + .pwrsts =3D PWRSTS_OFF_ON, + .flags =3D LEGACY_FOOTSWITCH | SW_RESET, +}; + +static struct gdsc gfx3d_gdsc =3D { + .gdscr =3D 0x0188, + /* + * GFX3D (Adreno 220) requires the core reset to be toggled on every + * power-on, in addition to the AHB reset. The legacy MSM8x60 + * footswitch driver (mach-msm/footswitch-8x60.c) does exactly this: + * after powering the GFX3D rail it issues an extra + * clk_reset(core_clk, ASSERT/DEASSERT). Without it the Adreno 220 + * core (parameter cache) comes up free-running, producing the + * deterministic period-8 render cycle on the HP TouchPad. Listing + * GFX3D_RESET here lets the GDSC framework assert/deassert it around + * the rail charge (see gdsc_enable LEGACY_FOOTSWITCH path), which is + * the framework-correct equivalent of the legacy toggle. + */ + .resets =3D (unsigned int []){ GFX3D_AHB_RESET, GFX3D_RESET }, + .reset_count =3D 2, + .pd =3D { + .name =3D "gfx3d", + }, + .pwrsts =3D PWRSTS_OFF_ON, + /* + * RPM_ALWAYS_ON: keep the GFX3D footswitch powered across runtime PM + * idle (clocks still gate), collapsing it only on system suspend. The + * Adreno 220 shares the MMSS AXI fabric with the MDP4 display; cold- + * cycling this footswitch on every GPU idle forces an a2xx_hw_init + * microcode reload whose MMIO burst can stall the shared bus when it + * lands during an MDP client-switch underrun, hard-hanging the SoC. + * Mirrors legacy KGSL, which parked the GPU in SLEEP (rail up, clocks + * gated) during use and only power-collapsed (SLUMBER) on suspend. + */ + .flags =3D LEGACY_FOOTSWITCH | SW_RESET | RPM_ALWAYS_ON, +}; + +static struct gdsc ijpeg_gdsc =3D { + .gdscr =3D 0x01a0, + /* + * IJPEG (Gemini) requires the AXI and CORE resets to be toggled on + * every power-on, in addition to the AHB reset. The legacy + * mach-msm/footswitch-8x60.c does exactly this: setup_clocks -> + * clk_reset(axi, ASSERT) + clk_reset(ahb, ASSERT) + clk_reset(core, + * ASSERT) -> udelay -> rail charge -> deassert in reverse -> extra + * core ASSERT/DEASSERT toggle. HTC and Samsung MSM8660 trees use the + * same sequence. With only the AHB reset toggled the JPEG register + * file comes up healthy (CPU reads/writes look fine) but the FE's + * AXI-side address generator and burst sequencer stay in whatever + * sub-state QSBL left them -- typically wedged waiting for an + * AR-channel ready that never comes. The WE survives because writes + * are post-and-forget and don't drive AR. Symptom: FE reads return + * idle 0x80 regardless of buffer contents while WE writes succeed. + * Listing AXI + CORE here lets the GDSC framework assert/deassert + * them around rail charge (gdsc_enable LEGACY_FOOTSWITCH | SW_RESET + * path), matching the legacy/HTC/Samsung convergent recipe and + * mirroring the gfx3d_gdsc precedent above. + */ + .resets =3D (unsigned int []){ + IJPEG_AXI_RESET, + IJPEG_AHB_RESET, + IJPEG_RESET, + }, + .reset_count =3D 3, + .pd =3D { + .name =3D "ijpeg", + }, + .pwrsts =3D PWRSTS_OFF_ON, + .flags =3D LEGACY_FOOTSWITCH | SW_RESET, +}; + +static struct gdsc mdp_gdsc =3D { + .gdscr =3D 0x0190, + .resets =3D (unsigned int []){ MDP_AHB_RESET }, + .reset_count =3D 1, + .pd =3D { + .name =3D "mdp", + }, + .pwrsts =3D PWRSTS_OFF_ON, + .flags =3D LEGACY_FOOTSWITCH | SW_RESET, +}; + +static struct gdsc rot_gdsc =3D { + .gdscr =3D 0x018c, + .resets =3D (unsigned int []){ ROT_AHB_RESET }, + .reset_count =3D 1, + .pd =3D { + .name =3D "rot", + }, + .pwrsts =3D PWRSTS_OFF_ON, + /* + * Skip SW_RESET during power domain enable, similar to VFE_GDSC. + * Asserting ROT_AHB_RESET during GDSC enable may cause a glitch + * affecting other MMSS peripherals during early boot. + * + * RPM_ALWAYS_ON: the rotator is an m2m block that pm_runtime-cycles per + * job; without this its footswitch re-enables on the shared MMSS fabric + * on every rotation burst, glitching MDP scanout. Keep it powered across + * runtime idle (clocks still gate); collapse only on system suspend. + */ + .flags =3D LEGACY_FOOTSWITCH | RPM_ALWAYS_ON, +}; + +static struct gdsc ved_gdsc =3D { + .gdscr =3D 0x0194, + .resets =3D (unsigned int []){ VCODEC_AHB_RESET }, + .reset_count =3D 1, + .pd =3D { + .name =3D "ved", + }, + .pwrsts =3D PWRSTS_OFF_ON, + .flags =3D LEGACY_FOOTSWITCH | SW_RESET, +}; + +static struct gdsc vfe_gdsc =3D { + .gdscr =3D 0x0198, + .resets =3D (unsigned int []){ VFE_AHB_RESET }, + .reset_count =3D 1, + .pd =3D { + .name =3D "vfe", + }, + .pwrsts =3D PWRSTS_OFF_ON, + /* + * Skip SW_RESET during power domain enable. Asserting VFE_AHB_RESET + * during GDSC enable appears to cause a glitch that affects other + * MMSS peripherals (specifically MDP display). The VFE driver performs + * its own software reset via vfe_reset() after enabling clocks, so + * the GDSC-level reset is not strictly required. + * + * Note: VFE is session-scoped (powered at streamon, collapsed at + * streamoff) so it only re-enables once per capture session -- not + * worth RPM_ALWAYS_ON (cf. rot_gdsc, which cycles per m2m job). + */ + .flags =3D LEGACY_FOOTSWITCH, +}; + +static struct gdsc vpe_gdsc =3D { + .gdscr =3D 0x019c, + .resets =3D (unsigned int []){ VPE_AHB_RESET }, + .reset_count =3D 1, + .pd =3D { + .name =3D "vpe", + }, + .pwrsts =3D PWRSTS_OFF_ON, + /* + * Skip SW_RESET during power domain enable, similar to VFE_GDSC. + * Asserting VPE_AHB_RESET during GDSC enable may cause a glitch + * affecting other MMSS peripherals during early boot. + * + * Note: VPE is session-scoped (powered at streamon, collapsed at + * streamoff) so it only re-enables once per session -- not worth + * RPM_ALWAYS_ON (cf. rot_gdsc, which cycles per m2m job). + */ + .flags =3D LEGACY_FOOTSWITCH, +}; + +static struct gdsc *mmcc_msm8660_gdscs[] =3D { + [GFX2D0_GDSC] =3D &gfx2d0_gdsc, + [GFX2D1_GDSC] =3D &gfx2d1_gdsc, + [GFX3D_GDSC] =3D &gfx3d_gdsc, + [IJPEG_GDSC] =3D &ijpeg_gdsc, + [MDP_GDSC] =3D &mdp_gdsc, + [ROT_GDSC] =3D &rot_gdsc, + [VED_GDSC] =3D &ved_gdsc, + [VFE_GDSC] =3D &vfe_gdsc, + [VPE_GDSC] =3D &vpe_gdsc, +}; + +static const struct regmap_config mmcc_msm8660_regmap_config =3D { + .reg_bits =3D 32, + .reg_stride =3D 4, + .val_bits =3D 32, + .max_register =3D 0x334, + .fast_io =3D true, +}; + +static const struct qcom_cc_desc mmcc_msm8660_desc =3D { + .config =3D &mmcc_msm8660_regmap_config, + .clks =3D mmcc_msm8660_clks, + .num_clks =3D ARRAY_SIZE(mmcc_msm8660_clks), + .resets =3D mmcc_msm8660_resets, + .num_resets =3D ARRAY_SIZE(mmcc_msm8660_resets), + .gdscs =3D mmcc_msm8660_gdscs, + .num_gdscs =3D ARRAY_SIZE(mmcc_msm8660_gdscs), +}; + +static const struct of_device_id mmcc_msm8660_match_table[] =3D { + { .compatible =3D "qcom,mmcc-msm8660", .data =3D &mmcc_msm8660_desc }, + { .compatible =3D "qcom,mmcc-apq8060", .data =3D &mmcc_msm8660_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, mmcc_msm8660_match_table); + +/* + * MSM8660 MMCC register offsets for initialization. + * Based on webOS kernel arch/arm/mach-msm/clock-8x60.c + */ + +/* Reset registers - safe to deassert */ +#define SW_RESET_ALL_REG 0x0204 +#define SW_RESET_AHB_REG 0x020c +#define SW_RESET_AXI_REG 0x0208 +#define SW_RESET_CORE_REG 0x0210 + +/* AHB enable registers - contain both control bits and clock enables */ +#define AHB_EN_REG 0x0008 +#define AHB_EN2_REG 0x0038 + +/* AXI enable registers - control AXI bus paths for memory access */ +#define MAXI_EN_REG 0x0018 +#define MAXI_EN3_REG 0x002c +#define SAXI_EN_REG 0x01d8 + +/* Misc CC registers */ +#define CSI_CC_REG 0x0040 +#define MISC_CC_REG 0x0058 +#define MISC_CC2_REG 0x005c + +/* CC registers - FORCE_CORE_ON in upper bits, enable in lower bits */ +#define GFX2D0_CC_REG 0x0060 +#define GFX2D1_CC_REG 0x0074 +#define GFX3D_CC_REG 0x0080 +#define IJPEG_CC_REG 0x0098 +#define JPEGD_CC_REG 0x00a4 +#define MDP_CC_REG 0x00c0 +#define PIXEL_CC_REG 0x00d4 +#define PIXEL_CC2_REG 0x0120 /* Not used as enable_reg */ +#define ROT_CC_REG 0x00e0 +#define TV_CC_REG 0x00ec +#define TV_CC2_REG 0x0124 /* Not used as enable_reg */ +#define VCODEC_CC_REG 0x00f8 +#define VFE_CC_REG 0x0104 +#define VPE_CC_REG 0x0110 + +/* + * Mask for FORCE_CORE_ON and sleep/wakeup delay bits in CC registers. + * Only touches upper bits to avoid conflicting with clock enable bits + * in the lower portion of the register. + */ +#define CC_FORCE_CORE_ON_MASK 0xe0ff0000 +#define CC_FORCE_CORE_ON_VAL 0x80ff0000 /* FORCE_CORE_ON + 0xFF delays */ +#define VCODEC_FORCE_CORE_ON 0xc0ff0000 /* VCODEC uses different bits */ + +static void mmcc_msm8660_init_hw(struct regmap *regmap) +{ + u32 val; + + /* + * Configure PLL2 (MM_PLL1) to 800 MHz for VFE and other MM clocks. + * + * PLL rate =3D PXO * (L + M/N) =3D 27 MHz * (29 + 17/27) =3D 800 MHz + * + * The bootloader (moboot) enables PLL2 but does not configure L/M/N, + * leaving it at whatever values are in the hardware. We must set it + * properly for VFE to reach 228 MHz. Without this, VFE gets ~66 MHz. + */ + regmap_read(regmap, 0x320, &val); /* PLL2 L value */ + if (val !=3D 29) { + u32 m_val, n_val; + /* PLL2 is not configured for 800 MHz, configure it */ + regmap_read(regmap, 0x324, &m_val); + regmap_read(regmap, 0x328, &n_val); + pr_info("mmcc-msm8660: Configuring PLL2 for 800 MHz (was L=3D%u M=3D%u N= =3D%u)\n", + val, m_val, n_val); + + /* Disable PLL output first */ + regmap_update_bits(regmap, 0x31c, BIT(0), 0); + udelay(10); + + /* Set L=3D29, M=3D17, N=3D27 for 800 MHz from 27 MHz PXO */ + regmap_write(regmap, 0x320, 29); /* L value */ + regmap_write(regmap, 0x324, 17); /* M value */ + regmap_write(regmap, 0x328, 27); /* N value */ + + /* Configure PLL: enable main output, set VCO */ + regmap_write(regmap, 0x32c, 0x00800000); + + /* Enable PLL: bypass off, reset deassert, output enable */ + regmap_update_bits(regmap, 0x31c, BIT(1), BIT(1)); /* Bypass off */ + udelay(10); + regmap_update_bits(regmap, 0x31c, BIT(2), BIT(2)); /* Reset deassert */ + udelay(50); + regmap_update_bits(regmap, 0x31c, BIT(0), BIT(0)); /* Output enable */ + udelay(50); + + /* Verify PLL locked (status register at 0x334, bit 16) */ + regmap_read(regmap, 0x334, &val); + if (val & BIT(16)) + pr_info("mmcc-msm8660: PLL2 locked at 800 MHz\n"); + else + pr_warn("mmcc-msm8660: PLL2 may not be locked (status=3D0x%x)\n", val); + } + + /* + * MSM8660 MMCC hardware initialization based on webOS kernel. + * + * WebOS sets specific control bits in AHB_EN_REG: + * rmwreg(0x00000003, AHB_EN_REG, 0x0F7FFFFF); + * BIT(0) and BIT(1) are control bits (FPB enable, HW gating disable), + * NOT clock enables. Clock enables start at BIT(2) and above. + * + * We initialize these control bits but leave clock enable bits + * for the clock framework to manage. + */ + + /* + * Set FPB enable and disable HW gating in AHB_EN_REG. + * BIT(0) =3D FPB clock enable + * BIT(1) =3D Disable HW gating for all AHB clocks + * These are required for CSI register writes to work. + */ + regmap_update_bits(regmap, AHB_EN_REG, 0x3, 0x3); + + /* + * AHB_EN2_REG contains additional control bits including + * VFE_AHB FORCE_CORE_ON to prevent memory collapse. + * WebOS: rmwreg(0x000007F9, AHB_EN2_REG, 0x7FFFBFFF); + */ + regmap_update_bits(regmap, AHB_EN2_REG, 0x7fffbfff, 0x000007f9); + + /* + * Initialize AXI bus registers for memory access paths. + * These enable HW gating and set FORCE_CORE_ON bits for AXI clocks. + * WebOS: rmwreg(0x000307F9, MAXI_EN_REG, 0x0FFFFFFF); + * writel(0x3FE7FCFF, MAXI_EN3_REG); + * writel(0x000001D8, SAXI_EN_REG); + * Note: MAXI_EN2_REG is owned by RPM, don't touch it. + */ + regmap_update_bits(regmap, MAXI_EN_REG, 0x0fffffff, 0x000307f9); + regmap_write(regmap, MAXI_EN3_REG, 0x3fe7fcff); + regmap_write(regmap, SAXI_EN_REG, 0x000001d8); + + /* Deassert all MM resets */ + regmap_write(regmap, SW_RESET_ALL_REG, 0); + regmap_write(regmap, SW_RESET_AHB_REG, 0); + regmap_write(regmap, SW_RESET_AXI_REG, 0); + regmap_write(regmap, SW_RESET_CORE_REG, 0); + + /* + * Initialize CSI and MISC CC registers. + * + * CSI_CC_REG (0x040): webOS camera dump shows 0x85 + * - Bit 0: CSI digital wrapper 0 enable + * - Bit 2: CSI digital wrapper 1 enable + * - Bit 7: Unknown (possibly global CSI enable) + * Without these bits set, CSIPHY data never reaches VFE. + * + * MISC_CC_REG (0x058): webOS camera dump shows 0x0400 + * - Bit 10: CSI1-to-VFE async bridge enable + * MSM8660 has direct CSIPHY->VFE path, NOT the csi_pix/csi_rdi mux + * architecture found in VFE3.2+. The csi_pix_clk/csi_rdi_clk (bits + * 25-26 and 12-13) don't exist on this SoC - only bit 10 matters. + * + * MISC_CC2_REG: webOS shows 0x004007fd - additional enables. + */ + regmap_write(regmap, CSI_CC_REG, 0x00000085); + regmap_update_bits(regmap, MISC_CC_REG, 0xfefff7ff, 0x00000400); + regmap_update_bits(regmap, MISC_CC2_REG, 0xffff7fff, 0x000007fd); + /* Set dsi_byte_clk src to DSI PHY PLL, hdmi_app_clk src to PXO */ + regmap_update_bits(regmap, MISC_CC2_REG, 0x00424003, 0x00400001); + + /* + * Set FORCE_CORE_ON bits in all multimedia CC registers to prevent + * core memories from being collapsed when clocks are halted. + * + * Value 0x80ff0000 sets: + * - Bit 31: FORCE_CORE_ON + * - Bits 16-23: Safe sleep/wakeup delay values (0xFF) + * + * We use regmap_update_bits to only touch upper bits, avoiding + * conflict with clock enable bits in the lower portion. + */ + + /* Graphics */ + regmap_update_bits(regmap, GFX2D0_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE= _CORE_ON_VAL); + regmap_update_bits(regmap, GFX2D1_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE= _CORE_ON_VAL); + regmap_update_bits(regmap, GFX3D_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE_= CORE_ON_VAL); + + /* Display */ + regmap_update_bits(regmap, MDP_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE_CO= RE_ON_VAL); + regmap_update_bits(regmap, PIXEL_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE_= CORE_ON_VAL); + regmap_write(regmap, PIXEL_CC2_REG, 0x000004ff); + regmap_update_bits(regmap, TV_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE_COR= E_ON_VAL); + regmap_write(regmap, TV_CC2_REG, 0x000004ff); + regmap_update_bits(regmap, ROT_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE_CO= RE_ON_VAL); + + /* Video */ + regmap_update_bits(regmap, VCODEC_CC_REG, CC_FORCE_CORE_ON_MASK, VCODEC_F= ORCE_CORE_ON); + regmap_update_bits(regmap, VPE_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE_CO= RE_ON_VAL); + + /* Camera */ + regmap_update_bits(regmap, VFE_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE_CO= RE_ON_VAL); + + /* JPEG */ + regmap_update_bits(regmap, IJPEG_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE_= CORE_ON_VAL); + regmap_update_bits(regmap, JPEGD_CC_REG, CC_FORCE_CORE_ON_MASK, CC_FORCE_= CORE_ON_VAL); +} + +/* + * Unhalt all RPM fabric AXI master ports. + * + * webOS downstream kernels (msm_bus_board_8660.c) program halt registers + * on three fabrics: APPS (4 masters), SYSTEM (17 masters), MMSS + * (14 masters). The downstream GDSC driver calls msm_bus_axi_portunhalt() + * in footswitch_enable() when each power domain comes up; mainline GDSC + * does not, leaving fabric master ports in their default (potentially + * halted) state. This can cause DMA stalls and arbitration hangs. + * + * MMSS fabric master ports (port:name from webOS enum): + * 0:MDP_PORT0 1:MDP_PORT1 2:ADM1_PORT0 3:ROTATOR + * 4:GFX3D 5:JPEG_DEC 6:GFX2D0 7:VFE + * 8:VPE 9:JPEG_ENC 10:GFX2D1 11:APPS_FAB + * 12:HDCODEC0 13:HDCODEC1 + * + * SYSTEM fabric master ports: + * 0:APPSS_FAB 1:SPS 2:ADM0_PORT0 3:ADM0_PORT1 + * 4:ADM1_PORT0 5:ADM1_PORT1 6:LPASS_PROC 7:MSS_PROCI + * 8:MSS_PROCD 9:MDM_PORT0 10:LPASS 11:CPSS_FPB + * 12:SYSTEM_FPB 13:MMSS_FPB 14:ADM1_AHB 15:ADM0_AHB + * 16:MSS_MDM_PORT1 + * + * APPS fabric has 4 masters; unhalting all is safe. + * + * Driven from mmcc probe because it runs after the qcom_rpm platform + * driver registers (mmcc is module_platform_driver, RPM is platform_init). + * Doing the same in gcc probe (core_initcall) is too early -- RPM is + * not yet bound and qcom_rpm_write() has no target. + */ +static void mmcc_msm8660_unhalt_fabric_ports(struct device *dev) +{ + struct device_node *rpm_node; + struct platform_device *rpm_pdev; + struct qcom_rpm *rpm; + /* halt_data[0]=3D0 =3D CLK_UNHALT for all bits; halt_data[1] =3D port ma= sk */ + u32 mmss_halt[2] =3D {0, GENMASK(13, 0)}; + u32 sys_halt[2] =3D {0, GENMASK(16, 0)}; + u32 apps_halt[2] =3D {0, GENMASK(3, 0)}; + int rc; + + rpm_node =3D of_find_compatible_node(NULL, NULL, "qcom,rpm-msm8660"); + if (!rpm_node) + return; + + rpm_pdev =3D of_find_device_by_node(rpm_node); + of_node_put(rpm_node); + if (!rpm_pdev) + return; + + rpm =3D dev_get_drvdata(&rpm_pdev->dev); + if (!rpm) { + put_device(&rpm_pdev->dev); + return; + } + + rc =3D qcom_rpm_write(rpm, QCOM_RPM_ACTIVE_STATE, + QCOM_RPM_MM_FABRIC_HALT, mmss_halt, 2); + if (rc) + dev_warn(dev, "MMSS fabric unhalt failed: %d\n", rc); + else + dev_info(dev, "MMSS fabric: unhalted all master ports (0-13)\n"); + + rc =3D qcom_rpm_write(rpm, QCOM_RPM_ACTIVE_STATE, + QCOM_RPM_SYS_FABRIC_HALT, sys_halt, 2); + if (rc) + dev_warn(dev, "system fabric unhalt failed: %d\n", rc); + else + dev_info(dev, "system fabric: unhalted all master ports (0-16)\n"); + + rc =3D qcom_rpm_write(rpm, QCOM_RPM_ACTIVE_STATE, + QCOM_RPM_APPS_FABRIC_HALT, apps_halt, 2); + if (rc) + dev_warn(dev, "apps fabric unhalt failed: %d\n", rc); + else + dev_info(dev, "apps fabric: unhalted all master ports (0-3)\n"); + + put_device(&rpm_pdev->dev); +} + +static int mmcc_msm8660_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + regmap =3D qcom_cc_map(pdev, &mmcc_msm8660_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Initialize MMCC hardware before registering clocks */ + mmcc_msm8660_init_hw(regmap); + + /* + * Unhalt all MMSS / SYSTEM / APPS fabric AXI master ports. Must + * happen before any peripheral (MMSS block for MMSS fabric; + * CE2/ADM/SDC/USB for SYSTEM fabric) performs DMA. Driven from + * mmcc probe because it runs after the qcom_rpm platform driver + * has bound (gcc core_initcall is too early). + */ + mmcc_msm8660_unhalt_fabric_ports(&pdev->dev); + + return qcom_cc_really_probe(&pdev->dev, &mmcc_msm8660_desc, regmap); +} + +static struct platform_driver mmcc_msm8660_driver =3D { + .probe =3D mmcc_msm8660_probe, + .driver =3D { + .name =3D "mmcc-msm8660", + .of_match_table =3D mmcc_msm8660_match_table, + }, +}; + +module_platform_driver(mmcc_msm8660_driver); + +MODULE_DESCRIPTION("Qualcomm MSM8x60 Multimedia Clock Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mmcc-msm8660"); --=20 2.43.0