From bd691136639963b61c028e55d5889997430e7fa7 Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Fri, 10 Jan 2025 16:26:21 +0200 Subject: [PATCH 01/11] feat(nxp-clk): add a basic get_rate implementation Replace the dummy implementation of clk_ops.get_rate with a basic version that only handles the oscillator objects. Subsequent commits will add more objects to this list. Change-Id: I8c1bbbfa6b116fdcf5a1f1353bdb52b474bac831 Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 78 +++++++++++++++++-- drivers/nxp/clk/s32cc/s32cc_clk_modules.c | 4 +- .../drivers/nxp/clk/s32cc/s32cc-clk-modules.h | 18 +++-- 3 files changed, 85 insertions(+), 15 deletions(-) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index 235b9889a..810162b0b 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 NXP + * Copyright 2024-2025 NXP * * SPDX-License-Identifier: BSD-3-Clause */ @@ -1059,11 +1059,6 @@ static bool s32cc_clk_is_enabled(unsigned long id) return false; } -static unsigned long s32cc_clk_get_rate(unsigned long id) -{ - return 0; -} - static int set_module_rate(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth); @@ -1091,6 +1086,29 @@ static int set_osc_freq(const struct s32cc_clk_obj *module, unsigned long rate, return 0; } +static int get_osc_freq(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, unsigned int depth) +{ + const struct s32cc_osc *osc = s32cc_obj2osc(module); + unsigned int ldepth = depth; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + if (osc->freq == 0UL) { + ERROR("Uninitialized oscillator\n"); + return -EINVAL; + } + + *rate = osc->freq; + + return 0; +} + static int set_clk_freq(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth) { @@ -1319,6 +1337,31 @@ static int set_module_rate(const struct s32cc_clk_obj *module, return ret; } +static int get_module_rate(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, + unsigned int depth) +{ + unsigned int ldepth = depth; + int ret = 0; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + switch (module->type) { + case s32cc_osc_t: + ret = get_osc_freq(module, drv, rate, ldepth); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static int s32cc_clk_set_rate(unsigned long id, unsigned long rate, unsigned long *orate) { @@ -1340,6 +1383,29 @@ static int s32cc_clk_set_rate(unsigned long id, unsigned long rate, return ret; } +static unsigned long s32cc_clk_get_rate(unsigned long id) +{ + const struct s32cc_clk_drv *drv = get_drv(); + unsigned int depth = MAX_STACK_DEPTH; + const struct s32cc_clk *clk; + unsigned long rate = 0UL; + int ret; + + clk = s32cc_get_arch_clk(id); + if (clk == NULL) { + return 0; + } + + ret = get_module_rate(&clk->desc, drv, &rate, depth); + if (ret != 0) { + ERROR("Failed to get frequency (%lu MHz) for clock %lu\n", + rate, id); + return 0; + } + + return rate; +} + static struct s32cc_clk_obj *get_no_parent(const struct s32cc_clk_obj *module) { return NULL; diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_modules.c b/drivers/nxp/clk/s32cc/s32cc_clk_modules.c index 71055abc0..f7af465dd 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_modules.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_modules.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 NXP + * Copyright 2020-2025 NXP * * SPDX-License-Identifier: BSD-3-Clause */ @@ -20,7 +20,7 @@ static struct s32cc_clk fxosc_clk = S32CC_MODULE_CLK(fxosc); static struct s32cc_osc firc = - S32CC_OSC_INIT(S32CC_FIRC); + S32CC_OSC_INIT_FREQ(S32CC_FIRC, 48 * MHZ); static struct s32cc_clk firc_clk = S32CC_MODULE_CLK(firc); diff --git a/include/drivers/nxp/clk/s32cc/s32cc-clk-modules.h b/include/drivers/nxp/clk/s32cc/s32cc-clk-modules.h index 4837f795b..c91f3b647 100644 --- a/include/drivers/nxp/clk/s32cc/s32cc-clk-modules.h +++ b/include/drivers/nxp/clk/s32cc/s32cc-clk-modules.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause */ /* - * Copyright 2020-2024 NXP + * Copyright 2020-2025 NXP */ #ifndef S32CC_CLK_MODULES_H #define S32CC_CLK_MODULES_H @@ -52,14 +52,18 @@ struct s32cc_osc { void *base; }; -#define S32CC_OSC_INIT(SOURCE) \ -{ \ - .desc = { \ - .type = s32cc_osc_t, \ - }, \ - .source = (SOURCE), \ +#define S32CC_OSC_INIT_FREQ(SOURCE, FREQ) \ +{ \ + .desc = { \ + .type = s32cc_osc_t, \ + }, \ + .source = (SOURCE), \ + .freq = (FREQ), \ } +#define S32CC_OSC_INIT(SOURCE) \ + S32CC_OSC_INIT_FREQ(SOURCE, 0) + struct s32cc_clkmux { struct s32cc_clk_obj desc; enum s32cc_clk_source module; From 46de0b9c992fd4da90075b39ccff0a849a976301 Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Fri, 10 Jan 2025 16:33:36 +0200 Subject: [PATCH 02/11] feat(nxp-clk): add get_rate for s32cc_clk Add the option to obtain the rate of an s32cc_clk object. s32cc_clk are usually links to either another s32cc_clk or a different clock module. Therefore, this function routes the request. Change-Id: I0c1174cb861d2062882319e46cb6ca97bad70aab Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index 810162b0b..ffbc3ac0e 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -1062,6 +1062,10 @@ static bool s32cc_clk_is_enabled(unsigned long id) static int set_module_rate(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth); +static int get_module_rate(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, + unsigned int depth); static int set_osc_freq(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth) @@ -1138,6 +1142,36 @@ static int set_clk_freq(const struct s32cc_clk_obj *module, unsigned long rate, return -EINVAL; } +static int get_clk_freq(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, unsigned long *rate, + unsigned int depth) +{ + const struct s32cc_clk *clk = s32cc_obj2clk(module); + unsigned int ldepth = depth; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + if (clk == NULL) { + ERROR("Invalid clock\n"); + return -EINVAL; + } + + if (clk->module != NULL) { + return get_module_rate(clk->module, drv, rate, ldepth); + } + + if (clk->pclock == NULL) { + ERROR("Invalid clock parent\n"); + return -EINVAL; + } + + return get_clk_freq(&clk->pclock->desc, drv, rate, ldepth); +} + static int set_pll_freq(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth) { @@ -1354,6 +1388,9 @@ static int get_module_rate(const struct s32cc_clk_obj *module, case s32cc_osc_t: ret = get_osc_freq(module, drv, rate, ldepth); break; + case s32cc_clk_t: + ret = get_clk_freq(module, drv, rate, ldepth); + break; default: ret = -EINVAL; break; From fbebafa518d5cbc69d0c64023f002ff6706019f2 Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Mon, 13 Jan 2025 09:33:42 +0200 Subject: [PATCH 03/11] feat(nxp-clk): add get_rate for s32cc_pll Add the option to obtain the rate of an s32cc_pll object. The rate of the PLL can be obtained regardless of its hardware state. The targeted frequency is returned in case the PLL is off. Otherwise, the frequency is determined based on settings found in its registers. Change-Id: Id200d0eff149109a724eee69b063bf750d5cba2e Signed-off-by: Ghennadi Procopciuc --- .../nxp/clk/s32cc/include/s32cc-clk-regs.h | 4 +- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 79 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/drivers/nxp/clk/s32cc/include/s32cc-clk-regs.h b/drivers/nxp/clk/s32cc/include/s32cc-clk-regs.h index e54d58130..665930b2e 100644 --- a/drivers/nxp/clk/s32cc/include/s32cc-clk-regs.h +++ b/drivers/nxp/clk/s32cc/include/s32cc-clk-regs.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause /* - * Copyright 2020-2021, 2023-2024 NXP + * Copyright 2020-2021, 2023-2025 NXP */ #ifndef S32CC_CLK_REGS_H #define S32CC_CLK_REGS_H @@ -48,6 +48,8 @@ #define PLLDIG_PLLDV_RDIV_MASK GENMASK_32(14U, PLLDIG_PLLDV_RDIV_OFFSET) #define PLLDIG_PLLDV_RDIV_SET(VAL) (PLLDIG_PLLDV_RDIV_MASK & \ ((VAL) << PLLDIG_PLLDV_RDIV_OFFSET)) +#define PLLDIG_PLLDV_RDIV(VAL) (((VAL) & PLLDIG_PLLDV_RDIV_MASK) >> \ + PLLDIG_PLLDV_RDIV_OFFSET) #define PLLDIG_PLLDV_MFI_MASK GENMASK_32(7U, 0U) #define PLLDIG_PLLDV_MFI(DIV) (PLLDIG_PLLDV_MFI_MASK & (DIV)) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index ffbc3ac0e..95298435d 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -1194,6 +1194,82 @@ static int set_pll_freq(const struct s32cc_clk_obj *module, unsigned long rate, return 0; } +static int get_pll_freq(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, unsigned int depth) +{ + const struct s32cc_pll *pll = s32cc_obj2pll(module); + const struct s32cc_clk *source; + uint32_t mfi, mfn, rdiv, plldv; + unsigned long prate, clk_src; + unsigned int ldepth = depth; + uintptr_t pll_addr = 0UL; + uint64_t t1, t2; + uint32_t pllpd; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + ret = get_base_addr(pll->instance, drv, &pll_addr); + if (ret != 0) { + ERROR("Failed to detect PLL instance\n"); + return ret; + } + + /* Disabled PLL */ + pllpd = mmio_read_32(PLLDIG_PLLCR(pll_addr)) & PLLDIG_PLLCR_PLLPD; + if (pllpd != 0U) { + *rate = pll->vco_freq; + return 0; + } + + clk_src = mmio_read_32(PLLDIG_PLLCLKMUX(pll_addr)); + switch (clk_src) { + case 0: + clk_src = S32CC_CLK_FIRC; + break; + case 1: + clk_src = S32CC_CLK_FXOSC; + break; + default: + ERROR("Failed to identify PLL source id %" PRIu64 "\n", clk_src); + return -EINVAL; + }; + + source = s32cc_get_arch_clk(clk_src); + if (source == NULL) { + ERROR("Failed to get PLL source clock\n"); + return -EINVAL; + } + + ret = get_module_rate(&source->desc, drv, &prate, ldepth); + if (ret != 0) { + ERROR("Failed to get PLL's parent frequency\n"); + return ret; + } + + plldv = mmio_read_32(PLLDIG_PLLDV(pll_addr)); + mfi = PLLDIG_PLLDV_MFI(plldv); + rdiv = PLLDIG_PLLDV_RDIV(plldv); + if (rdiv == 0U) { + rdiv = 1; + } + + /* Frac-N mode */ + mfn = PLLDIG_PLLFD_MFN_SET(mmio_read_32(PLLDIG_PLLFD(pll_addr))); + + /* PLL VCO frequency in Fractional mode when PLLDV[RDIV] is not 0 */ + t1 = prate / rdiv; + t2 = (mfi * FP_PRECISION) + (mfn * FP_PRECISION / 18432U); + + *rate = t1 * t2 / FP_PRECISION; + + return 0; +} + static int set_pll_div_freq(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth) { @@ -1391,6 +1467,9 @@ static int get_module_rate(const struct s32cc_clk_obj *module, case s32cc_clk_t: ret = get_clk_freq(module, drv, rate, ldepth); break; + case s32cc_pll_t: + ret = get_pll_freq(module, drv, rate, ldepth); + break; default: ret = -EINVAL; break; From 2fb25509b800726342955194a0c6ac24299fb08e Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Mon, 13 Jan 2025 09:50:51 +0200 Subject: [PATCH 04/11] feat(nxp-clk): add get_rate for s32cc_dfs Add the option to obtain the rate of an s32cc_dfs object. The DFS rate depends on the module to which it's connected. Therefore, it will always return the rate of its parent. Change-Id: Ie3becd36721f541d0fab11b2fb57aacd66d48220 Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 42 ++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index 95298435d..3b945dd4b 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -33,6 +33,14 @@ struct s32cc_clk_drv { uintptr_t rdc; }; +static int set_module_rate(const struct s32cc_clk_obj *module, + unsigned long rate, unsigned long *orate, + unsigned int *depth); +static int get_module_rate(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, + unsigned int depth); + static int update_stack_depth(unsigned int *depth) { if (*depth == 0U) { @@ -651,6 +659,29 @@ static int enable_dfs(struct s32cc_clk_obj *module, return 0; } +static int get_dfs_freq(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, unsigned int depth) +{ + const struct s32cc_dfs *dfs = s32cc_obj2dfs(module); + unsigned int ldepth = depth; + uintptr_t dfs_addr; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + ret = get_base_addr(dfs->instance, drv, &dfs_addr); + if (ret != 0) { + ERROR("Failed to detect the DFS instance\n"); + return ret; + } + + return get_module_rate(dfs->parent, drv, rate, ldepth); +} + static struct s32cc_dfs *get_div_dfs(const struct s32cc_dfs_div *dfs_div) { const struct s32cc_clk_obj *parent = dfs_div->parent; @@ -1059,14 +1090,6 @@ static bool s32cc_clk_is_enabled(unsigned long id) return false; } -static int set_module_rate(const struct s32cc_clk_obj *module, - unsigned long rate, unsigned long *orate, - unsigned int *depth); -static int get_module_rate(const struct s32cc_clk_obj *module, - const struct s32cc_clk_drv *drv, - unsigned long *rate, - unsigned int depth); - static int set_osc_freq(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth) { @@ -1470,6 +1493,9 @@ static int get_module_rate(const struct s32cc_clk_obj *module, case s32cc_pll_t: ret = get_pll_freq(module, drv, rate, ldepth); break; + case s32cc_dfs_t: + ret = get_dfs_freq(module, drv, rate, ldepth); + break; default: ret = -EINVAL; break; From 8f23e76fa5886ef9adbd867a546f291200fc2142 Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Mon, 13 Jan 2025 10:47:26 +0200 Subject: [PATCH 05/11] feat(nxp-clk): add get_rate for s32cc_dfs_div Add the option to obtain the rate of an s32cc_dfs_div object. As in the case of the PLL, the output divider of a DFS will return its targeted frequency if the module is disabled and calculate the rate based on the settings found in its registers if the module is turned on. Change-Id: Id6db92dbdf03f8119875476ad8f7aa268ff6ea93 Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 68 +++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index 3b945dd4b..b77eb2175 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -1422,6 +1422,71 @@ static int set_dfs_div_freq(const struct s32cc_clk_obj *module, unsigned long ra return ret; } +static unsigned long compute_dfs_div_freq(unsigned long pfreq, uint32_t mfi, uint32_t mfn) +{ + unsigned long freq; + + /** + * Formula for input and output clocks of each port divider. + * See 'Digital Frequency Synthesizer' chapter from Reference Manual. + * + * freq = pfreq / (2 * (mfi + mfn / 36.0)); + */ + freq = (mfi * FP_PRECISION) + (mfn * FP_PRECISION / 36UL); + freq *= 2UL; + freq = pfreq * FP_PRECISION / freq; + + return freq; +} + +static int get_dfs_div_freq(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, unsigned int depth) +{ + const struct s32cc_dfs_div *dfs_div = s32cc_obj2dfsdiv(module); + unsigned int ldepth = depth; + const struct s32cc_dfs *dfs; + uint32_t dvport, mfi, mfn; + uintptr_t dfs_addr = 0UL; + unsigned long pfreq; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + dfs = get_div_dfs(dfs_div); + if (dfs == NULL) { + return -EINVAL; + } + + ret = get_module_rate(dfs_div->parent, drv, &pfreq, ldepth); + if (ret != 0) { + return ret; + } + + ret = get_base_addr(dfs->instance, drv, &dfs_addr); + if (ret != 0) { + ERROR("Failed to detect the DFS instance\n"); + return ret; + } + + dvport = mmio_read_32(DFS_DVPORTn(dfs_addr, dfs_div->index)); + + mfi = DFS_DVPORTn_MFI(dvport); + mfn = DFS_DVPORTn_MFN(dvport); + + /* Disabled port */ + if ((mfi == 0U) && (mfn == 0U)) { + *rate = dfs_div->freq; + return 0; + } + + *rate = compute_dfs_div_freq(pfreq, mfi, mfn); + return 0; +} + static int set_module_rate(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth) @@ -1496,6 +1561,9 @@ static int get_module_rate(const struct s32cc_clk_obj *module, case s32cc_dfs_t: ret = get_dfs_freq(module, drv, rate, ldepth); break; + case s32cc_dfs_div_t: + ret = get_dfs_div_freq(module, drv, rate, ldepth); + break; default: ret = -EINVAL; break; From 7c298ebcbf1003b98f815b86b2014112e89644d3 Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Mon, 13 Jan 2025 10:56:04 +0200 Subject: [PATCH 06/11] feat(nxp-clk): add get_rate for s32cc_fixed_div The get rate callback is needed for s32cc_fixed_div to allow the frequency compilation for modules attached to a fixed divider like LINFLEXD_CLK. Change-Id: Ibc3e52f7f1127bba0dd793be0a26bdff15260824 Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index b77eb2175..d41fcb3ca 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -1365,6 +1365,23 @@ static int set_fixed_div_freq(const struct s32cc_clk_obj *module, unsigned long return ret; } +static int get_fixed_div_freq(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, unsigned int depth) +{ + const struct s32cc_fixed_div *fdiv = s32cc_obj2fixeddiv(module); + unsigned long pfreq; + int ret; + + ret = get_module_rate(fdiv->parent, drv, &pfreq, depth); + if (ret != 0) { + return ret; + } + + *rate = (pfreq * FP_PRECISION / fdiv->rate_div) / FP_PRECISION; + return 0; +} + static int set_mux_freq(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth) { @@ -1564,6 +1581,9 @@ static int get_module_rate(const struct s32cc_clk_obj *module, case s32cc_dfs_div_t: ret = get_dfs_div_freq(module, drv, rate, ldepth); break; + case s32cc_fixed_div_t: + ret = get_fixed_div_freq(module, drv, rate, ldepth); + break; default: ret = -EINVAL; break; From a762c50579cb3bcae9c266e652c7c959e66fa943 Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Mon, 13 Jan 2025 11:38:34 +0200 Subject: [PATCH 07/11] feat(nxp-clk): add get_rate for s32cc_pll_out_div The get rate callback is needed for s32cc_pll_out_div to get the A53 cores and DDR rate. Change-Id: Ife7860c9941e819b612d7948dac9843bdf0c31c4 Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index d41fcb3ca..7a199bc7e 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -1341,6 +1341,57 @@ static int set_pll_div_freq(const struct s32cc_clk_obj *module, unsigned long ra return 0; } +static int get_pll_div_freq(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, unsigned int depth) +{ + const struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module); + const struct s32cc_pll *pll; + unsigned int ldepth = depth; + uintptr_t pll_addr = 0UL; + unsigned long pfreq; + uint32_t pllodiv; + uint32_t dc; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + pll = get_div_pll(pdiv); + if (pll == NULL) { + ERROR("The parent of the PLL DIV is invalid\n"); + return -EINVAL; + } + + ret = get_base_addr(pll->instance, drv, &pll_addr); + if (ret != 0) { + ERROR("Failed to detect PLL instance\n"); + return -EINVAL; + } + + ret = get_module_rate(pdiv->parent, drv, &pfreq, ldepth); + if (ret != 0) { + ERROR("Failed to get the frequency of PLL %" PRIxPTR "\n", + pll_addr); + return ret; + } + + pllodiv = mmio_read_32(PLLDIG_PLLODIV(pll_addr, pdiv->index)); + + /* Disabled module */ + if ((pllodiv & PLLDIG_PLLODIV_DE) == 0U) { + *rate = pdiv->freq; + return 0; + } + + dc = PLLDIG_PLLODIV_DIV(pllodiv); + *rate = (pfreq * FP_PRECISION) / (dc + 1U) / FP_PRECISION; + + return 0; +} + static int set_fixed_div_freq(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth) { @@ -1584,6 +1635,9 @@ static int get_module_rate(const struct s32cc_clk_obj *module, case s32cc_fixed_div_t: ret = get_fixed_div_freq(module, drv, rate, ldepth); break; + case s32cc_pll_out_div_t: + ret = get_pll_div_freq(module, drv, rate, ldepth); + break; default: ret = -EINVAL; break; From d1567da68d954be8f454ed641cbf7a08ca86f0bd Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Mon, 13 Jan 2025 11:49:55 +0200 Subject: [PATCH 08/11] feat(nxp-clk): add get_rate for clock muxes From the get rate callback perspective, all types of clock muxes should return the frequency of the selected source, regardless of whether it is an MC_CGM or PLL mux. Change-Id: I24ae821013b0844e4d62793fde12b53b043a9776 Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index 7a199bc7e..5d0a304c3 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -1454,6 +1454,29 @@ static int set_mux_freq(const struct s32cc_clk_obj *module, unsigned long rate, return set_module_rate(&clk->desc, rate, orate, depth); } +static int get_mux_freq(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, unsigned int depth) +{ + const struct s32cc_clkmux *mux = s32cc_obj2clkmux(module); + const struct s32cc_clk *clk = s32cc_get_arch_clk(mux->source_id); + unsigned int ldepth = depth; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + if (clk == NULL) { + ERROR("Mux (id:%" PRIu8 ") without a valid source (%lu)\n", + mux->index, mux->source_id); + return -EINVAL; + } + + return get_clk_freq(&clk->desc, drv, rate, ldepth); +} + static int set_dfs_div_freq(const struct s32cc_clk_obj *module, unsigned long rate, unsigned long *orate, unsigned int *depth) { @@ -1638,6 +1661,12 @@ static int get_module_rate(const struct s32cc_clk_obj *module, case s32cc_pll_out_div_t: ret = get_pll_div_freq(module, drv, rate, ldepth); break; + case s32cc_clkmux_t: + ret = get_mux_freq(module, drv, rate, ldepth); + break; + case s32cc_shared_clkmux_t: + ret = get_mux_freq(module, drv, rate, ldepth); + break; default: ret = -EINVAL; break; From a74cf75f084e62888f57f7718f614bcd6e5eb50f Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Mon, 13 Jan 2025 12:00:50 +0200 Subject: [PATCH 09/11] feat(nxp-clk): add get_rate for partition objects The partition-related objects do not participate in clock rate calculation, except the s32cc_part_block_link_t, whose call is forwarded to the parent object. Change-Id: Id9e7fa49c3c1fb5b30b4c1b97fc8441bc967578a Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index 5d0a304c3..17def0108 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -958,6 +958,22 @@ get_part_block_link_parent(const struct s32cc_clk_obj *module) return link->parent; } +static int get_part_block_link_freq(const struct s32cc_clk_obj *module, + const struct s32cc_clk_drv *drv, + unsigned long *rate, unsigned int depth) +{ + const struct s32cc_part_block_link *block = s32cc_obj2partblocklink(module); + unsigned int ldepth = depth; + int ret; + + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + + return get_module_rate(block->parent, drv, rate, ldepth); +} + static int no_enable(struct s32cc_clk_obj *module, const struct s32cc_clk_drv *drv, unsigned int depth) @@ -1667,6 +1683,15 @@ static int get_module_rate(const struct s32cc_clk_obj *module, case s32cc_shared_clkmux_t: ret = get_mux_freq(module, drv, rate, ldepth); break; + case s32cc_part_t: + ERROR("s32cc_part_t cannot be used to get rate\n"); + break; + case s32cc_part_block_t: + ERROR("s32cc_part_block_t cannot be used to get rate\n"); + break; + case s32cc_part_block_link_t: + ret = get_part_block_link_freq(module, drv, rate, ldepth); + break; default: ret = -EINVAL; break; From 43b4b29fb996ee05d2ca98c7f824d6a003342215 Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Wed, 15 Jan 2025 14:57:58 +0200 Subject: [PATCH 10/11] feat(nxp-clk): get pll rate using get_module_rate The DFS can use the get_module_rate instead of assuming its parent object is a PLL. It also has the advantage that the frequency will be returned based on the hardware state of the PLL module. Change-Id: I3a270cbc92622ae82606382df1301597dc29782a Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 33 ++++++--------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index 17def0108..a7320486d 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -694,24 +694,6 @@ static struct s32cc_dfs *get_div_dfs(const struct s32cc_dfs_div *dfs_div) return s32cc_obj2dfs(parent); } -static struct s32cc_pll *dfsdiv2pll(const struct s32cc_dfs_div *dfs_div) -{ - const struct s32cc_clk_obj *parent; - const struct s32cc_dfs *dfs; - - dfs = get_div_dfs(dfs_div); - if (dfs == NULL) { - return NULL; - } - - parent = dfs->parent; - if (parent->type != s32cc_pll_t) { - return NULL; - } - - return s32cc_obj2pll(parent); -} - static int get_dfs_mfi_mfn(unsigned long dfs_freq, const struct s32cc_dfs_div *dfs_div, uint32_t *mfi, uint32_t *mfn) { @@ -839,9 +821,9 @@ static int enable_dfs_div(struct s32cc_clk_obj *module, { const struct s32cc_dfs_div *dfs_div = s32cc_obj2dfsdiv(module); unsigned int ldepth = depth; - const struct s32cc_pll *pll; const struct s32cc_dfs *dfs; uintptr_t dfs_addr = 0UL; + unsigned long dfs_freq; uint32_t mfi, mfn; int ret = 0; @@ -855,18 +837,17 @@ static int enable_dfs_div(struct s32cc_clk_obj *module, return -EINVAL; } - pll = dfsdiv2pll(dfs_div); - if (pll == NULL) { - ERROR("Failed to identify DFS divider's parent\n"); - return -EINVAL; - } - ret = get_base_addr(dfs->instance, drv, &dfs_addr); if ((ret != 0) || (dfs_addr == 0UL)) { return -EINVAL; } - ret = get_dfs_mfi_mfn(pll->vco_freq, dfs_div, &mfi, &mfn); + ret = get_module_rate(&dfs->desc, drv, &dfs_freq, depth); + if (ret != 0) { + return ret; + } + + ret = get_dfs_mfi_mfn(dfs_freq, dfs_div, &mfi, &mfn); if (ret != 0) { return -EINVAL; } From c23dde6c193d26fae9b2a8e18140b90faeba3661 Mon Sep 17 00:00:00 2001 From: Ghennadi Procopciuc Date: Wed, 15 Jan 2025 14:59:22 +0200 Subject: [PATCH 11/11] feat(nxp-clk): restore pll output dividers rate Reconfiguration of the PLL may be requested while some output dividers are already enabled. To prevent setting a different frequency for these enabled dividers, the driver will attempt to adjust the division factor to achieve the initially requested rate. Change-Id: I7800c05b2f21bbdeda243db865942b647983687d Signed-off-by: Ghennadi Procopciuc --- drivers/nxp/clk/s32cc/s32cc_clk_drv.c | 144 ++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 8 deletions(-) diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c index a7320486d..c235e046f 100644 --- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c +++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c @@ -281,6 +281,70 @@ static void enable_odiv(uintptr_t pll_addr, uint32_t div_index) mmio_setbits_32(PLLDIG_PLLODIV(pll_addr, div_index), PLLDIG_PLLODIV_DE); } +static void enable_odivs(uintptr_t pll_addr, uint32_t ndivs, uint32_t mask) +{ + uint32_t i; + + for (i = 0; i < ndivs; i++) { + if ((mask & BIT_32(i)) != 0U) { + enable_odiv(pll_addr, i); + } + } +} + +static int adjust_odiv_settings(const struct s32cc_pll *pll, uintptr_t pll_addr, + uint32_t odivs_mask, unsigned long old_vco) +{ + uint64_t old_odiv_freq, odiv_freq; + uint32_t i, pllodiv, pdiv; + int ret = 0; + + if (old_vco == 0UL) { + return 0; + } + + for (i = 0; i < pll->ndividers; i++) { + if ((odivs_mask & BIT_32(i)) == 0U) { + continue; + } + + pllodiv = mmio_read_32(PLLDIG_PLLODIV(pll_addr, i)); + + pdiv = PLLDIG_PLLODIV_DIV(pllodiv); + + old_odiv_freq = ((old_vco * FP_PRECISION) / (pdiv + 1U)) / FP_PRECISION; + pdiv = (uint32_t)(pll->vco_freq * FP_PRECISION / old_odiv_freq / FP_PRECISION); + + odiv_freq = pll->vco_freq * FP_PRECISION / pdiv / FP_PRECISION; + + if (old_odiv_freq != odiv_freq) { + ERROR("Failed to adjust ODIV %" PRIu32 " to match previous frequency\n", + i); + } + + pllodiv = PLLDIG_PLLODIV_DIV_SET(pdiv - 1U); + mmio_write_32(PLLDIG_PLLODIV(pll_addr, i), pllodiv); + } + + return ret; +} + +static uint32_t get_enabled_odivs(uintptr_t pll_addr, uint32_t ndivs) +{ + uint32_t mask = 0; + uint32_t pllodiv; + uint32_t i; + + for (i = 0; i < ndivs; i++) { + pllodiv = mmio_read_32(PLLDIG_PLLODIV(pll_addr, i)); + if ((pllodiv & PLLDIG_PLLODIV_DE) != 0U) { + mask |= BIT_32(i); + } + } + + return mask; +} + static void disable_odivs(uintptr_t pll_addr, uint32_t ndivs) { uint32_t i; @@ -305,18 +369,54 @@ static void disable_pll_hw(uintptr_t pll_addr) mmio_write_32(PLLDIG_PLLCR(pll_addr), PLLDIG_PLLCR_PLLPD); } +static bool is_pll_enabled(uintptr_t pll_base) +{ + uint32_t pllcr, pllsr; + + pllcr = mmio_read_32(PLLDIG_PLLCR(pll_base)); + pllsr = mmio_read_32(PLLDIG_PLLSR(pll_base)); + + /* Enabled and locked PLL */ + if ((pllcr & PLLDIG_PLLCR_PLLPD) != 0U) { + return false; + } + + if ((pllsr & PLLDIG_PLLSR_LOCK) == 0U) { + return false; + } + + return true; +} + static int program_pll(const struct s32cc_pll *pll, uintptr_t pll_addr, const struct s32cc_clk_drv *drv, uint32_t sclk_id, - unsigned long sclk_freq) + unsigned long sclk_freq, unsigned int depth) { uint32_t rdiv = 1, mfi, mfn; + unsigned long old_vco = 0UL; + unsigned int ldepth = depth; + uint32_t odivs_mask; int ret; + ret = update_stack_depth(&ldepth); + if (ret != 0) { + return ret; + } + ret = get_pll_mfi_mfn(pll->vco_freq, sclk_freq, &mfi, &mfn); if (ret != 0) { return -EINVAL; } + odivs_mask = get_enabled_odivs(pll_addr, pll->ndividers); + + if (is_pll_enabled(pll_addr)) { + ret = get_module_rate(&pll->desc, drv, &old_vco, ldepth); + if (ret != 0) { + return ret; + } + } + /* Disable ODIVs*/ disable_odivs(pll_addr, pll->ndividers); @@ -334,8 +434,16 @@ static int program_pll(const struct s32cc_pll *pll, uintptr_t pll_addr, mmio_write_32(PLLDIG_PLLFD(pll_addr), PLLDIG_PLLFD_MFN_SET(mfn) | PLLDIG_PLLFD_SMDEN); + ret = adjust_odiv_settings(pll, pll_addr, odivs_mask, old_vco); + if (ret != 0) { + return ret; + } + enable_pll_hw(pll_addr); + /* Enable out dividers */ + enable_odivs(pll_addr, pll->ndividers, odivs_mask); + return ret; } @@ -344,10 +452,11 @@ static int enable_pll(struct s32cc_clk_obj *module, unsigned int depth) { const struct s32cc_pll *pll = s32cc_obj2pll(module); + unsigned int clk_src, ldepth = depth; + unsigned long sclk_freq, pll_vco; const struct s32cc_clkmux *mux; uintptr_t pll_addr = UL(0x0); - unsigned int ldepth = depth; - unsigned long sclk_freq; + bool pll_enabled; uint32_t sclk_id; int ret; @@ -387,7 +496,20 @@ static int enable_pll(struct s32cc_clk_obj *module, return -EINVAL; }; - return program_pll(pll, pll_addr, drv, sclk_id, sclk_freq); + ret = get_module_rate(&pll->desc, drv, &pll_vco, depth); + if (ret != 0) { + return ret; + } + + pll_enabled = is_pll_enabled(pll_addr); + clk_src = mmio_read_32(PLLDIG_PLLCLKMUX(pll_addr)); + + if ((clk_src == sclk_id) && pll_enabled && + (pll_vco == pll->vco_freq)) { + return 0; + } + + return program_pll(pll, pll_addr, drv, sclk_id, sclk_freq, ldepth); } static inline struct s32cc_pll *get_div_pll(const struct s32cc_pll_out_div *pdiv) @@ -449,6 +571,7 @@ static int enable_pll_div(struct s32cc_clk_obj *module, uintptr_t pll_addr = 0x0ULL; unsigned int ldepth = depth; const struct s32cc_pll *pll; + unsigned long pll_vco; uint32_t dc; int ret; @@ -469,7 +592,14 @@ static int enable_pll_div(struct s32cc_clk_obj *module, return -EINVAL; } - dc = (uint32_t)(pll->vco_freq / pdiv->freq); + ret = get_module_rate(&pll->desc, drv, &pll_vco, ldepth); + if (ret != 0) { + ERROR("Failed to enable the PLL due to unknown rate for 0x%" PRIxPTR "\n", + pll_addr); + return ret; + } + + dc = (uint32_t)(pll_vco / pdiv->freq); config_pll_out_div(pll_addr, pdiv->index, dc); @@ -1225,7 +1355,6 @@ static int get_pll_freq(const struct s32cc_clk_obj *module, unsigned int ldepth = depth; uintptr_t pll_addr = 0UL; uint64_t t1, t2; - uint32_t pllpd; int ret; ret = update_stack_depth(&ldepth); @@ -1240,8 +1369,7 @@ static int get_pll_freq(const struct s32cc_clk_obj *module, } /* Disabled PLL */ - pllpd = mmio_read_32(PLLDIG_PLLCR(pll_addr)) & PLLDIG_PLLCR_PLLPD; - if (pllpd != 0U) { + if (!is_pll_enabled(pll_addr)) { *rate = pll->vco_freq; return 0; }