From: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
To: Georgi Djakov <djakov@kernel.org>,
Bjorn Andersson <andersson@kernel.org>,
Abel Vesa <abelvesa@kernel.org>, Stephen Boyd <sboyd@kernel.org>,
Brian Masney <bmasney+clk@redhat.com>,
Jerome Brunet <jbrunet+clk@baylibre.com>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>
Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-msm@vger.kernel.org, linux-clk@vger.kernel.org,
devicetree@vger.kernel.org,
Kathiravan Thirumoorthy
<kathiravan.thirumoorthy@oss.qualcomm.com>
Subject: [PATCH 4/4] clk: qcom: apss-ipq9650: Add IPQ9650 APSS clock controller driver
Date: Wed, 07 Oct 2026 12:20:47 +0530 [thread overview]
Message-ID: <20261007-ipq9650_apss_clk-v1-4-fd4c2b3cbdea@oss.qualcomm.com> (raw)
In-Reply-To: <20261007-ipq9650_apss_clk-v1-0-fd4c2b3cbdea@oss.qualcomm.com>
The APSS clock controller on IPQ9650 provides the CPU and L3 clocks.
The four Cortex-A55 cores (silver), the Cortex-A78 core (gold) and the
L3 are each clocked by their own Zonda PLL. Each clock is selected
through a glitch-free mux (GFM) whose parents are XO, GPLL0 and the
corresponding PLL. The gold mux can also select the PLL's post-divider
output.
The L3 needs to be scaled along with the CPUs, so register an icc-clk
provider in which both CPU clusters vote on the L3 core clock.
Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
---
drivers/clk/qcom/Kconfig | 9 +
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/apss-ipq9650.c | 385 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 395 insertions(+)
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 27d0ab24d50a..a17f661c3102 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -599,6 +599,15 @@ config IPQ_APSS_6018
Say Y if you want to support CPU frequency scaling on
ipq based devices.
+config IPQ_APSS_9650
+ tristate "IPQ9650 APSS Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ default y if IPQ_GCC_9650
+ help
+ Support for APSS Clock controller on Qualcomm IPQ9650 platform.
+ Say Y if you want to support CPU frequency scaling on IPQ9650 based
+ devices.
+
config IPQ_CMN_PLL
tristate "IPQ CMN PLL Clock Controller"
depends on ARM64 || COMPILE_TEST
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 6affb5073f26..ec3d8512a812 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_CLK_QCM2290_GPUCC) += gpucc-qcm2290.o
obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o
obj-$(CONFIG_IPQ_APSS_5424) += apss-ipq5424.o
obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o
+obj-$(CONFIG_IPQ_APSS_9650) += apss-ipq9650.o
obj-$(CONFIG_IPQ_CMN_PLL) += ipq-cmn-pll.o
obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o
obj-$(CONFIG_IPQ_GCC_5018) += gcc-ipq5018.o
diff --git a/drivers/clk/qcom/apss-ipq9650.c b/drivers/clk/qcom/apss-ipq9650.c
new file mode 100644
index 000000000000..7602f3896fe3
--- /dev/null
+++ b/drivers/clk/qcom/apss-ipq9650.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/interconnect-provider.h>
+
+#include <dt-bindings/clock/qcom,apss-ipq.h>
+#include <dt-bindings/interconnect/qcom,ipq9650.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-branch.h"
+#include "clk-regmap.h"
+#include "common.h"
+
+enum {
+ DT_XO,
+ DT_GPLL0,
+};
+
+static const struct pll_vco silver_pll_vco[] = {
+ { 816000000, 1968000000, 0 },
+};
+
+static const struct pll_vco gold_pll_vco[] = {
+ { 696000000, 1992000000, 0 },
+};
+
+static const struct pll_vco l3_pll_vco[] = {
+ { 696000000, 1512000000, 0 },
+};
+
+static const struct alpha_pll_config silver_apss_pll_config = {
+ .l = 0x23,
+ .config_ctl_val = 0x08200920,
+ .config_ctl_hi_val = 0x05008001,
+ .config_ctl_hi1_val = 0x04000000,
+ .user_ctl_val = 0x01000009,
+};
+
+static const struct alpha_pll_config gold_apss_pll_config = {
+ .l = 0x26,
+ .config_ctl_val = 0x08200920,
+ .config_ctl_hi_val = 0x05008001,
+ .config_ctl_hi1_val = 0x04000000,
+ .user_ctl_val = 0x01000009,
+};
+
+static const struct alpha_pll_config l3_pll_config = {
+ .l = 0x1e,
+ .config_ctl_val = 0x08200920,
+ .config_ctl_hi_val = 0x05008001,
+ .config_ctl_hi1_val = 0x04000000,
+ .user_ctl_val = 0x01000009,
+};
+
+static struct clk_alpha_pll silver_apss_pll = {
+ .offset = 0x0,
+ .config = &silver_apss_pll_config,
+ .vco_table = silver_pll_vco,
+ .num_vco = ARRAY_SIZE(silver_pll_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_ZONDA],
+ .flags = SUPPORTS_DYNAMIC_UPDATE,
+ .clkr = {
+ .enable_reg = 0x0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "silver_apss_pll",
+ .parent_data = &(const struct clk_parent_data){
+ .index = DT_XO,
+ },
+ .num_parents = 1,
+ .flags = CLK_IS_CRITICAL,
+ .ops = &clk_alpha_pll_zonda_ops,
+ },
+ },
+};
+
+static struct clk_alpha_pll gold_apss_pll = {
+ .offset = 0x10000,
+ .config = &gold_apss_pll_config,
+ .vco_table = gold_pll_vco,
+ .num_vco = ARRAY_SIZE(gold_pll_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_ZONDA],
+ .flags = SUPPORTS_DYNAMIC_UPDATE,
+ .clkr = {
+ .enable_reg = 0x10000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gold_apss_pll",
+ .parent_data = &(const struct clk_parent_data){
+ .index = DT_XO,
+ },
+ .num_parents = 1,
+ .flags = CLK_IS_CRITICAL,
+ .ops = &clk_alpha_pll_zonda_ops,
+ },
+ },
+};
+
+static const struct clk_div_table gold_apss_pll_post_div_table[] = {
+ { 0x0, 1 },
+ { 0x1, 2 },
+ { }
+};
+
+static struct clk_alpha_pll_postdiv gold_apss_pll_postdiv = {
+ .offset = 0x10000,
+ .post_div_shift = 8,
+ .post_div_table = gold_apss_pll_post_div_table,
+ .num_post_div = ARRAY_SIZE(gold_apss_pll_post_div_table),
+ .width = 2,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_ZONDA],
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gold_apss_pll_postdiv",
+ .parent_hws = (const struct clk_hw*[]){
+ &gold_apss_pll.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_alpha_pll_postdiv_zonda_ops,
+ },
+};
+
+static struct clk_alpha_pll l3_pll = {
+ .offset = 0x20000,
+ .config = &l3_pll_config,
+ .vco_table = l3_pll_vco,
+ .num_vco = ARRAY_SIZE(l3_pll_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_ZONDA],
+ .flags = SUPPORTS_DYNAMIC_UPDATE,
+ .clkr = {
+ .enable_reg = 0x20000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "l3_pll",
+ .parent_data = &(const struct clk_parent_data){
+ .index = DT_XO,
+ },
+ .num_parents = 1,
+ .flags = CLK_IS_CRITICAL,
+ .ops = &clk_alpha_pll_zonda_ops,
+ },
+ },
+};
+
+struct clk_gfm {
+ u32 mux_reg;
+ u32 mux_mask;
+ struct clk_hw hw;
+ void __iomem *base;
+};
+
+#define to_clk_gfm(_hw) container_of(_hw, struct clk_gfm, hw)
+
+static u8 clk_gfm_get_parent(struct clk_hw *hw)
+{
+ struct clk_gfm *gfm = to_clk_gfm(hw);
+ u32 val;
+
+ val = readl(gfm->base + gfm->mux_reg);
+ return val & gfm->mux_mask;
+}
+
+static int clk_gfm_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_gfm *gfm = to_clk_gfm(hw);
+ u32 val;
+
+ val = readl(gfm->base + gfm->mux_reg);
+ val &= ~gfm->mux_mask;
+ val |= (index & gfm->mux_mask);
+ writel(val, gfm->base + gfm->mux_reg);
+
+ return 0;
+}
+
+static const struct clk_ops clk_gfm_ops = {
+ .get_parent = clk_gfm_get_parent,
+ .set_parent = clk_gfm_set_parent,
+ .determine_rate = __clk_mux_determine_rate_closest,
+};
+
+static struct clk_gfm silver_apss_pll_gfmux = {
+ .mux_reg = 0x84,
+ .mux_mask = 0xf,
+ .hw.init = &(struct clk_init_data) {
+ .name = "silver_apss_pll_gfmux",
+ .parent_data = (const struct clk_parent_data[]) {
+ { .index = DT_XO },
+ { .index = DT_GPLL0 },
+ { .hw = &silver_apss_pll.clkr.hw },
+ },
+ .num_parents = 3,
+ .ops = &clk_gfm_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_gfm gold_apss_pll_gfmux = {
+ .mux_reg = 0x10084,
+ .mux_mask = 0xf,
+ .hw.init = &(struct clk_init_data) {
+ .name = "gold_apss_pll_gfmux",
+ .parent_data = (const struct clk_parent_data[]) {
+ { .index = DT_XO },
+ { .index = DT_GPLL0 },
+ { .hw = &gold_apss_pll.clkr.hw },
+ { .hw = &gold_apss_pll_postdiv.clkr.hw },
+ },
+ .num_parents = 4,
+ .ops = &clk_gfm_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_gfm l3_pll_gfmux = {
+ .mux_reg = 0x20084,
+ .mux_mask = 0xf,
+ .hw.init = &(struct clk_init_data) {
+ .name = "l3_pll_gfmux",
+ .parent_data = (const struct clk_parent_data[]) {
+ { .index = DT_XO },
+ { .index = DT_GPLL0 },
+ { .hw = &l3_pll.clkr.hw },
+ },
+ .num_parents = 3,
+ .ops = &clk_gfm_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_branch apss_silver_core_clk = {
+ .halt_reg = 0x8c,
+ .clkr = {
+ .enable_reg = 0x8c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "apss_silver_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &silver_apss_pll_gfmux.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch apss_gold_core_clk = {
+ .halt_reg = 0x1008c,
+ .clkr = {
+ .enable_reg = 0x1008c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "apss_gold_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &gold_apss_pll_gfmux.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch l3_core_clk = {
+ .halt_reg = 0x2008c,
+ .clkr = {
+ .enable_reg = 0x2008c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "l3_core_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &l3_pll_gfmux.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_hw *apss_hws[] = {
+ &silver_apss_pll_gfmux.hw,
+ &l3_pll_gfmux.hw,
+ &gold_apss_pll_gfmux.hw,
+};
+
+static struct clk_regmap *apss_clks[] = {
+ [APSS_PLL_EARLY] = &silver_apss_pll.clkr,
+ [APSS_SILVER_CORE_CLK] = &apss_silver_core_clk.clkr,
+ [L3_PLL] = &l3_pll.clkr,
+ [L3_CORE_CLK] = &l3_core_clk.clkr,
+ [APSS_GOLD_PLL] = &gold_apss_pll.clkr,
+ [APSS_GOLD_PLL_POSTDIV] = &gold_apss_pll_postdiv.clkr,
+ [APSS_GOLD_CORE_CLK] = &apss_gold_core_clk.clkr,
+};
+
+static const struct regmap_config apss_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x2fffc,
+ .fast_io = true,
+};
+
+static struct clk_alpha_pll *ipq9650_apss_plls[] = {
+ &silver_apss_pll,
+ &gold_apss_pll,
+ &l3_pll,
+};
+
+static const struct qcom_cc_driver_data ipq9650_apss_driver_data = {
+ .alpha_plls = ipq9650_apss_plls,
+ .num_alpha_plls = ARRAY_SIZE(ipq9650_apss_plls),
+};
+
+#define IPQ_APPS_PLL_ID (9650 * 3) /* some unique value */
+
+static const struct qcom_icc_hws_data icc_cpu_l3[] = {
+ { MASTER_CPU_SILVER, SLAVE_L3, L3_CORE_CLK },
+ { MASTER_CPU_GOLD, SLAVE_L3, L3_CORE_CLK },
+};
+
+static const struct qcom_cc_desc apss_desc = {
+ .config = &apss_regmap_config,
+ .clks = apss_clks,
+ .num_clks = ARRAY_SIZE(apss_clks),
+ .icc_hws = icc_cpu_l3,
+ .num_icc_hws = ARRAY_SIZE(icc_cpu_l3),
+ .icc_first_node_id = IPQ_APPS_PLL_ID,
+ .clk_hws = apss_hws,
+ .num_clk_hws = ARRAY_SIZE(apss_hws),
+ .driver_data = &ipq9650_apss_driver_data,
+};
+
+static int apss_ipq9650_probe(struct platform_device *pdev)
+{
+ const struct qcom_cc_desc *desc = &apss_desc;
+ struct regmap *regmap;
+ struct clk_gfm *gfm;
+ void __iomem *base;
+ int i;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &apss_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ for (i = 0; i < desc->num_clk_hws; i++) {
+ gfm = to_clk_gfm(apss_hws[i]);
+ gfm->base = base;
+ }
+
+ return qcom_cc_really_probe(&pdev->dev, desc, regmap);
+}
+
+static const struct of_device_id apss_ipq9650_match_table[] = {
+ { .compatible = "qcom,ipq9650-apss-clk" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, apss_ipq9650_match_table);
+
+static struct platform_driver apss_ipq9650_driver = {
+ .probe = apss_ipq9650_probe,
+ .driver = {
+ .name = "qcom,ipq9650-apss-clk",
+ .of_match_table = apss_ipq9650_match_table,
+ .sync_state = icc_sync_state,
+ },
+};
+
+module_platform_driver(apss_ipq9650_driver);
+
+MODULE_DESCRIPTION("Qualcomm IPQ9650 APSS Clock Driver");
+MODULE_LICENSE("GPL");
--
2.34.1
prev parent reply other threads:[~2026-10-07 6:51 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-07 6:50 [PATCH 0/4] Add Qualcomm's IPQ9650 APSS Clock Driver Support Kathiravan Thirumoorthy
2026-10-07 6:50 ` [PATCH 1/4] interconnect: icc-clk: support N:1 master-to-slave topology Kathiravan Thirumoorthy
2026-10-07 7:05 ` sashiko-bot
2026-10-07 6:50 ` [PATCH 2/4] clk: qcom: clk-alpha-pll: Skip Zonda PLL configuration if already enabled Kathiravan Thirumoorthy
2026-10-09 18:23 ` Abel Vesa
2026-10-07 6:50 ` [PATCH 3/4] dt-bindings: clock: ipq9650-apss-clk: Add IPQ9650 apss clock controller Kathiravan Thirumoorthy
2026-10-07 6:50 ` Kathiravan Thirumoorthy [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261007-ipq9650_apss_clk-v1-4-fd4c2b3cbdea@oss.qualcomm.com \
--to=kathiravan.thirumoorthy@oss.qualcomm.com \
--cc=abelvesa@kernel.org \
--cc=andersson@kernel.org \
--cc=bmasney+clk@redhat.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=djakov@kernel.org \
--cc=jbrunet+clk@baylibre.com \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=robh@kernel.org \
--cc=sboyd@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox