Re: [PATCH 2/4] clk: qcom: apss-ipq5424: Add ipq5424 apss clock controller

Varadarajan Narayanan
2025-01-28T07:48:50Z

On Mon, Jan 27, 2025 at 03:01:26PM +0530, Sricharan R wrote:
> From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
>
> CPU on Qualcomm ipq5424 is clocked by huayra PLL with RCG support.
> Add support for the APSS PLL, RCG and clock enable for ipq5424.
> The PLL, RCG register space are clubbed. Hence adding new APSS driver
> for both PLL and RCG/CBC control. Also the L3 cache has a separate pll
> and needs to be scaled along with the CPU.
>
> Co-developed-by: Md Sadre Alam <quic_mdalam@quicinc.com>
> Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
> Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
> ---
> drivers/clk/qcom/Kconfig | 7 +
> drivers/clk/qcom/Makefile | 1 +
> drivers/clk/qcom/apss-ipq5424.c | 373 ++++++++++++++++++++++++++++++++
> 3 files changed, 381 insertions(+)
> create mode 100644 drivers/clk/qcom/apss-ipq5424.c
>
> diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
> index ef89d686cbc4..9a03257d67e0 100644
> --- a/drivers/clk/qcom/Kconfig
> +++ b/drivers/clk/qcom/Kconfig
> @@ -190,6 +190,13 @@ config IPQ_APSS_6018
> Say Y if you want to support CPU frequency scaling on
> ipq based devices.
>
> +config IPQ_APSS_5424
> + tristate "IPQ APSS Clock Controller"
> + help
> + Support for APSS Clock controller on Qualcom IPQ5424 platform.
> + Say Y if you want to support CPU frequency scaling on ipq based
> + devices.
> +
> config IPQ_GCC_4019
> tristate "IPQ4019 Global Clock Controller"
> help
> diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
> index b09dbdc210eb..db15514e7367 100644
> --- a/drivers/clk/qcom/Makefile
> +++ b/drivers/clk/qcom/Makefile
> @@ -28,6 +28,7 @@ obj-$(CONFIG_CLK_X1E80100_GPUCC) += gpucc-x1e80100.o
> obj-$(CONFIG_CLK_X1E80100_TCSRCC) += tcsrcc-x1e80100.o
> 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_GCC_4019) += gcc-ipq4019.o
> obj-$(CONFIG_IPQ_GCC_5018) += gcc-ipq5018.o
> diff --git a/drivers/clk/qcom/apss-ipq5424.c b/drivers/clk/qcom/apss-ipq5424.c
> new file mode 100644
> index 000000000000..2bd6ee7575dc
> --- /dev/null
> +++ b/drivers/clk/qcom/apss-ipq5424.c
> @@ -0,0 +1,373 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.

2025

> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#include <dt-bindings/clock/qcom,apss-ipq.h>
> +#include <dt-bindings/arm/qcom,ids.h>
> +
> +#include "clk-alpha-pll.h"
> +#include "clk-branch.h"
> +#include "clk-rcg.h"
> +#include "clk-regmap.h"
> +#include "common.h"
> +
> +#define GPLL0_CLK_RATE 800000000
> +#define CPU_NOM_CLK_RATE 1416000000
> +#define CPU_TURBO_CLK_RATE 1800000000
> +#define L3_NOM_CLK_RATE 984000000
> +#define L3_TURBO_CLK_RATE 1272000000
> +
> +enum {
> + P_XO,
> + P_GPLL0,
> + P_APSS_PLL_EARLY,
> + P_L3_PLL,
> +};
> +
> +struct apss_clk {
> + struct notifier_block cpu_clk_notifier;
> + struct clk_hw *hw;
> + struct device *dev;
> + struct clk *l3_clk;
> +};
> +
> +/*
> + * IPQ5424 Huayra PLL offsets are different from the one mentioned in the
> + * clk-alpha-pll.c, hence define the IPQ5424 offsets here
> + */

This seems to be same as clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_HUAYRA_2290]
in clk-alpha-pll.c. Please see if that can be used here.

