From 27fc5ec673b527dbc2f44787246a39c5ecc01de5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 1 Mar 2023 17:32:53 +0100 Subject: [PATCH 01/25] clk: Introduce devm_clk_hw_register_gate_parent_data() Add an API for clock gate that uses parent_data for the parent instead of a string parent_name. Reviewed-by: Peng Fan Reviewed-by: Fabio Estevam Tested-by: Adam Ford #imx8mp-beacon-kit Tested-by: Alexander Stein Signed-off-by: Marek Vasut Tested-by: Richard Leitner Reviewed-by: Stephen Boyd Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20230301163257.49005-1-marex@denx.de --- include/linux/clk-provider.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 842e72a5348fa..92b7c794c6272 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -608,6 +608,25 @@ struct clk *clk_register_gate(struct device *dev, const char *name, __devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \ NULL, (flags), (reg), (bit_idx), \ (clk_gate_flags), (lock)) + +/** + * devm_clk_hw_register_gate - register a gate clock with the clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_data: parent clk data + * @flags: framework-specific flags for this clock + * @reg: register address to control gating of this clock + * @bit_idx: which bit in the register controls gating of this clock + * @clk_gate_flags: gate-specific flags for this clock + * @lock: shared register lock for this clock + */ +#define devm_clk_hw_register_gate_parent_data(dev, name, parent_data, flags, \ + reg, bit_idx, clk_gate_flags, \ + lock) \ + __devm_clk_hw_register_gate((dev), NULL, (name), NULL, NULL, \ + (parent_data), (flags), (reg), (bit_idx), \ + (clk_gate_flags), (lock)) + void clk_unregister_gate(struct clk *clk); void clk_hw_unregister_gate(struct clk_hw *hw); int clk_gate_is_enabled(struct clk_hw *hw); From 6cd95f7b151cdd7852ed9f212faeea8f98ecba10 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 1 Mar 2023 17:32:54 +0100 Subject: [PATCH 02/25] clk: imx: imx8mp: Add audiomix block control Unlike the other block control IPs in i.MX8M, the audiomix is mostly a series of clock gates and muxes. Model it as a large static table of gates and muxes with one exception, which is the PLL14xx . The PLL14xx SAI PLL has to be registered separately. Reviewed-by: Marco Felsch Reviewed-by: Peng Fan Reviewed-by: Fabio Estevam Tested-by: Adam Ford #imx8mp-beacon-kit Tested-by: Alexander Stein Tested-by: Luca Ceresoli Signed-off-by: Marek Vasut Tested-by: Richard Leitner Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20230301163257.49005-2-marex@denx.de --- drivers/clk/imx/Makefile | 2 +- drivers/clk/imx/clk-imx8mp-audiomix.c | 277 ++++++++++++++++++++++++++ 2 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/imx/clk-imx8mp-audiomix.c diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index a75d59f7cb8a6..ae9d84ef046ba 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_MXC_CLK) += mxc-clk.o obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o obj-$(CONFIG_CLK_IMX8MN) += clk-imx8mn.o -obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o +obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-imx8mp-audiomix.o obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o obj-$(CONFIG_CLK_IMX93) += clk-imx93.o diff --git a/drivers/clk/imx/clk-imx8mp-audiomix.c b/drivers/clk/imx/clk-imx8mp-audiomix.c new file mode 100644 index 0000000000000..e4300df88f1ac --- /dev/null +++ b/drivers/clk/imx/clk-imx8mp-audiomix.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for i.MX8M Plus Audio BLK_CTRL + * + * Copyright (C) 2022 Marek Vasut + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" + +#define CLKEN0 0x000 +#define CLKEN1 0x004 +#define SAI_MCLK_SEL(n) (0x300 + 4 * (n)) /* n in 0..5 */ +#define PDM_SEL 0x318 +#define SAI_PLL_GNRL_CTL 0x400 + +#define SAIn_MCLK1_PARENT(n) \ +static const struct clk_parent_data \ +clk_imx8mp_audiomix_sai##n##_mclk1_parents[] = { \ + { \ + .fw_name = "sai"__stringify(n), \ + .name = "sai"__stringify(n) \ + }, { \ + .fw_name = "sai"__stringify(n)"_mclk", \ + .name = "sai"__stringify(n)"_mclk" \ + }, \ +} + +SAIn_MCLK1_PARENT(1); +SAIn_MCLK1_PARENT(2); +SAIn_MCLK1_PARENT(3); +SAIn_MCLK1_PARENT(5); +SAIn_MCLK1_PARENT(6); +SAIn_MCLK1_PARENT(7); + +static const struct clk_parent_data clk_imx8mp_audiomix_sai_mclk2_parents[] = { + { .fw_name = "sai1", .name = "sai1" }, + { .fw_name = "sai2", .name = "sai2" }, + { .fw_name = "sai3", .name = "sai3" }, + { .name = "dummy" }, + { .fw_name = "sai5", .name = "sai5" }, + { .fw_name = "sai6", .name = "sai6" }, + { .fw_name = "sai7", .name = "sai7" }, + { .fw_name = "sai1_mclk", .name = "sai1_mclk" }, + { .fw_name = "sai2_mclk", .name = "sai2_mclk" }, + { .fw_name = "sai3_mclk", .name = "sai3_mclk" }, + { .name = "dummy" }, + { .fw_name = "sai5_mclk", .name = "sai5_mclk" }, + { .fw_name = "sai6_mclk", .name = "sai6_mclk" }, + { .fw_name = "sai7_mclk", .name = "sai7_mclk" }, + { .fw_name = "spdif_extclk", .name = "spdif_extclk" }, + { .name = "dummy" }, +}; + +static const struct clk_parent_data clk_imx8mp_audiomix_pdm_parents[] = { + { .fw_name = "pdm", .name = "pdm" }, + { .name = "sai_pll_out_div2" }, + { .fw_name = "sai1_mclk", .name = "sai1_mclk" }, + { .name = "dummy" }, +}; + + +static const struct clk_parent_data clk_imx8mp_audiomix_pll_parents[] = { + { .fw_name = "osc_24m", .name = "osc_24m" }, + { .name = "dummy" }, + { .name = "dummy" }, + { .name = "dummy" }, +}; + +static const struct clk_parent_data clk_imx8mp_audiomix_pll_bypass_sels[] = { + { .fw_name = "sai_pll", .name = "sai_pll" }, + { .fw_name = "sai_pll_ref_sel", .name = "sai_pll_ref_sel" }, +}; + +#define CLK_GATE(gname, cname) \ + { \ + gname"_cg", \ + IMX8MP_CLK_AUDIOMIX_##cname, \ + { .fw_name = "ahb", .name = "ahb" }, NULL, 1, \ + CLKEN0 + 4 * !!(IMX8MP_CLK_AUDIOMIX_##cname / 32), \ + 1, IMX8MP_CLK_AUDIOMIX_##cname % 32 \ + } + +#define CLK_SAIn(n) \ + { \ + "sai"__stringify(n)"_mclk1_sel", \ + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1_SEL, {}, \ + clk_imx8mp_audiomix_sai##n##_mclk1_parents, \ + ARRAY_SIZE(clk_imx8mp_audiomix_sai##n##_mclk1_parents), \ + SAI_MCLK_SEL(n), 1, 0 \ + }, { \ + "sai"__stringify(n)"_mclk2_sel", \ + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2_SEL, {}, \ + clk_imx8mp_audiomix_sai_mclk2_parents, \ + ARRAY_SIZE(clk_imx8mp_audiomix_sai_mclk2_parents), \ + SAI_MCLK_SEL(n), 4, 1 \ + }, { \ + "sai"__stringify(n)"_ipg_cg", \ + IMX8MP_CLK_AUDIOMIX_SAI##n##_IPG, \ + { .fw_name = "ahb", .name = "ahb" }, NULL, 1, \ + CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_IPG \ + }, { \ + "sai"__stringify(n)"_mclk1_cg", \ + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1, \ + { \ + .fw_name = "sai"__stringify(n)"_mclk1_sel", \ + .name = "sai"__stringify(n)"_mclk1_sel" \ + }, NULL, 1, \ + CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1 \ + }, { \ + "sai"__stringify(n)"_mclk2_cg", \ + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2, \ + { \ + .fw_name = "sai"__stringify(n)"_mclk2_sel", \ + .name = "sai"__stringify(n)"_mclk2_sel" \ + }, NULL, 1, \ + CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2 \ + }, { \ + "sai"__stringify(n)"_mclk3_cg", \ + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK3, \ + { \ + .fw_name = "sai_pll_out_div2", \ + .name = "sai_pll_out_div2" \ + }, NULL, 1, \ + CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK3 \ + } + +#define CLK_PDM \ + { \ + "pdm_sel", IMX8MP_CLK_AUDIOMIX_PDM_SEL, {}, \ + clk_imx8mp_audiomix_pdm_parents, \ + ARRAY_SIZE(clk_imx8mp_audiomix_pdm_parents), \ + PDM_SEL, 2, 0 \ + } + +struct clk_imx8mp_audiomix_sel { + const char *name; + int clkid; + const struct clk_parent_data parent; /* For gate */ + const struct clk_parent_data *parents; /* For mux */ + int num_parents; + u16 reg; + u8 width; + u8 shift; +}; + +static struct clk_imx8mp_audiomix_sel sels[] = { + CLK_GATE("asrc", ASRC_IPG), + CLK_GATE("pdm", PDM_IPG), + CLK_GATE("earc", EARC_IPG), + CLK_GATE("ocrama", OCRAMA_IPG), + CLK_GATE("aud2htx", AUD2HTX_IPG), + CLK_GATE("earc_phy", EARC_PHY), + CLK_GATE("sdma2", SDMA2_ROOT), + CLK_GATE("sdma3", SDMA3_ROOT), + CLK_GATE("spba2", SPBA2_ROOT), + CLK_GATE("dsp", DSP_ROOT), + CLK_GATE("dspdbg", DSPDBG_ROOT), + CLK_GATE("edma", EDMA_ROOT), + CLK_GATE("audpll", AUDPLL_ROOT), + CLK_GATE("mu2", MU2_ROOT), + CLK_GATE("mu3", MU3_ROOT), + CLK_PDM, + CLK_SAIn(1), + CLK_SAIn(2), + CLK_SAIn(3), + CLK_SAIn(5), + CLK_SAIn(6), + CLK_SAIn(7) +}; + +static int clk_imx8mp_audiomix_probe(struct platform_device *pdev) +{ + struct clk_hw_onecell_data *priv; + struct device *dev = &pdev->dev; + void __iomem *base; + struct clk_hw *hw; + int i; + + priv = devm_kzalloc(dev, + struct_size(priv, hws, IMX8MP_CLK_AUDIOMIX_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->num = IMX8MP_CLK_AUDIOMIX_END; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + for (i = 0; i < ARRAY_SIZE(sels); i++) { + if (sels[i].num_parents == 1) { + hw = devm_clk_hw_register_gate_parent_data(dev, + sels[i].name, &sels[i].parent, 0, + base + sels[i].reg, sels[i].shift, 0, NULL); + } else { + hw = devm_clk_hw_register_mux_parent_data_table(dev, + sels[i].name, sels[i].parents, + sels[i].num_parents, 0, + base + sels[i].reg, + sels[i].shift, sels[i].width, + 0, NULL, NULL); + } + + if (IS_ERR(hw)) + return PTR_ERR(hw); + + priv->hws[sels[i].clkid] = hw; + } + + /* SAI PLL */ + hw = devm_clk_hw_register_mux_parent_data_table(dev, + "sai_pll_ref_sel", clk_imx8mp_audiomix_pll_parents, + ARRAY_SIZE(clk_imx8mp_audiomix_pll_parents), + CLK_SET_RATE_NO_REPARENT, base + SAI_PLL_GNRL_CTL, + 0, 2, 0, NULL, NULL); + priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = hw; + + hw = imx_dev_clk_hw_pll14xx(dev, "sai_pll", "sai_pll_ref_sel", + base + 0x400, &imx_1443x_pll); + if (IS_ERR(hw)) + return PTR_ERR(hw); + priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = hw; + + hw = devm_clk_hw_register_mux_parent_data_table(dev, + "sai_pll_bypass", clk_imx8mp_audiomix_pll_bypass_sels, + ARRAY_SIZE(clk_imx8mp_audiomix_pll_bypass_sels), + CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, + base + SAI_PLL_GNRL_CTL, 16, 1, 0, NULL, NULL); + if (IS_ERR(hw)) + return PTR_ERR(hw); + priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = hw; + + hw = devm_clk_hw_register_gate(dev, "sai_pll_out", "sai_pll_bypass", + 0, base + SAI_PLL_GNRL_CTL, 13, + 0, NULL); + if (IS_ERR(hw)) + return PTR_ERR(hw); + priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = hw; + + hw = devm_clk_hw_register_fixed_factor(dev, "sai_pll_out_div2", + "sai_pll_out", 0, 1, 2); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, + priv); +} + +static const struct of_device_id clk_imx8mp_audiomix_of_match[] = { + { .compatible = "fsl,imx8mp-audio-blk-ctrl" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, clk_imx8mp_audiomix_of_match); + +static struct platform_driver clk_imx8mp_audiomix_driver = { + .probe = clk_imx8mp_audiomix_probe, + .driver = { + .name = "imx8mp-audio-blk-ctrl", + .of_match_table = clk_imx8mp_audiomix_of_match, + }, +}; + +module_platform_driver(clk_imx8mp_audiomix_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller driver"); +MODULE_LICENSE("GPL"); From 95a0aa7bb10e79cfbe8a1dc4b993d21dd58c253f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 1 Mar 2023 17:32:55 +0100 Subject: [PATCH 03/25] dt-bindings: clock: imx8mp: Add audiomix block control Unlike the other block control IPs in i.MX8M, the audiomix is mostly a series of clock gates and muxes. Add DT bindings for this IP. Reviewed-by: Fabio Estevam Reviewed-by: Marco Felsch Reviewed-by: Peng Fan Reviewed-by: Rob Herring Tested-by: Adam Ford #imx8mp-beacon-kit Tested-by: Alexander Stein Signed-off-by: Marek Vasut Tested-by: Richard Leitner Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20230301163257.49005-3-marex@denx.de --- .../bindings/clock/imx8mp-audiomix.yaml | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/imx8mp-audiomix.yaml diff --git a/Documentation/devicetree/bindings/clock/imx8mp-audiomix.yaml b/Documentation/devicetree/bindings/clock/imx8mp-audiomix.yaml new file mode 100644 index 0000000000000..ff9600474df20 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx8mp-audiomix.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/imx8mp-audiomix.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP i.MX8MP AudioMIX Block Control Binding + +maintainers: + - Marek Vasut + +description: | + NXP i.MX8M Plus AudioMIX is dedicated clock muxing and gating IP + used to control Audio related clock on the SoC. + +properties: + compatible: + const: fsl,imx8mp-audio-blk-ctrl + + reg: + maxItems: 1 + + power-domains: + maxItems: 1 + + clocks: + minItems: 7 + maxItems: 7 + + clock-names: + items: + - const: ahb + - const: sai1 + - const: sai2 + - const: sai3 + - const: sai5 + - const: sai6 + - const: sai7 + + '#clock-cells': + const: 1 + description: + The clock consumer should specify the desired clock by having the clock + ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx8mp-clock.h + for the full list of i.MX8MP IMX8MP_CLK_AUDIOMIX_ clock IDs. + +required: + - compatible + - reg + - clocks + - clock-names + - power-domains + - '#clock-cells' + +additionalProperties: false + +examples: + # Clock Control Module node: + - | + #include + + clock-controller@30e20000 { + compatible = "fsl,imx8mp-audio-blk-ctrl"; + reg = <0x30e20000 0x10000>; + #clock-cells = <1>; + clocks = <&clk IMX8MP_CLK_AUDIO_ROOT>, + <&clk IMX8MP_CLK_SAI1>, + <&clk IMX8MP_CLK_SAI2>, + <&clk IMX8MP_CLK_SAI3>, + <&clk IMX8MP_CLK_SAI5>, + <&clk IMX8MP_CLK_SAI6>, + <&clk IMX8MP_CLK_SAI7>; + clock-names = "ahb", + "sai1", "sai2", "sai3", + "sai5", "sai6", "sai7"; + power-domains = <&pgc_audio>; + }; + +... From 156e96ff2172518b6f83e97d8f11f677bc668e22 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Thu, 23 Mar 2023 18:01:24 -0500 Subject: [PATCH 04/25] clk: imx: composite-8m: Add support to determine_rate Similar to imx/clk-composite-93 and imx/clk-divider-gate, the imx8m_clk_composite_divider_ops can support determine_rate. Without this the parent clocks are set to a fixed value, and if a consumer needs a slower reate, the clock is divided, but the division is only as good as the parent clock rate. With this added, the system can attempt to adjust the parent rate if the proper flags are set which can lead to a more precise clock value. Signed-off-by: Adam Ford Reviewed-by: Peng Fan Reviewed-by: Fabio Estevam Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20230323230127.120883-2-aford173@gmail.com --- drivers/clk/imx/clk-composite-8m.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index cbf0d7955a00a..6883a8199b6c3 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -119,10 +119,17 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, return ret; } +static int imx8m_clk_divider_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_divider_ops.determine_rate(hw, req); +} + static const struct clk_ops imx8m_clk_composite_divider_ops = { .recalc_rate = imx8m_clk_composite_divider_recalc_rate, .round_rate = imx8m_clk_composite_divider_round_rate, .set_rate = imx8m_clk_composite_divider_set_rate, + .determine_rate = imx8m_clk_divider_determine_rate, }; static u8 imx8m_clk_composite_mux_get_parent(struct clk_hw *hw) From 784a9b3916e949c00666588fd167c4ab245ec9d6 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Thu, 23 Mar 2023 18:01:25 -0500 Subject: [PATCH 05/25] clk: imx: Add imx8m_clk_hw_composite_flags macro In order to set custom flags to imx8m_clk_hw_composite, split it off into a separate macro which can accept additional flags. Signed-off-by: Adam Ford Reviewed-by: Fabio Estevam Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20230323230127.120883-3-aford173@gmail.com --- drivers/clk/imx/clk.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 3d94722bbf992..621b0e84ef270 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -417,6 +417,10 @@ struct clk_hw *__imx8m_clk_hw_composite(const char *name, _imx8m_clk_hw_composite(name, parent_names, reg, \ 0, IMX_COMPOSITE_CLK_FLAGS_DEFAULT) +#define imx8m_clk_hw_composite_flags(name, parent_names, reg, flags) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + 0, IMX_COMPOSITE_CLK_FLAGS_DEFAULT | flags) + #define imx8m_clk_hw_composite_critical(name, parent_names, reg) \ _imx8m_clk_hw_composite(name, parent_names, reg, \ 0, IMX_COMPOSITE_CLK_FLAGS_CRITICAL) From 5fe6ec93f10b0765d59e0efb6ecba419a6a49d48 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Thu, 23 Mar 2023 18:01:26 -0500 Subject: [PATCH 06/25] clk: imx8mm: Let IMX8MM_CLK_LCDIF_PIXEL set parent rate By default the display pixel clock needs to be evenly divide down from 594MHz which rules out a significant number of resolution and refresh rates. The current clock tree looks something like: video_pll1 594000000 video_pll1_bypass 594000000 video_pll1_out 594000000 lcdif_pixel 148500000 Now that composite-8m supports determine_rate, we can allow lcdif_pixel to set the parent rate which then switches every clock in the chain to a new frequency when lcdif_pixel cannot evenly divide from video_pll1_out. Signed-off-by: Adam Ford Reviewed-by: Fabio Estevam Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20230323230127.120883-4-aford173@gmail.com --- drivers/clk/imx/clk-imx8mm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index b618892170f26..075f643e3f356 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -468,7 +468,7 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) hws[IMX8MM_CLK_PCIE1_PHY] = imx8m_clk_hw_composite("pcie1_phy", imx8mm_pcie1_phy_sels, base + 0xa380); hws[IMX8MM_CLK_PCIE1_AUX] = imx8m_clk_hw_composite("pcie1_aux", imx8mm_pcie1_aux_sels, base + 0xa400); hws[IMX8MM_CLK_DC_PIXEL] = imx8m_clk_hw_composite("dc_pixel", imx8mm_dc_pixel_sels, base + 0xa480); - hws[IMX8MM_CLK_LCDIF_PIXEL] = imx8m_clk_hw_composite("lcdif_pixel", imx8mm_lcdif_pixel_sels, base + 0xa500); + hws[IMX8MM_CLK_LCDIF_PIXEL] = imx8m_clk_hw_composite_flags("lcdif_pixel", imx8mm_lcdif_pixel_sels, base + 0xa500, CLK_SET_RATE_PARENT); hws[IMX8MM_CLK_SAI1] = imx8m_clk_hw_composite("sai1", imx8mm_sai1_sels, base + 0xa580); hws[IMX8MM_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mm_sai2_sels, base + 0xa600); hws[IMX8MM_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mm_sai3_sels, base + 0xa680); From 46a974433ea7fa468b45db70536f7cea81feb87c Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Thu, 23 Mar 2023 18:01:27 -0500 Subject: [PATCH 07/25] clk: imx: Let IMX8MN_CLK_DISP_PIXEL set parent rate By default the display pixel clock needs to be evenly divide down from the video_pll_out clock which rules out a significant number of resolution and refresh rates. The current clock tree looks something like: video_pll 594000000 video_pll_bypass 594000000 video_pll_out 594000000 disp_pixel 148500000 disp_pixel_clk 148500000 Now that composite-8m supports determine_rate, we can allow disp_pixel to set the parent rate which then switches every clock in the chain to a new frequency when disp_pixel cannot evenly divide from video_pll_out. Signed-off-by: Adam Ford Reviewed-by: Fabio Estevam Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20230323230127.120883-5-aford173@gmail.com --- drivers/clk/imx/clk-imx8mn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index a042ed3a9d6c2..4b23a46486004 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -470,7 +470,7 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) hws[IMX8MN_CLK_DRAM_ALT] = imx8m_clk_hw_fw_managed_composite("dram_alt", imx8mn_dram_alt_sels, base + 0xa000); hws[IMX8MN_CLK_DRAM_APB] = imx8m_clk_hw_fw_managed_composite_critical("dram_apb", imx8mn_dram_apb_sels, base + 0xa080); - hws[IMX8MN_CLK_DISP_PIXEL] = imx8m_clk_hw_composite("disp_pixel", imx8mn_disp_pixel_sels, base + 0xa500); + hws[IMX8MN_CLK_DISP_PIXEL] = imx8m_clk_hw_composite_flags("disp_pixel", imx8mn_disp_pixel_sels, base + 0xa500, CLK_SET_RATE_PARENT); hws[IMX8MN_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mn_sai2_sels, base + 0xa600); hws[IMX8MN_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mn_sai3_sels, base + 0xa680); hws[IMX8MN_CLK_SAI5] = imx8m_clk_hw_composite("sai5", imx8mn_sai5_sels, base + 0xa780); From f47a669ffa11c6c14b463d762562fe9681345c6e Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Wed, 8 Mar 2023 19:46:03 +0100 Subject: [PATCH 08/25] clk: imx: clk-gpr-mux: Provide clock name in error message In error case the error message doesn't provide much context: imx:clk-gpr-mux: failed to get parent (-EINVAL) So additionally provide the clock name in the message, in order to simplify the further analyze. Signed-off-by: Stefan Wahren Reviewed-by: Peng Fan Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20230308184603.10049-1-stefan.wahren@i2se.com --- drivers/clk/imx/clk-gpr-mux.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-gpr-mux.c b/drivers/clk/imx/clk-gpr-mux.c index c8d6090f15d65..0b5a97698b47c 100644 --- a/drivers/clk/imx/clk-gpr-mux.c +++ b/drivers/clk/imx/clk-gpr-mux.c @@ -48,7 +48,8 @@ static u8 imx_clk_gpr_mux_get_parent(struct clk_hw *hw) return ret; get_parent_err: - pr_err("failed to get parent (%pe)\n", ERR_PTR(ret)); + pr_err("%s: failed to get parent (%pe)\n", + clk_hw_get_name(hw), ERR_PTR(ret)); /* return some realistic non negative value. Potentially we could * give index to some dummy error parent. From 79ef82c55a37b9c3605602c4909db84481c9fb2f Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 3 Apr 2023 16:27:28 +0800 Subject: [PATCH 09/25] clk: imx: drop duplicated macro Drop duplicated macro definition Signed-off-by: Peng Fan Reviewed-by: Ahmad Fatoum Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20230403082728.3199849-1-peng.fan@oss.nxp.com --- drivers/clk/imx/clk.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 621b0e84ef270..ecce45b0dd70d 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -153,9 +153,6 @@ extern struct imx_fracn_gppll_clk imx_fracn_gppll; #define imx_clk_pllv2(name, parent, base) \ to_clk(imx_clk_hw_pllv2(name, parent, base)) -#define imx_clk_mux_flags(name, reg, shift, width, parents, num_parents, flags) \ - to_clk(imx_clk_hw_mux_flags(name, reg, shift, width, parents, num_parents, flags)) - #define imx_clk_hw_gate(name, parent, reg, shift) \ imx_clk_hw_gate_flags(name, parent, reg, shift, 0) From 3ea7c4c907119eb369d6b4cdec22af0434eb5304 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 3 Apr 2023 17:46:30 +0800 Subject: [PATCH 10/25] clk: imx: imx8mp: correct DISP2 pixel clock type The MEDIA_DISP2_CLK_ROOT use ccm_ahb_channel, it is bus type. Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230403094633.3366446-1-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx8mp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index 3253589851ffb..4a0f1b739fd42 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -554,7 +554,7 @@ static int imx8mp_clocks_probe(struct platform_device *pdev) hws[IMX8MP_CLK_AHB] = imx8m_clk_hw_composite_bus_critical("ahb_root", imx8mp_ahb_sels, ccm_base + 0x9000); hws[IMX8MP_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mp_audio_ahb_sels, ccm_base + 0x9100); hws[IMX8MP_CLK_MIPI_DSI_ESC_RX] = imx8m_clk_hw_composite_bus("mipi_dsi_esc_rx", imx8mp_mipi_dsi_esc_rx_sels, ccm_base + 0x9200); - hws[IMX8MP_CLK_MEDIA_DISP2_PIX] = imx8m_clk_hw_composite("media_disp2_pix", imx8mp_media_disp_pix_sels, ccm_base + 0x9300); + hws[IMX8MP_CLK_MEDIA_DISP2_PIX] = imx8m_clk_hw_composite_bus("media_disp2_pix", imx8mp_media_disp_pix_sels, ccm_base + 0x9300); hws[IMX8MP_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb_root", ccm_base + 0x9080, 0, 1); From 79643567cc34ebd0743f4da3ac8f853e26202453 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 3 Apr 2023 17:46:31 +0800 Subject: [PATCH 11/25] dt-bindings: clock: imx8mp: Add LDB clock entry Add LDB clock entry for i.MX8MP Signed-off-by: Peng Fan Acked-by: Krzysztof Kozlowski Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230403094633.3366446-2-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- include/dt-bindings/clock/imx8mp-clock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dt-bindings/clock/imx8mp-clock.h b/include/dt-bindings/clock/imx8mp-clock.h index ede1f65a31471..3f28ce685f412 100644 --- a/include/dt-bindings/clock/imx8mp-clock.h +++ b/include/dt-bindings/clock/imx8mp-clock.h @@ -334,8 +334,8 @@ #define IMX8MP_CLK_SAI6_ROOT 326 #define IMX8MP_CLK_SAI7_ROOT 327 #define IMX8MP_CLK_PDM_ROOT 328 - -#define IMX8MP_CLK_END 329 +#define IMX8MP_CLK_MEDIA_LDB_ROOT 329 +#define IMX8MP_CLK_END 330 #define IMX8MP_CLK_AUDIOMIX_SAI1_IPG 0 #define IMX8MP_CLK_AUDIOMIX_SAI1_MCLK1 1 From 82afc344d795cb467a646a2873573298162f01b9 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 3 Apr 2023 17:46:32 +0800 Subject: [PATCH 12/25] clk: imx: imx8mp: Add LDB root clock This patch adds "media_ldb_root_clk" clock for the LDB in the MEDIAMIX subsystem. Reviewed-by: Sandor Yu Signed-off-by: Liu Ying Signed-off-by: Dong Aisheng Signed-off-by: Peng Fan Link: https://lore.kernel.org/r/20230403094633.3366446-3-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx8mp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index 4a0f1b739fd42..8dcaeb213277b 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -696,6 +696,7 @@ static int imx8mp_clocks_probe(struct platform_device *pdev) hws[IMX8MP_CLK_MEDIA_DISP1_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp1_pix_root_clk", "media_disp1_pix", ccm_base + 0x45d0, 0, &share_count_media); hws[IMX8MP_CLK_MEDIA_DISP2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp2_pix_root_clk", "media_disp2_pix", ccm_base + 0x45d0, 0, &share_count_media); hws[IMX8MP_CLK_MEDIA_MIPI_PHY1_REF_ROOT] = imx_clk_hw_gate2_shared2("media_mipi_phy1_ref_root", "media_mipi_phy1_ref", ccm_base + 0x45d0, 0, &share_count_media); + hws[IMX8MP_CLK_MEDIA_LDB_ROOT] = imx_clk_hw_gate2_shared2("media_ldb_root_clk", "media_ldb", ccm_base + 0x45d0, 0, &share_count_media); hws[IMX8MP_CLK_MEDIA_ISP_ROOT] = imx_clk_hw_gate2_shared2("media_isp_root_clk", "media_isp", ccm_base + 0x45d0, 0, &share_count_media); hws[IMX8MP_CLK_USDHC3_ROOT] = imx_clk_hw_gate4("usdhc3_root_clk", "usdhc3", ccm_base + 0x45e0, 0); From 7875ee29f877dc76dae2d04648b95811f6a05b41 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 3 Apr 2023 17:46:33 +0800 Subject: [PATCH 13/25] clk: imx: imx8mp: change the 'nand_usdhc_bus' clock to non-critical The 'nand_usdhc_bus' clock is only need to be enabled when usdhc or nand module is active, so change it to non-critical clock type. Signed-off-by: Haibo Chen Signed-off-by: Peng Fan Link: https://lore.kernel.org/r/20230403094633.3366446-4-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx8mp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index 8dcaeb213277b..f26ae8de4cc6f 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -538,7 +538,7 @@ static int imx8mp_clocks_probe(struct platform_device *pdev) hws[IMX8MP_CLK_MAIN_AXI] = imx8m_clk_hw_composite_bus_critical("main_axi", imx8mp_main_axi_sels, ccm_base + 0x8800); hws[IMX8MP_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mp_enet_axi_sels, ccm_base + 0x8880); - hws[IMX8MP_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_bus_critical("nand_usdhc_bus", imx8mp_nand_usdhc_sels, ccm_base + 0x8900); + hws[IMX8MP_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite("nand_usdhc_bus", imx8mp_nand_usdhc_sels, ccm_base + 0x8900); hws[IMX8MP_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mp_vpu_bus_sels, ccm_base + 0x8980); hws[IMX8MP_CLK_MEDIA_AXI] = imx8m_clk_hw_composite_bus("media_axi", imx8mp_media_axi_sels, ccm_base + 0x8a00); hws[IMX8MP_CLK_MEDIA_APB] = imx8m_clk_hw_composite_bus("media_apb", imx8mp_media_apb_sels, ccm_base + 0x8a80); From cf8dccfedce848f67eaa42e8839305d028319161 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 3 Apr 2023 17:52:54 +0800 Subject: [PATCH 14/25] clk: imx: fracn-gppll: fix the rate table The Fvco should be range 2.4GHz to 5GHz, the original table voilate the spec, so update the table to fix it. Fixes: c196175acdd3 ("clk: imx: clk-fracn-gppll: Add more freq config for video pll") Fixes: 044034efbeea ("clk: imx: clk-fracn-gppll: fix mfd value") Fixes: 1b26cb8a77a4 ("clk: imx: support fracn gppll") Signed-off-by: Jacky Bai Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230403095300.3386988-2-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-fracn-gppll.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c index a2aaa14fc1aef..ec50c41e2a4c9 100644 --- a/drivers/clk/imx/clk-fracn-gppll.c +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -60,18 +60,20 @@ struct clk_fracn_gppll { }; /* - * Fvco = Fref * (MFI + MFN / MFD) - * Fout = Fvco / (rdiv * odiv) + * Fvco = (Fref / rdiv) * (MFI + MFN / MFD) + * Fout = Fvco / odiv + * The (Fref / rdiv) should be in range 20MHz to 40MHz + * The Fvco should be in range 2.5Ghz to 5Ghz */ static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { - PLL_FRACN_GP(650000000U, 81, 0, 1, 0, 3), + PLL_FRACN_GP(650000000U, 162, 50, 100, 0, 6), PLL_FRACN_GP(594000000U, 198, 0, 1, 0, 8), - PLL_FRACN_GP(560000000U, 70, 0, 1, 0, 3), - PLL_FRACN_GP(498000000U, 83, 0, 1, 0, 4), + PLL_FRACN_GP(560000000U, 140, 0, 1, 0, 6), + PLL_FRACN_GP(498000000U, 166, 0, 1, 0, 8), PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6), PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9), - PLL_FRACN_GP(400000000U, 50, 0, 1, 0, 3), - PLL_FRACN_GP(393216000U, 81, 92, 100, 0, 5) + PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12), + PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10) }; struct imx_fracn_gppll_clk imx_fracn_gppll = { From 4435467b15b069e5a6f50ca9a9260e86b74dbc13 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 3 Apr 2023 17:52:55 +0800 Subject: [PATCH 15/25] clk: imx: fracn-gppll: disable hardware select control When programming PLL, should disable Hardware control select to make PLL controlled by register, not hardware inputs through OSCPLL. Fixes: 1b26cb8a77a4 ("clk: imx: support fracn gppll") Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230403095300.3386988-3-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-fracn-gppll.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c index ec50c41e2a4c9..f6674110a88e0 100644 --- a/drivers/clk/imx/clk-fracn-gppll.c +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -15,6 +15,7 @@ #include "clk.h" #define PLL_CTRL 0x0 +#define HW_CTRL_SEL BIT(16) #define CLKMUX_BYPASS BIT(2) #define CLKMUX_EN BIT(1) #define POWERUP_MASK BIT(0) @@ -193,6 +194,11 @@ static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate, rate = imx_get_pll_settings(pll, drate); + /* Hardware control select disable. PLL is control by register */ + tmp = readl_relaxed(pll->base + PLL_CTRL); + tmp &= ~HW_CTRL_SEL; + writel_relaxed(tmp, pll->base + PLL_CTRL); + /* Disable output */ tmp = readl_relaxed(pll->base + PLL_CTRL); tmp &= ~CLKMUX_EN; From 56b8d0bf3ea8b0db8543e04a6b97348a543405ab Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 3 Apr 2023 17:52:56 +0800 Subject: [PATCH 16/25] clk: imx: fracn-gppll: support integer pll The fracn gppll could be configured in FRAC or INTEGER mode during hardware design. The current driver only support FRAC mode, while this patch introduces INTEGER support. When the PLL is INTEGER pll, there is no mfn, mfd, the calculation is as below: Fvco_clk = (Fref / DIV[RDIV] ) * DIV[MFI] Fclko_odiv = Fvco_clk / DIV[ODIV] In this patch, we reuse the FRAC pll logic with some condition check to simplify the driver Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230403095300.3386988-4-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-fracn-gppll.c | 68 +++++++++++++++++++++++++++---- drivers/clk/imx/clk.h | 7 ++++ 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c index f6674110a88e0..e2633ad94640f 100644 --- a/drivers/clk/imx/clk-fracn-gppll.c +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -53,11 +53,22 @@ .odiv = (_odiv), \ } +#define PLL_FRACN_GP_INTEGER(_rate, _mfi, _rdiv, _odiv) \ + { \ + .rate = (_rate), \ + .mfi = (_mfi), \ + .mfn = 0, \ + .mfd = 0, \ + .rdiv = (_rdiv), \ + .odiv = (_odiv), \ + } + struct clk_fracn_gppll { struct clk_hw hw; void __iomem *base; const struct imx_fracn_gppll_rate_table *rate_table; int rate_count; + u32 flags; }; /* @@ -83,6 +94,24 @@ struct imx_fracn_gppll_clk imx_fracn_gppll = { }; EXPORT_SYMBOL_GPL(imx_fracn_gppll); +/* + * Fvco = (Fref / rdiv) * MFI + * Fout = Fvco / odiv + * The (Fref / rdiv) should be in range 20MHz to 40MHz + * The Fvco should be in range 2.5Ghz to 5Ghz + */ +static const struct imx_fracn_gppll_rate_table int_tbl[] = { + PLL_FRACN_GP_INTEGER(1700000000U, 141, 1, 2), + PLL_FRACN_GP_INTEGER(1400000000U, 175, 1, 3), + PLL_FRACN_GP_INTEGER(900000000U, 150, 1, 4), +}; + +struct imx_fracn_gppll_clk imx_fracn_gppll_integer = { + .rate_table = int_tbl, + .rate_count = ARRAY_SIZE(int_tbl), +}; +EXPORT_SYMBOL_GPL(imx_fracn_gppll_integer); + static inline struct clk_fracn_gppll *to_clk_fracn_gppll(struct clk_hw *hw) { return container_of(hw, struct clk_fracn_gppll, hw); @@ -169,9 +198,15 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon break; } - /* Fvco = Fref * (MFI + MFN / MFD) */ - fvco = fvco * mfi * mfd + fvco * mfn; - do_div(fvco, mfd * rdiv * odiv); + if (pll->flags & CLK_FRACN_GPPLL_INTEGER) { + /* Fvco = (Fref / rdiv) * MFI */ + fvco = fvco * mfi; + do_div(fvco, rdiv * odiv); + } else { + /* Fvco = (Fref / rdiv) * (MFI + MFN / MFD) */ + fvco = fvco * mfi * mfd + fvco * mfn; + do_div(fvco, mfd * rdiv * odiv); + } return (unsigned long)fvco; } @@ -215,8 +250,10 @@ static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate, pll_div = FIELD_PREP(PLL_RDIV_MASK, rate->rdiv) | rate->odiv | FIELD_PREP(PLL_MFI_MASK, rate->mfi); writel_relaxed(pll_div, pll->base + PLL_DIV); - writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR); - writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR); + if (pll->flags & CLK_FRACN_GPPLL_FRACN) { + writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR); + writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR); + } /* Wait for 5us according to fracn mode pll doc */ udelay(5); @@ -300,8 +337,10 @@ static const struct clk_ops clk_fracn_gppll_ops = { .set_rate = clk_fracn_gppll_set_rate, }; -struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, - const struct imx_fracn_gppll_clk *pll_clk) +static struct clk_hw *_imx_clk_fracn_gppll(const char *name, const char *parent_name, + void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk, + u32 pll_flags) { struct clk_fracn_gppll *pll; struct clk_hw *hw; @@ -322,6 +361,7 @@ struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, vo pll->hw.init = &init; pll->rate_table = pll_clk->rate_table; pll->rate_count = pll_clk->rate_count; + pll->flags = pll_flags; hw = &pll->hw; @@ -334,4 +374,18 @@ struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, vo return hw; } + +struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk) +{ + return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_FRACN); +} EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll); + +struct clk_hw *imx_clk_fracn_gppll_integer(const char *name, const char *parent_name, + void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk) +{ + return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_INTEGER); +} +EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll_integer); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index ecce45b0dd70d..c8db0c8d4c5da 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -73,6 +73,9 @@ extern struct imx_pll14xx_clk imx_1416x_pll; extern struct imx_pll14xx_clk imx_1443x_pll; extern struct imx_pll14xx_clk imx_1443x_dram_pll; +#define CLK_FRACN_GPPLL_INTEGER BIT(0) +#define CLK_FRACN_GPPLL_FRACN BIT(1) + /* NOTE: Rate table should be kept sorted in descending order. */ struct imx_fracn_gppll_rate_table { unsigned int rate; @@ -91,8 +94,12 @@ struct imx_fracn_gppll_clk { struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, const struct imx_fracn_gppll_clk *pll_clk); +struct clk_hw *imx_clk_fracn_gppll_integer(const char *name, const char *parent_name, + void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk); extern struct imx_fracn_gppll_clk imx_fracn_gppll; +extern struct imx_fracn_gppll_clk imx_fracn_gppll_integer; #define imx_clk_cpu(name, parent_name, div, mux, pll, step) \ to_clk(imx_clk_hw_cpu(name, parent_name, div, mux, pll, step)) From e040897111a12b7647b8f758336b2f14991e9371 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Mon, 3 Apr 2023 17:52:57 +0800 Subject: [PATCH 17/25] clk: imx: fracn-gppll: Add 300MHz freq support for imx9 Add 300MHz frequency config support on i.MX93 PLL. Reviewed-by: Ye Li Signed-off-by: Jacky Bai Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230403095300.3386988-5-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-fracn-gppll.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c index e2633ad94640f..c54f9999da04c 100644 --- a/drivers/clk/imx/clk-fracn-gppll.c +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -85,7 +85,8 @@ static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6), PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9), PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12), - PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10) + PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10), + PLL_FRACN_GP(300000000U, 150, 0, 1, 0, 12) }; struct imx_fracn_gppll_clk imx_fracn_gppll = { From a740d7350ff77ce1ebbdc3b9c548dd3bcaf39b31 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 3 Apr 2023 17:52:58 +0800 Subject: [PATCH 18/25] clk: imx: imx93: add mcore_booted module paratemter Add mcore_booted boot parameter which could simplify AMP clock management. To i.MX93, there is CCM(clock control Module) to generate clock root clock, anatop(analog PLL module) to generate PLL, and LPCG (clock gating) to gate clocks to peripherals. As below: anatop->ccm->lpcg->peripheral Linux handles the clock management and the auxiliary core is under control of Linux. Although there is per hardware domain control for LPCG and CCM, auxiliary core normally only use LPCG hardware domain control to avoid linux gate off the clk to peripherals and leave CCM ana anatop to Linux. Reviewed-by: Ye Li Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230403095300.3386988-6-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-composite-93.c | 8 +++++++- drivers/clk/imx/clk-imx93.c | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-composite-93.c b/drivers/clk/imx/clk-composite-93.c index 74a66b0203e4b..81164bdcd6cc9 100644 --- a/drivers/clk/imx/clk-composite-93.c +++ b/drivers/clk/imx/clk-composite-93.c @@ -222,7 +222,7 @@ struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *p hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, mux_hw, &clk_mux_ro_ops, div_hw, &clk_divider_ro_ops, NULL, NULL, flags); - } else { + } else if (!mcore_booted) { gate = kzalloc(sizeof(*gate), GFP_KERNEL); if (!gate) goto fail; @@ -238,6 +238,12 @@ struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *p &imx93_clk_composite_divider_ops, gate_hw, &imx93_clk_composite_gate_ops, flags | CLK_SET_RATE_NO_REPARENT); + } else { + hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, + mux_hw, &imx93_clk_composite_mux_ops, div_hw, + &imx93_clk_composite_divider_ops, NULL, + &imx93_clk_composite_gate_ops, + flags | CLK_SET_RATE_NO_REPARENT); } if (IS_ERR(hw)) diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c index 8d0974db6bfd8..de1ed1d8ba541 100644 --- a/drivers/clk/imx/clk-imx93.c +++ b/drivers/clk/imx/clk-imx93.c @@ -352,6 +352,8 @@ static struct platform_driver imx93_clk_driver = { }, }; module_platform_driver(imx93_clk_driver); +module_param(mcore_booted, bool, 0444); +MODULE_PARM_DESC(mcore_booted, "See Cortex-M core is booted or not"); MODULE_DESCRIPTION("NXP i.MX93 clock driver"); MODULE_LICENSE("GPL v2"); From 5fd7b00ca2361c81f2026f82dff93e52afd97a0b Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 3 Apr 2023 17:52:59 +0800 Subject: [PATCH 19/25] dt-bindings: clock: imx93: add NIC, A55 and ARM PLL CLK Add i.MX93 NIC, A55 and ARM PLL CLK. Signed-off-by: Peng Fan Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230403095300.3386988-7-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- include/dt-bindings/clock/imx93-clock.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/dt-bindings/clock/imx93-clock.h b/include/dt-bindings/clock/imx93-clock.h index 8e02859d8ce2d..35a1f62053a5c 100644 --- a/include/dt-bindings/clock/imx93-clock.h +++ b/include/dt-bindings/clock/imx93-clock.h @@ -199,6 +199,10 @@ #define IMX93_CLK_MU1_B_GATE 194 #define IMX93_CLK_MU2_A_GATE 195 #define IMX93_CLK_MU2_B_GATE 196 -#define IMX93_CLK_END 197 +#define IMX93_CLK_NIC_AXI 197 +#define IMX93_CLK_ARM_PLL 198 +#define IMX93_CLK_A55_SEL 199 +#define IMX93_CLK_A55_CORE 200 +#define IMX93_CLK_END 201 #endif From 6b60c3ae3e98d036945f2d5c11d35b4c178ea423 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 3 Apr 2023 17:53:00 +0800 Subject: [PATCH 20/25] clk: imx: imx93: Add nic and A55 clk The A55 clock logic as below: A55_PLL ----------------->\ A55_SEL-->A55_CORE A55_CCM_ROOT--->A55_GATE->/ Add A55 CPU clk to support freq change. Add NIC CLK to reflect the clk status Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230403095300.3386988-8-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx93.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c index de1ed1d8ba541..07b4a043e4495 100644 --- a/drivers/clk/imx/clk-imx93.c +++ b/drivers/clk/imx/clk-imx93.c @@ -33,6 +33,7 @@ static u32 share_count_sai2; static u32 share_count_sai3; static u32 share_count_mub; +static const char * const a55_core_sels[] = {"a55_alt", "arm_pll"}; static const char *parent_names[MAX_SEL][4] = { {"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "video_pll"}, {"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "sys_pll_pfd2_div2"}, @@ -55,7 +56,7 @@ static const struct imx93_clk_root { /* a55/m33/bus critical clk for system run */ { IMX93_CLK_A55_PERIPH, "a55_periph_root", 0x0000, FAST_SEL, CLK_IS_CRITICAL }, { IMX93_CLK_A55_MTR_BUS, "a55_mtr_bus_root", 0x0080, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, - { IMX93_CLK_A55, "a55_root", 0x0100, FAST_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_A55, "a55_alt_root", 0x0100, FAST_SEL, CLK_IS_CRITICAL }, { IMX93_CLK_M33, "m33_root", 0x0180, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, { IMX93_CLK_BUS_WAKEUP, "bus_wakeup_root", 0x0280, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, { IMX93_CLK_BUS_AON, "bus_aon_root", 0x0300, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, @@ -117,6 +118,7 @@ static const struct imx93_clk_root { { IMX93_CLK_HSIO_USB_TEST_60M, "hsio_usb_test_60m_root", 0x1f00, LOW_SPEED_IO_SEL, }, { IMX93_CLK_HSIO_ACSCAN_80M, "hsio_acscan_80m_root", 0x1f80, LOW_SPEED_IO_SEL, }, { IMX93_CLK_HSIO_ACSCAN_480M, "hsio_acscan_480m_root", 0x2000, MISC_SEL, }, + { IMX93_CLK_NIC_AXI, "nic_axi_root", 0x2080, FAST_SEL, CLK_IS_CRITICAL, }, { IMX93_CLK_ML_APB, "ml_apb_root", 0x2180, LOW_SPEED_IO_SEL, }, { IMX93_CLK_ML, "ml_root", 0x2200, FAST_SEL, }, { IMX93_CLK_MEDIA_AXI, "media_axi_root", 0x2280, FAST_SEL, }, @@ -153,7 +155,7 @@ static const struct imx93_clk_ccgr { unsigned long flags; u32 *shared_count; } ccgr_array[] = { - { IMX93_CLK_A55_GATE, "a55", "a55_root", 0x8000, }, + { IMX93_CLK_A55_GATE, "a55_alt", "a55_alt_root", 0x8000, }, /* M33 critical clk for system run */ { IMX93_CLK_CM33_GATE, "cm33", "m33_root", 0x8040, CLK_IS_CRITICAL }, { IMX93_CLK_ADC1_GATE, "adc1", "adc_root", 0x82c0, }, @@ -291,6 +293,9 @@ static int imx93_clocks_probe(struct platform_device *pdev) if (WARN_ON(!anatop_base)) return -ENOMEM; + clks[IMX93_CLK_ARM_PLL] = imx_clk_fracn_gppll_integer("arm_pll", "osc_24m", + anatop_base + 0x1000, + &imx_fracn_gppll_integer); clks[IMX93_CLK_AUDIO_PLL] = imx_clk_fracn_gppll("audio_pll", "osc_24m", anatop_base + 0x1200, &imx_fracn_gppll); clks[IMX93_CLK_VIDEO_PLL] = imx_clk_fracn_gppll("video_pll", "osc_24m", anatop_base + 0x1400, @@ -318,6 +323,14 @@ static int imx93_clocks_probe(struct platform_device *pdev) ccgr->shared_count); } + clks[IMX93_CLK_A55_SEL] = imx_clk_hw_mux2("a55_sel", base + 0x4820, 0, 1, a55_core_sels, + ARRAY_SIZE(a55_core_sels)); + clks[IMX93_CLK_A55_CORE] = imx_clk_hw_cpu("a55_core", "a55_sel", + clks[IMX93_CLK_A55_SEL]->clk, + clks[IMX93_CLK_A55_SEL]->clk, + clks[IMX93_CLK_ARM_PLL]->clk, + clks[IMX93_CLK_A55_GATE]->clk); + imx_check_clk_hws(clks, IMX93_CLK_END); ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); From d608c18018c897b88d66f1340fe274b7181817fa Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 31 Mar 2023 14:38:10 +0800 Subject: [PATCH 21/25] clk: imx: imx8ulp: Fix XBAR_DIVBUS and AD_SLOW clock parents XBAR_DIVBUS and AD_SLOW should set parent to XBAR_AD_DIVPLAT and XBAR_DIVBUS respectively, not the NIC_AD. otherwise we will get wrong clock rate. Fixes: c43a801a5789 ("clk: imx: Add clock driver for imx8ulp") Reviewed-by: Jacky Bai Signed-off-by: Ye Li Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230331063814.2462059-2-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx8ulp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c index a07df3b44703f..89121037a8f0e 100644 --- a/drivers/clk/imx/clk-imx8ulp.c +++ b/drivers/clk/imx/clk-imx8ulp.c @@ -200,8 +200,8 @@ static int imx8ulp_clk_cgc1_init(struct platform_device *pdev) clks[IMX8ULP_CLK_NIC_AD_DIVPLAT] = imx_clk_hw_divider_flags("nic_ad_divplat", "nic_sel", base + 0x34, 21, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); clks[IMX8ULP_CLK_NIC_PER_DIVPLAT] = imx_clk_hw_divider_flags("nic_per_divplat", "nic_ad_divplat", base + 0x34, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); clks[IMX8ULP_CLK_XBAR_AD_DIVPLAT] = imx_clk_hw_divider_flags("xbar_ad_divplat", "nic_ad_divplat", base + 0x38, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); - clks[IMX8ULP_CLK_XBAR_DIVBUS] = imx_clk_hw_divider_flags("xbar_divbus", "nic_ad_divplat", base + 0x38, 7, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); - clks[IMX8ULP_CLK_XBAR_AD_SLOW] = imx_clk_hw_divider_flags("xbar_ad_slow", "nic_ad_divplat", base + 0x38, 0, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_XBAR_DIVBUS] = imx_clk_hw_divider_flags("xbar_divbus", "xbar_ad_divplat", base + 0x38, 7, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_XBAR_AD_SLOW] = imx_clk_hw_divider_flags("xbar_ad_slow", "xbar_divbus", base + 0x38, 0, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); clks[IMX8ULP_CLK_SOSC_DIV1_GATE] = imx_clk_hw_gate_dis("sosc_div1_gate", "sosc", base + 0x108, 7); clks[IMX8ULP_CLK_SOSC_DIV2_GATE] = imx_clk_hw_gate_dis("sosc_div2_gate", "sosc", base + 0x108, 15); From 335aee51ffc72149ddf99755ba629f981f20e6b6 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Fri, 31 Mar 2023 14:38:11 +0800 Subject: [PATCH 22/25] clk: imx: imx8ulp: Add divider closest support to get more accurate clock rate If a divider's parent clock has fractional part, it will hard to round out a more accurate clock rate for this divider, add the 'CLK_DIVIDER_ROUND_CLOSEST' flags for such divider to get a more accurate clock rate. Reviewed-by: Peng Fan Signed-off-by: Jacky Bai Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230331063814.2462059-3-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx8ulp.c | 16 ++++++++-------- drivers/clk/imx/clk.h | 9 +++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c index 89121037a8f0e..3cf4b094dfff3 100644 --- a/drivers/clk/imx/clk-imx8ulp.c +++ b/drivers/clk/imx/clk-imx8ulp.c @@ -275,14 +275,14 @@ static int imx8ulp_clk_cgc2_init(struct platform_device *pdev) clks[IMX8ULP_CLK_PLL4_PFD2_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd2_div2_gate", "pll4_pfd2", base + 0x60c, 15); clks[IMX8ULP_CLK_PLL4_PFD3_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd3_div1_gate", "pll4_pfd3", base + 0x60c, 23); clks[IMX8ULP_CLK_PLL4_PFD3_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd3_div2_gate", "pll4_pfd3", base + 0x60c, 31); - clks[IMX8ULP_CLK_PLL4_PFD0_DIV1] = imx_clk_hw_divider("pll4_pfd0_div1", "pll4_pfd0_div1_gate", base + 0x608, 0, 6); - clks[IMX8ULP_CLK_PLL4_PFD0_DIV2] = imx_clk_hw_divider("pll4_pfd0_div2", "pll4_pfd0_div2_gate", base + 0x608, 8, 6); - clks[IMX8ULP_CLK_PLL4_PFD1_DIV1] = imx_clk_hw_divider("pll4_pfd1_div1", "pll4_pfd1_div1_gate", base + 0x608, 16, 6); - clks[IMX8ULP_CLK_PLL4_PFD1_DIV2] = imx_clk_hw_divider("pll4_pfd1_div2", "pll4_pfd1_div2_gate", base + 0x608, 24, 6); - clks[IMX8ULP_CLK_PLL4_PFD2_DIV1] = imx_clk_hw_divider("pll4_pfd2_div1", "pll4_pfd2_div1_gate", base + 0x60c, 0, 6); - clks[IMX8ULP_CLK_PLL4_PFD2_DIV2] = imx_clk_hw_divider("pll4_pfd2_div2", "pll4_pfd2_div2_gate", base + 0x60c, 8, 6); - clks[IMX8ULP_CLK_PLL4_PFD3_DIV1] = imx_clk_hw_divider("pll4_pfd3_div1", "pll4_pfd3_div1_gate", base + 0x60c, 16, 6); - clks[IMX8ULP_CLK_PLL4_PFD3_DIV2] = imx_clk_hw_divider("pll4_pfd3_div2", "pll4_pfd3_div2_gate", base + 0x60c, 24, 6); + clks[IMX8ULP_CLK_PLL4_PFD0_DIV1] = imx_clk_hw_divider_closest("pll4_pfd0_div1", "pll4_pfd0_div1_gate", base + 0x608, 0, 6); + clks[IMX8ULP_CLK_PLL4_PFD0_DIV2] = imx_clk_hw_divider_closest("pll4_pfd0_div2", "pll4_pfd0_div2_gate", base + 0x608, 8, 6); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV1] = imx_clk_hw_divider_closest("pll4_pfd1_div1", "pll4_pfd1_div1_gate", base + 0x608, 16, 6); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV2] = imx_clk_hw_divider_closest("pll4_pfd1_div2", "pll4_pfd1_div2_gate", base + 0x608, 24, 6); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV1] = imx_clk_hw_divider_closest("pll4_pfd2_div1", "pll4_pfd2_div1_gate", base + 0x60c, 0, 6); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV2] = imx_clk_hw_divider_closest("pll4_pfd2_div2", "pll4_pfd2_div2_gate", base + 0x60c, 8, 6); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV1] = imx_clk_hw_divider_closest("pll4_pfd3_div1", "pll4_pfd3_div1_gate", base + 0x60c, 16, 6); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV2] = imx_clk_hw_divider_closest("pll4_pfd3_div2", "pll4_pfd3_div2_gate", base + 0x60c, 24, 6); clks[IMX8ULP_CLK_CGC2_SOSC_DIV1_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div1_gate", "sosc", base + 0x108, 7); clks[IMX8ULP_CLK_CGC2_SOSC_DIV2_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div2_gate", "sosc", base + 0x108, 15); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index c8db0c8d4c5da..1031468701d7f 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -353,6 +353,15 @@ static inline struct clk_hw *imx_clk_hw_fixed_factor(const char *name, CLK_SET_RATE_PARENT, mult, div); } +static inline struct clk_hw *imx_clk_hw_divider_closest(const char *name, + const char *parent, + void __iomem *reg, u8 shift, + u8 width) +{ + return clk_hw_register_divider(NULL, name, parent, 0, + reg, shift, width, CLK_DIVIDER_ROUND_CLOSEST, &imx_ccm_lock); +} + static inline struct clk_hw *__imx_clk_hw_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, From 4883200d8c0b20cc3bf90fcd3b837a344a31ac66 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Fri, 31 Mar 2023 14:38:12 +0800 Subject: [PATCH 23/25] clk: imx: imx8ulp: keep MU0_B clock enabled always Keep the A35<->M33 MU0_B clock enabled always for low power communication. Reviewed-by: Peng Fan Signed-off-by: Jacky Bai Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230331063814.2462059-4-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx8ulp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c index 3cf4b094dfff3..0dd48e8159ee9 100644 --- a/drivers/clk/imx/clk-imx8ulp.c +++ b/drivers/clk/imx/clk-imx8ulp.c @@ -376,7 +376,7 @@ static int imx8ulp_clk_pcc3_init(struct platform_device *pdev) clks[IMX8ULP_CLK_DMA1_CH29] = imx_clk_hw_gate("pcc_dma1_ch29", "xbar_ad_divplat", base + 0x7c, 30); clks[IMX8ULP_CLK_DMA1_CH30] = imx_clk_hw_gate("pcc_dma1_ch30", "xbar_ad_divplat", base + 0x80, 30); clks[IMX8ULP_CLK_DMA1_CH31] = imx_clk_hw_gate("pcc_dma1_ch31", "xbar_ad_divplat", base + 0x84, 30); - clks[IMX8ULP_CLK_MU0_B] = imx_clk_hw_gate("mu0_b", "xbar_ad_divplat", base + 0x88, 30); + clks[IMX8ULP_CLK_MU0_B] = imx_clk_hw_gate_flags("mu0_b", "xbar_ad_divplat", base + 0x88, 30, CLK_IS_CRITICAL); clks[IMX8ULP_CLK_MU3_A] = imx_clk_hw_gate("mu3_a", "xbar_ad_divplat", base + 0x8c, 30); imx_check_clk_hws(clks, clk_data->num); From 66d72c62d20eb571b7ab624813b1b98b626ab493 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Fri, 31 Mar 2023 14:38:13 +0800 Subject: [PATCH 24/25] clk: imx: imx8ulp: Add tpm5 clock as critical gate clock The TPM5 is used for broadcast timer purpose and registered with TIMER_OF_DECLARE. As the clock driver is not ready at that stage, so the TPM5 clock is configured in bootloader(TF-A). if we just remove the TPM5 clock from linux will introduce a risk that the TPM5's parent clock will be gated, then lead to TPM's channel control config can NOT be written into register successfully. Due to the above reason, we still need to add the TPM5 clock into linux clock but register it as a simple critical gate clock to make sure its parent is always on. Reviewed-by: Peng Fan Signed-off-by: Jacky Bai Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230331063814.2462059-5-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx8ulp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c index 0dd48e8159ee9..6a8a9e50d826c 100644 --- a/drivers/clk/imx/clk-imx8ulp.c +++ b/drivers/clk/imx/clk-imx8ulp.c @@ -333,7 +333,6 @@ static int imx8ulp_clk_pcc3_init(struct platform_device *pdev) clks[IMX8ULP_CLK_WDOG4] = imx8ulp_clk_hw_composite("wdog4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xac, 1); clks[IMX8ULP_CLK_LPIT1] = imx8ulp_clk_hw_composite("lpit1", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xc8, 1); clks[IMX8ULP_CLK_TPM4] = imx8ulp_clk_hw_composite("tpm4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xcc, 1); - clks[IMX8ULP_CLK_TPM5] = imx8ulp_clk_hw_composite("tpm5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd0, 1); clks[IMX8ULP_CLK_FLEXIO1] = imx8ulp_clk_hw_composite("flexio1", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd4, 1); clks[IMX8ULP_CLK_I3C2] = imx8ulp_clk_hw_composite("i3c2", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd8, 1); clks[IMX8ULP_CLK_LPI2C4] = imx8ulp_clk_hw_composite("lpi2c4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xdc, 1); @@ -378,6 +377,7 @@ static int imx8ulp_clk_pcc3_init(struct platform_device *pdev) clks[IMX8ULP_CLK_DMA1_CH31] = imx_clk_hw_gate("pcc_dma1_ch31", "xbar_ad_divplat", base + 0x84, 30); clks[IMX8ULP_CLK_MU0_B] = imx_clk_hw_gate_flags("mu0_b", "xbar_ad_divplat", base + 0x88, 30, CLK_IS_CRITICAL); clks[IMX8ULP_CLK_MU3_A] = imx_clk_hw_gate("mu3_a", "xbar_ad_divplat", base + 0x8c, 30); + clks[IMX8ULP_CLK_TPM5] = imx_clk_hw_gate_flags("tpm5", "sosc_div2", base + 0xd0, 30, CLK_IS_CRITICAL); imx_check_clk_hws(clks, clk_data->num); From 8a05f5cccdbe851265bf513643ada48c26b1267f Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Fri, 31 Mar 2023 14:38:14 +0800 Subject: [PATCH 25/25] clk: imx: imx8ulp: update clk flag for system critical clock In order to support bus fabric clock frequency changed on the fly, need to update some bus clocks'flags to make sure these clocks'frequency and parent can be changed on the fly. For these clocks, HW can make sure no glitch will be introduced when changing on the fly. In order to support DDR DFS, the HW register bit for DDR_SEL and DDR_DIV clock will be modified by TF-A. So need to update these two clock's flag to make sure that the linux kernel side can correct these clocks' SW state to reflect the actual HW state. Reviewed-by: Ye Li Signed-off-by: Jacky Bai Signed-off-by: Peng Fan Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230331063814.2462059-6-peng.fan@oss.nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx8ulp.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c index 6a8a9e50d826c..e308c88cb801c 100644 --- a/drivers/clk/imx/clk-imx8ulp.c +++ b/drivers/clk/imx/clk-imx8ulp.c @@ -198,10 +198,10 @@ static int imx8ulp_clk_cgc1_init(struct platform_device *pdev) clks[IMX8ULP_CLK_NIC_SEL] = imx_clk_hw_mux2("nic_sel", base + 0x34, 28, 2, nic_sels, ARRAY_SIZE(nic_sels)); clks[IMX8ULP_CLK_NIC_AD_DIVPLAT] = imx_clk_hw_divider_flags("nic_ad_divplat", "nic_sel", base + 0x34, 21, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); - clks[IMX8ULP_CLK_NIC_PER_DIVPLAT] = imx_clk_hw_divider_flags("nic_per_divplat", "nic_ad_divplat", base + 0x34, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); - clks[IMX8ULP_CLK_XBAR_AD_DIVPLAT] = imx_clk_hw_divider_flags("xbar_ad_divplat", "nic_ad_divplat", base + 0x38, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); - clks[IMX8ULP_CLK_XBAR_DIVBUS] = imx_clk_hw_divider_flags("xbar_divbus", "xbar_ad_divplat", base + 0x38, 7, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); - clks[IMX8ULP_CLK_XBAR_AD_SLOW] = imx_clk_hw_divider_flags("xbar_ad_slow", "xbar_divbus", base + 0x38, 0, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_NIC_PER_DIVPLAT] = imx_clk_hw_divider_flags("nic_per_divplat", "nic_ad_divplat", base + 0x34, 14, 6, CLK_SET_RATE_PARENT); + clks[IMX8ULP_CLK_XBAR_AD_DIVPLAT] = imx_clk_hw_divider_flags("xbar_ad_divplat", "nic_ad_divplat", base + 0x38, 14, 6, CLK_SET_RATE_PARENT); + clks[IMX8ULP_CLK_XBAR_DIVBUS] = imx_clk_hw_divider_flags("xbar_divbus", "xbar_ad_divplat", base + 0x38, 7, 6, CLK_SET_RATE_PARENT); + clks[IMX8ULP_CLK_XBAR_AD_SLOW] = imx_clk_hw_divider_flags("xbar_ad_slow", "xbar_divbus", base + 0x38, 0, 6, CLK_SET_RATE_PARENT); clks[IMX8ULP_CLK_SOSC_DIV1_GATE] = imx_clk_hw_gate_dis("sosc_div1_gate", "sosc", base + 0x108, 7); clks[IMX8ULP_CLK_SOSC_DIV2_GATE] = imx_clk_hw_gate_dis("sosc_div2_gate", "sosc", base + 0x108, 15); @@ -255,9 +255,9 @@ static int imx8ulp_clk_cgc2_init(struct platform_device *pdev) clks[IMX8ULP_CLK_HIFI_DIVCORE] = imx_clk_hw_divider("hifi_core_div", "hifi_sel", base + 0x14, 21, 6); clks[IMX8ULP_CLK_HIFI_DIVPLAT] = imx_clk_hw_divider("hifi_plat_div", "hifi_core_div", base + 0x14, 14, 6); - clks[IMX8ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x40, 28, 3, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_PARENT_GATE); - clks[IMX8ULP_CLK_DDR_DIV] = imx_clk_hw_divider_flags("ddr_div", "ddr_sel", base + 0x40, 21, 6, CLK_IS_CRITICAL); - clks[IMX8ULP_CLK_LPAV_AXI_SEL] = imx_clk_hw_mux("lpav_sel", base + 0x3c, 28, 2, lpav_sels, ARRAY_SIZE(lpav_sels)); + clks[IMX8ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x40, 28, 3, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_GET_RATE_NOCACHE); + clks[IMX8ULP_CLK_DDR_DIV] = imx_clk_hw_divider_flags("ddr_div", "ddr_sel", base + 0x40, 21, 6, CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE); + clks[IMX8ULP_CLK_LPAV_AXI_SEL] = imx_clk_hw_mux2("lpav_sel", base + 0x3c, 28, 2, lpav_sels, ARRAY_SIZE(lpav_sels)); clks[IMX8ULP_CLK_LPAV_AXI_DIV] = imx_clk_hw_divider_flags("lpav_axi_div", "lpav_sel", base + 0x3c, 21, 6, CLK_IS_CRITICAL); clks[IMX8ULP_CLK_LPAV_AHB_DIV] = imx_clk_hw_divider_flags("lpav_ahb_div", "lpav_axi_div", base + 0x3c, 14, 6, CLK_IS_CRITICAL); clks[IMX8ULP_CLK_LPAV_BUS_DIV] = imx_clk_hw_divider_flags("lpav_bus_div", "lpav_axi_div", base + 0x3c, 7, 6, CLK_IS_CRITICAL);