> +static const u8 ipq5424_pll_offsets[][PLL_OFF_MAX_REGS] = {
> + [CLK_ALPHA_PLL_TYPE_HUAYRA] = {
> + [PLL_OFF_L_VAL] = 0x04,
> + [PLL_OFF_ALPHA_VAL] = 0x08,
> + [PLL_OFF_USER_CTL] = 0x0c,
> + [PLL_OFF_CONFIG_CTL] = 0x10,
> + [PLL_OFF_CONFIG_CTL_U] = 0x14,
> + [PLL_OFF_CONFIG_CTL_U1] = 0x18,
> + [PLL_OFF_TEST_CTL] = 0x1c,
> + [PLL_OFF_TEST_CTL_U] = 0x20,
> + [PLL_OFF_TEST_CTL_U1] = 0x24,
> + [PLL_OFF_STATUS] = 0x38,
> + },
> +};
> +
> +static struct clk_alpha_pll ipq5424_apss_pll = {
> + .offset = 0x0,
> + .regs = ipq5424_pll_offsets[CLK_ALPHA_PLL_TYPE_HUAYRA],
> + .flags = SUPPORTS_DYNAMIC_UPDATE,
> + .clkr = {
> + .enable_reg = 0x0,
> + .enable_mask = BIT(0),
> + .hw.init = &(struct clk_init_data){
> + .name = "apss_pll",
> + .parent_data = &(const struct clk_parent_data) {
> + .fw_name = "xo-board-clk",
> + },
> + .parent_names = (const char *[]){ "xo-board-clk"},
> + .num_parents = 1,
> + .ops = &clk_alpha_pll_huayra_ops,
> + },
> + },
> +};
> +
> +static const struct clk_parent_data parents_apss_silver_clk_src[] = {
> + { .fw_name = "xo-board-clk" },
> + { .fw_name = "gpll0" },
> + { .hw = &ipq5424_apss_pll.clkr.hw },
> +};
> +
> +static const struct parent_map parents_apss_silver_clk_src_map[] = {
> + { P_XO, 0 },
> + { P_GPLL0, 4 },
> + { P_APSS_PLL_EARLY, 5 },
> +};
> +
> +static const struct freq_tbl ftbl_apss_clk_src[] = {
> + F(GPLL0_CLK_RATE, P_GPLL0, 1, 0, 0),
> + F(CPU_NOM_CLK_RATE, P_APSS_PLL_EARLY, 1, 0, 0),
> + F(CPU_TURBO_CLK_RATE, P_APSS_PLL_EARLY, 1, 0, 0),
> + { }
> +};
> +
> +static struct clk_rcg2 apss_silver_clk_src = {
> + .cmd_rcgr = 0x0080,
> + .freq_tbl = ftbl_apss_clk_src,
> + .hid_width = 5,
> + .parent_map = parents_apss_silver_clk_src_map,
> + .clkr.hw.init = &(struct clk_init_data){
> + .name = "apss_silver_clk_src",
> + .parent_data = parents_apss_silver_clk_src,
> + .num_parents = ARRAY_SIZE(parents_apss_silver_clk_src),
> + .ops = &clk_rcg2_ops,
> + .flags = CLK_SET_RATE_PARENT,
> + },
> +};
> +
> +static struct clk_branch apss_silver_core_clk = {
> + .halt_reg = 0x008c,
> + .clkr = {
> + .enable_reg = 0x008c,
> + .enable_mask = BIT(0),
> + .hw.init = &(struct clk_init_data){
> + .name = "apss_silver_core_clk",
> + .parent_hws = (const struct clk_hw *[]){
> + &apss_silver_clk_src.clkr.hw },
> + .num_parents = 1,
> + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> + .ops = &clk_branch2_ops,
> + },
> + },
> +};
> +
> +static struct clk_alpha_pll ipq5424_l3_pll = {
> + .offset = 0x10000,
> + .regs = ipq5424_pll_offsets[CLK_ALPHA_PLL_TYPE_HUAYRA],
> + .flags = SUPPORTS_DYNAMIC_UPDATE,
> + .clkr = {
> + .enable_reg = 0x0,
> + .enable_mask = BIT(0),
> + .hw.init = &(struct clk_init_data){
> + .name = "l3_pll",
> + .parent_data = &(const struct clk_parent_data) {
> + .fw_name = "xo-board-clk",
> + },
> + .parent_names = (const char *[]){ "xo-board-clk"},
> + .num_parents = 1,
> + .ops = &clk_alpha_pll_huayra_ops,
> + },
> + },
> +};
> +
> +static const struct clk_parent_data parents_l3_clk_src[] = {
> + { .fw_name = "xo-board-clk" },
> + { .fw_name = "gpll0" },
> + { .hw = &ipq5424_l3_pll.clkr.hw },
> +};
> +
> +static const struct parent_map parents_l3_clk_src_map[] = {
> + { P_XO, 0 },
> + { P_GPLL0, 4 },
> + { P_L3_PLL, 5 },
> +};
> +
> +static const struct freq_tbl ftbl_l3_clk_src[] = {
> + F(GPLL0_CLK_RATE, P_GPLL0, 1, 0, 0),
> + F(L3_NOM_CLK_RATE, P_L3_PLL, 1, 0, 0),
> + F(L3_TURBO_CLK_RATE, P_L3_PLL, 1, 0, 0),
> + { }
> +};
> +
> +static struct clk_rcg2 l3_clk_src = {
> + .cmd_rcgr = 0x10080,
> + .freq_tbl = ftbl_l3_clk_src,
> + .hid_width = 5,
> + .parent_map = parents_l3_clk_src_map,
> + .clkr.hw.init = &(struct clk_init_data){
> + .name = "l3_clk_src",
> + .parent_data = parents_l3_clk_src,
> + .num_parents = ARRAY_SIZE(parents_l3_clk_src),
> + .ops = &clk_rcg2_ops,
> + .flags = CLK_SET_RATE_PARENT,
> + },
> +};
> +
> +static struct clk_branch l3_core_clk = {
> + .halt_reg = 0x1008c,
> + .clkr = {
> + .enable_reg = 0x1008c,
> + .enable_mask = BIT(0),
> + .hw.init = &(struct clk_init_data){
> + .name = "l3_clk",
> + .parent_hws = (const struct clk_hw *[]){
> + &l3_clk_src.clkr.hw },
> + .num_parents = 1,
> + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> + .ops = &clk_branch2_ops,
> + },
> + },
> +};
> +
> +static const struct regmap_config apss_ipq5424_regmap_config = {
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .max_register = 0x20000,
> + .fast_io = true,
> +};
> +
> +static struct clk_regmap *apss_ipq5424_clks[] = {
> + [APSS_PLL_EARLY] = &ipq5424_apss_pll.clkr,
> + [APSS_SILVER_CLK_SRC] = &apss_silver_clk_src.clkr,
> + [APSS_SILVER_CORE_CLK] = &apss_silver_core_clk.clkr,
> + [L3_PLL] = &ipq5424_l3_pll.clkr,
> + [L3_CLK_SRC] = &l3_clk_src.clkr,
> + [L3_CORE_CLK] = &l3_core_clk.clkr,
> +
> +};
> +
> +static const struct qcom_cc_desc apss_ipq5424_desc = {
> + .config = &apss_ipq5424_regmap_config,
> + .clks = apss_ipq5424_clks,
> + .num_clks = ARRAY_SIZE(apss_ipq5424_clks),
> +};
> +
> +static const struct alpha_pll_config apss_pll_config = {
> + .l = 0x3b,
> + .config_ctl_val = 0x08200920,
> + .config_ctl_hi_val = 0x05008001,
> + .config_ctl_hi1_val = 0x04000000,
> + .test_ctl_val = 0x0,
> + .test_ctl_hi_val = 0x0,
> + .test_ctl_hi1_val = 0x0,
> + .user_ctl_val = 0x1,
> + .early_output_mask = BIT(3),
> + .aux2_output_mask = BIT(2),
> + .aux_output_mask = BIT(1),
> + .main_output_mask = BIT(0),
> +};
> +
> +static const struct alpha_pll_config l3_pll_config = {
> + .l = 0x29,
> + .config_ctl_val = 0x08200920,
> + .config_ctl_hi_val = 0x05008001,
> + .config_ctl_hi1_val = 0x04000000,
> + .test_ctl_val = 0x0,
> + .test_ctl_hi_val = 0x0,
> + .test_ctl_hi1_val = 0x0,
> + .user_ctl_val = 0x1,
> + .early_output_mask = BIT(3),
> + .aux2_output_mask = BIT(2),
> + .aux_output_mask = BIT(1),
> + .main_output_mask = BIT(0),
> +};
> +
> +static unsigned long get_l3_clk_from_tbl(unsigned long rate)
> +{
> + struct clk_rcg2 *l3_rcg2 = container_of(&l3_clk_src.clkr, struct clk_rcg2, clkr);
> + u8 max_clk = sizeof(ftbl_apss_clk_src) / sizeof(struct freq_tbl);
> + u8 loop;
> +
> + for (loop = 0; loop < max_clk; loop++)
> + if (ftbl_apss_clk_src[loop].freq == rate)
> + return l3_rcg2->freq_tbl[loop].freq;
> + return 0;
> +}
> +
> +static int cpu_clk_notifier_fn(struct notifier_block *nb, unsigned long action,
> + void *data)
> +{
> + struct apss_clk *apss_ipq5424_cfg = container_of(nb, struct apss_clk, cpu_clk_notifier);
> + struct clk_notifier_data *cnd = (struct clk_notifier_data *)data;
> + struct device *dev = apss_ipq5424_cfg->dev;
> + unsigned long rate = 0, l3_rate;
> + int err = 0;

No need to init 'err' here.

> +
> + dev_dbg(dev, "action:%ld old_rate:%ld new_rate:%ld\n", action,
> + cnd->old_rate, cnd->new_rate);
> +
> + switch (action) {
> + case PRE_RATE_CHANGE:
> + if (cnd->old_rate < cnd->new_rate)
> + rate = cnd->new_rate;
> + break;
> + case POST_RATE_CHANGE:
> + if (cnd->old_rate > cnd->new_rate)
> + rate = cnd->new_rate;
> + break;
> + };
> +
> + if (!rate)
> + goto notif_ret;
> +
> + l3_rate = get_l3_clk_from_tbl(rate);
> + if (!l3_rate) {
> + dev_err(dev, "Failed to get l3 clock rate from l3_tbl\n");
> + return NOTIFY_BAD;
> + }
> +
> + err = clk_set_rate(apss_ipq5424_cfg->l3_clk, l3_rate);
> + if (err) {
> + dev_err(dev, "Failed to set l3 clock rate(%ld) err(%d)\n", l3_rate, err);
> + return NOTIFY_BAD;
> + }
> +
> +notif_ret:
> + return NOTIFY_OK;
> +}
> +
> +static int apss_ipq5424_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct apss_clk *apss_ipq5424_cfg;
> + struct regmap *regmap;
> + void __iomem *base;
> + int ret;
> +
> + apss_ipq5424_cfg = devm_kzalloc(&pdev->dev, sizeof(struct apss_clk), GFP_KERNEL);
> + if (IS_ERR_OR_NULL(apss_ipq5424_cfg))
> + return PTR_ERR(apss_ipq5424_cfg);
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + regmap = devm_regmap_init_mmio(dev, base, &apss_ipq5424_regmap_config);
> + if (!regmap)
> + return PTR_ERR(regmap);
> +
> + clk_alpha_pll_configure(&ipq5424_l3_pll, regmap, &l3_pll_config);
> +
> + clk_alpha_pll_configure(&ipq5424_apss_pll, regmap, &apss_pll_config);
> +
> + ret = qcom_cc_really_probe(dev, &apss_ipq5424_desc, regmap);
> + if (ret)
> + return ret;
> +
> + dev_dbg(&pdev->dev, "Registered APSS & L3 clock provider\n");
> +
> + apss_ipq5424_cfg->dev = dev;
> + apss_ipq5424_cfg->hw = &apss_silver_clk_src.clkr.hw;
> + apss_ipq5424_cfg->cpu_clk_notifier.notifier_call = cpu_clk_notifier_fn;
> +
> + apss_ipq5424_cfg->l3_clk = clk_hw_get_clk(&l3_core_clk.clkr.hw, "l3_clk");
> + if (IS_ERR(apss_ipq5424_cfg->l3_clk)) {
> + dev_err(&pdev->dev, "Failed to get L3 clk, %ld\n",
> + PTR_ERR(apss_ipq5424_cfg->l3_clk));
> + return PTR_ERR(apss_ipq5424_cfg->l3_clk);
> + }
> +
> + ret = devm_clk_notifier_register(&pdev->dev, apss_ipq5424_cfg->hw->clk,
> + &apss_ipq5424_cfg->cpu_clk_notifier);

Use return devm_clk_notifier_register(...) and below lines can be skipped.

Thanks
Varada

> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static const struct of_device_id apss_ipq5424_match_table[] = {
> + { .compatible = "qcom,ipq5424-apss-clk" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, apss_ipq5424_match_table);
> +
> +static struct platform_driver apss_ipq5424_driver = {
> + .probe = apss_ipq5424_probe,
> + .driver = {
> + .name = "apss-ipq5424-clk",
> + .of_match_table = apss_ipq5424_match_table,
> + },
> +};
> +
> +module_platform_driver(apss_ipq5424_driver);
> +
> +MODULE_DESCRIPTION("QCOM APSS IPQ5424 CLK Driver");
> +MODULE_LICENSE("GPL v2");
>
> --
> 2.34.1
>