Message ID | 20230517070215.28463-4-yu.tu@amlogic.com (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
Series | Add S4 SoC PLL and Peripheral clock | expand |
On Wed 17 May 2023 at 15:02, Yu Tu <yu.tu@amlogic.com> wrote: > Add the S4 PLL clock controller driver in the s4 SoC family. > > Signed-off-by: Yu Tu <yu.tu@amlogic.com> > --- > drivers/clk/meson/Kconfig | 13 + > drivers/clk/meson/Makefile | 1 + > drivers/clk/meson/s4-pll.c | 907 +++++++++++++++++++++++++++++++++++++ > drivers/clk/meson/s4-pll.h | 87 ++++ > 4 files changed, 1008 insertions(+) > create mode 100644 drivers/clk/meson/s4-pll.c > create mode 100644 drivers/clk/meson/s4-pll.h > > diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig > index fc002c155bc3..a663c90a3f3b 100644 > --- a/drivers/clk/meson/Kconfig > +++ b/drivers/clk/meson/Kconfig > @@ -115,4 +115,17 @@ config COMMON_CLK_G12A > help > Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2 > devices, aka g12a. Say Y if you want peripherals to work. > + > +config COMMON_CLK_S4_PLL > + tristate "S4 SoC PLL clock controllers support" > + depends on ARM64 > + default y > + select COMMON_CLK_MESON_MPLL > + select COMMON_CLK_MESON_PLL > + select COMMON_CLK_MESON_REGMAP > + help > + Support for the pll clock controller on Amlogic S805X2 and S905Y4 devices, > + aka s4. Amlogic S805X2 and S905Y4 devices include AQ222 and AQ229. > + Say Y if you want the board to work, because plls are the parent of most > + peripherals. > endmenu > diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile > index 6eca2a406ee3..376f49cc13f1 100644 > --- a/drivers/clk/meson/Makefile > +++ b/drivers/clk/meson/Makefile > @@ -19,3 +19,4 @@ obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o > obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o > obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o > obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o > +obj-$(CONFIG_COMMON_CLK_S4_PLL) += s4-pll.o > diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c > new file mode 100644 > index 000000000000..c70a33586bcd > --- /dev/null > +++ b/drivers/clk/meson/s4-pll.c > @@ -0,0 +1,907 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Amlogic Meson-S4 PLL Clock Controller Driver > + * > + * Copyright (c) 2021 Amlogic, inc. > + * Author: Yu Tu <yu.tu@amlogic.com> > + */ > + > +#include <linux/clk-provider.h> > +#include <linux/of_device.h> > +#include <linux/platform_device.h> > + > +#include "clk-mpll.h" > +#include "clk-pll.h" > +#include "clk-regmap.h" > +#include "s4-pll.h" > + > +static DEFINE_SPINLOCK(meson_clk_lock); > + > +static struct clk_regmap s4_fixed_pll_dco = { > + .data = &(struct meson_clk_pll_data){ > + .en = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 28, > + .width = 1, > + }, > + .m = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 0, > + .width = 8, > + }, > + .n = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 10, > + .width = 5, > + }, > + .frac = { > + .reg_off = ANACTRL_FIXPLL_CTRL1, > + .shift = 0, > + .width = 17, > + }, > + .l = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 31, > + .width = 1, > + }, > + .rst = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 29, > + .width = 1, > + }, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fixed_pll_dco", > + /* > + * This clock is a fixed value (4GHz) that is initialized by ROMcode. Really, isn't it the bl2 doing that ? > + * This clock won't ever change at runtime. > + * The chip design determines that this clock cannot be changed after > + * initialization. To prevent system crash caused by changing > + * fixed related register in kernel phase. This register is not > + * writable in the kernel phase. > + * So we use ro_ops. > + */ This explanation is good but we don't need mutliple copy/paste or "please refer" for the same thing. That is very verbose (I think I already commented on this previously) Just make one comment block, that clearly explain your intent and that it applies to the fixed_pll group > + .ops = &meson_clk_pll_ro_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", } > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fixed_pll = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_FIXPLL_CTRL0, > + .shift = 16, > + .width = 2, > + .flags = CLK_DIVIDER_POWER_OF_TWO, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fixed_pll", > + /* > + * This clock is a fixed value (2GHz) that is initialized by ROMcode. > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_divider_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fixed_pll_dco.hw > + }, > + .num_parents = 1, > + /* > + * This clock won't ever change at runtime so > + * CLK_SET_RATE_PARENT is not required > + */ > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div2_div = { > + .mult = 1, > + .div = 2, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div2_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div2 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 24, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div2", > + /* > + * This clock is a fixed value (1GHz) that is initialized by ROMcode. > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div2_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div3_div = { > + .mult = 1, > + .div = 3, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div3_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div3 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 20, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div3", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div3_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div4_div = { > + .mult = 1, > + .div = 4, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div4_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div4 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 21, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div4", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ While div2 and div3 got an explanation from previous SoCs, they others - like div4/div7/etc ... - have been able to cope with rw ops so far. Why is the S4 different for all these clocks ? Requiring RO ops (or fishy clock flags) is usually a sign that a clock is used without an appropriate driver. > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div4_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div5_div = { > + .mult = 1, > + .div = 5, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div5_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div5 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 22, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div5", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div5_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div7_div = { > + .mult = 1, > + .div = 7, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div7_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div7 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 23, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div7", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div7_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div2p5_div = { > + .mult = 2, > + .div = 5, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div2p5_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fixed_pll.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div2p5 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 25, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div2p5", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div2p5_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static const struct pll_mult_range s4_gp0_pll_mult_range = { > + .min = 125, > + .max = 250, > +}; > + > +/* > + * Internal gp0 pll emulation configuration parameters > + */ > +static const struct reg_sequence s4_gp0_init_regs[] = { > + { .reg = ANACTRL_GP0PLL_CTRL1, .def = 0x00000000 }, > + { .reg = ANACTRL_GP0PLL_CTRL2, .def = 0x00000000 }, > + { .reg = ANACTRL_GP0PLL_CTRL3, .def = 0x48681c00 }, > + { .reg = ANACTRL_GP0PLL_CTRL4, .def = 0x88770290 }, > + { .reg = ANACTRL_GP0PLL_CTRL5, .def = 0x39272000 }, > + { .reg = ANACTRL_GP0PLL_CTRL6, .def = 0x56540000 } > +}; > + > +static struct clk_regmap s4_gp0_pll_dco = { > + .data = &(struct meson_clk_pll_data){ > + .en = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 28, > + .width = 1, > + }, > + .m = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 0, > + .width = 8, > + }, > + .n = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 10, > + .width = 5, > + }, > + .frac = { > + .reg_off = ANACTRL_GP0PLL_CTRL1, > + .shift = 0, > + .width = 17, > + }, > + .l = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 31, > + .width = 1, > + }, > + .rst = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 29, > + .width = 1, > + }, > + .range = &s4_gp0_pll_mult_range, > + .init_regs = s4_gp0_init_regs, > + .init_count = ARRAY_SIZE(s4_gp0_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "gp0_pll_dco", > + .ops = &meson_clk_pll_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", } > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_gp0_pll = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_GP0PLL_CTRL0, > + .shift = 16, > + .width = 3, > + .flags = (CLK_DIVIDER_POWER_OF_TWO | > + CLK_DIVIDER_ROUND_CLOSEST), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "gp0_pll", > + .ops = &clk_regmap_divider_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_gp0_pll_dco.hw > + }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +/* > + * Internal hifi pll emulation configuration parameters > + */ > +static const struct reg_sequence s4_hifi_init_regs[] = { > + { .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x00010e56 }, > + { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00000000 }, > + { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x6a285c00 }, > + { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x65771290 }, > + { .reg = ANACTRL_HIFIPLL_CTRL5, .def = 0x39272000 }, > + { .reg = ANACTRL_HIFIPLL_CTRL6, .def = 0x56540000 } > +}; > + > +static struct clk_regmap s4_hifi_pll_dco = { > + .data = &(struct meson_clk_pll_data){ > + .en = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 28, > + .width = 1, > + }, > + .m = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 0, > + .width = 8, > + }, > + .n = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 10, > + .width = 5, > + }, > + .frac = { > + .reg_off = ANACTRL_HIFIPLL_CTRL1, > + .shift = 0, > + .width = 17, > + }, > + .l = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 31, > + .width = 1, > + }, > + .rst = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 29, > + .width = 1, > + }, > + .range = &s4_gp0_pll_mult_range, > + .init_regs = s4_hifi_init_regs, > + .init_count = ARRAY_SIZE(s4_hifi_init_regs), > + .flags = CLK_MESON_PLL_ROUND_CLOSEST, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hifi_pll_dco", > + .ops = &meson_clk_pll_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", } > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_hifi_pll = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_HIFIPLL_CTRL0, > + .shift = 16, > + .width = 2, > + .flags = (CLK_DIVIDER_POWER_OF_TWO | > + CLK_DIVIDER_ROUND_CLOSEST), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hifi_pll", > + .ops = &clk_regmap_divider_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_hifi_pll_dco.hw > + }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static struct clk_regmap s4_hdmi_pll_dco = { > + .data = &(struct meson_clk_pll_data){ > + .en = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 28, > + .width = 1, > + }, > + .m = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 0, > + .width = 8, > + }, > + .n = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 10, > + .width = 5, > + }, > + .frac = { > + .reg_off = ANACTRL_HDMIPLL_CTRL1, > + .shift = 0, > + .width = 17, > + }, > + .l = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 31, > + .width = 1, > + }, > + .rst = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 29, > + .width = 1, > + }, > + .range = &s4_gp0_pll_mult_range, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hdmi_pll_dco", > + .ops = &meson_clk_pll_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", } > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_hdmi_pll_od = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_HDMIPLL_CTRL0, > + .shift = 16, > + .width = 4, > + .flags = CLK_DIVIDER_POWER_OF_TWO, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hdmi_pll_od", > + .ops = &clk_regmap_divider_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_hdmi_pll_dco.hw > + }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static struct clk_regmap s4_hdmi_pll = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_HDMIPLL_CTRL0, > + .shift = 20, > + .width = 2, > + .flags = CLK_DIVIDER_POWER_OF_TWO, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hdmi_pll", > + .ops = &clk_regmap_divider_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_hdmi_pll_od.hw > + }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static struct clk_fixed_factor s4_mpll_50m_div = { > + .mult = 1, > + .div = 80, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll_50m_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fixed_pll_dco.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll_50m = { > + .data = &(struct clk_regmap_mux_data){ > + .offset = ANACTRL_FIXPLL_CTRL3, > + .mask = 0x1, > + .shift = 5, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll_50m", > + .ops = &clk_regmap_mux_ro_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", }, > + { .hw = &s4_mpll_50m_div.hw }, > + }, > + .num_parents = 2, > + }, > +}; > + > +static struct clk_fixed_factor s4_mpll_prediv = { > + .mult = 1, > + .div = 2, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll_prediv", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fixed_pll_dco.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static const struct reg_sequence s4_mpll0_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL2, .def = 0x40000033 } > +}; > + > +static struct clk_regmap s4_mpll0_div = { > + .data = &(struct meson_clk_mpll_data){ > + .sdm = { > + .reg_off = ANACTRL_MPLL_CTRL1, > + .shift = 0, > + .width = 14, > + }, > + .sdm_en = { > + .reg_off = ANACTRL_MPLL_CTRL1, > + .shift = 30, > + .width = 1, > + }, > + .n2 = { > + .reg_off = ANACTRL_MPLL_CTRL1, > + .shift = 20, > + .width = 9, > + }, > + .ssen = { > + .reg_off = ANACTRL_MPLL_CTRL1, > + .shift = 29, > + .width = 1, > + }, > + .lock = &meson_clk_lock, > + .init_regs = s4_mpll0_init_regs, > + .init_count = ARRAY_SIZE(s4_mpll0_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll0_div", > + .ops = &meson_clk_mpll_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_mpll_prediv.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll0 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_MPLL_CTRL1, > + .bit_idx = 31, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll0", > + .ops = &clk_regmap_gate_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_mpll0_div.hw }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static const struct reg_sequence s4_mpll1_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL4, .def = 0x40000033 } > +}; > + > +static struct clk_regmap s4_mpll1_div = { > + .data = &(struct meson_clk_mpll_data){ > + .sdm = { > + .reg_off = ANACTRL_MPLL_CTRL3, > + .shift = 0, > + .width = 14, > + }, > + .sdm_en = { > + .reg_off = ANACTRL_MPLL_CTRL3, > + .shift = 30, > + .width = 1, > + }, > + .n2 = { > + .reg_off = ANACTRL_MPLL_CTRL3, > + .shift = 20, > + .width = 9, > + }, > + .ssen = { > + .reg_off = ANACTRL_MPLL_CTRL3, > + .shift = 29, > + .width = 1, > + }, > + .lock = &meson_clk_lock, > + .init_regs = s4_mpll1_init_regs, > + .init_count = ARRAY_SIZE(s4_mpll1_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll1_div", > + .ops = &meson_clk_mpll_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_mpll_prediv.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll1 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_MPLL_CTRL3, > + .bit_idx = 31, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll1", > + .ops = &clk_regmap_gate_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_mpll1_div.hw }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static const struct reg_sequence s4_mpll2_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL6, .def = 0x40000033 } > +}; > + > +static struct clk_regmap s4_mpll2_div = { > + .data = &(struct meson_clk_mpll_data){ > + .sdm = { > + .reg_off = ANACTRL_MPLL_CTRL5, > + .shift = 0, > + .width = 14, > + }, > + .sdm_en = { > + .reg_off = ANACTRL_MPLL_CTRL5, > + .shift = 30, > + .width = 1, > + }, > + .n2 = { > + .reg_off = ANACTRL_MPLL_CTRL5, > + .shift = 20, > + .width = 9, > + }, > + .ssen = { > + .reg_off = ANACTRL_MPLL_CTRL5, > + .shift = 29, > + .width = 1, > + }, > + .lock = &meson_clk_lock, > + .init_regs = s4_mpll2_init_regs, > + .init_count = ARRAY_SIZE(s4_mpll2_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll2_div", > + .ops = &meson_clk_mpll_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_mpll_prediv.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll2 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_MPLL_CTRL5, > + .bit_idx = 31, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll2", > + .ops = &clk_regmap_gate_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_mpll2_div.hw }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static const struct reg_sequence s4_mpll3_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL8, .def = 0x40000033 } > +}; > + > +static struct clk_regmap s4_mpll3_div = { > + .data = &(struct meson_clk_mpll_data){ > + .sdm = { > + .reg_off = ANACTRL_MPLL_CTRL7, > + .shift = 0, > + .width = 14, > + }, > + .sdm_en = { > + .reg_off = ANACTRL_MPLL_CTRL7, > + .shift = 30, > + .width = 1, > + }, > + .n2 = { > + .reg_off = ANACTRL_MPLL_CTRL7, > + .shift = 20, > + .width = 9, > + }, > + .ssen = { > + .reg_off = ANACTRL_MPLL_CTRL7, > + .shift = 29, > + .width = 1, > + }, > + .lock = &meson_clk_lock, > + .init_regs = s4_mpll3_init_regs, > + .init_count = ARRAY_SIZE(s4_mpll3_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll3_div", > + .ops = &meson_clk_mpll_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_mpll_prediv.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll3 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_MPLL_CTRL7, > + .bit_idx = 31, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll3", > + .ops = &clk_regmap_gate_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_mpll3_div.hw }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +/* Array of all clocks provided by this provider */ > +static struct clk_hw_onecell_data s4_pll_hw_onecell_data = { > + .hws = { > + [CLKID_FIXED_PLL_DCO] = &s4_fixed_pll_dco.hw, > + [CLKID_FIXED_PLL] = &s4_fixed_pll.hw, > + [CLKID_FCLK_DIV2_DIV] = &s4_fclk_div2_div.hw, > + [CLKID_FCLK_DIV2] = &s4_fclk_div2.hw, > + [CLKID_FCLK_DIV3_DIV] = &s4_fclk_div3_div.hw, > + [CLKID_FCLK_DIV3] = &s4_fclk_div3.hw, > + [CLKID_FCLK_DIV4_DIV] = &s4_fclk_div4_div.hw, > + [CLKID_FCLK_DIV4] = &s4_fclk_div4.hw, > + [CLKID_FCLK_DIV5_DIV] = &s4_fclk_div5_div.hw, > + [CLKID_FCLK_DIV5] = &s4_fclk_div5.hw, > + [CLKID_FCLK_DIV7_DIV] = &s4_fclk_div7_div.hw, > + [CLKID_FCLK_DIV7] = &s4_fclk_div7.hw, > + [CLKID_FCLK_DIV2P5_DIV] = &s4_fclk_div2p5_div.hw, > + [CLKID_FCLK_DIV2P5] = &s4_fclk_div2p5.hw, > + [CLKID_GP0_PLL_DCO] = &s4_gp0_pll_dco.hw, > + [CLKID_GP0_PLL] = &s4_gp0_pll.hw, > + [CLKID_HIFI_PLL_DCO] = &s4_hifi_pll_dco.hw, > + [CLKID_HIFI_PLL] = &s4_hifi_pll.hw, > + [CLKID_HDMI_PLL_DCO] = &s4_hdmi_pll_dco.hw, > + [CLKID_HDMI_PLL_OD] = &s4_hdmi_pll_od.hw, > + [CLKID_HDMI_PLL] = &s4_hdmi_pll.hw, > + [CLKID_MPLL_50M_DIV] = &s4_mpll_50m_div.hw, > + [CLKID_MPLL_50M] = &s4_mpll_50m.hw, > + [CLKID_MPLL_PREDIV] = &s4_mpll_prediv.hw, > + [CLKID_MPLL0_DIV] = &s4_mpll0_div.hw, > + [CLKID_MPLL0] = &s4_mpll0.hw, > + [CLKID_MPLL1_DIV] = &s4_mpll1_div.hw, > + [CLKID_MPLL1] = &s4_mpll1.hw, > + [CLKID_MPLL2_DIV] = &s4_mpll2_div.hw, > + [CLKID_MPLL2] = &s4_mpll2.hw, > + [CLKID_MPLL3_DIV] = &s4_mpll3_div.hw, > + [CLKID_MPLL3] = &s4_mpll3.hw, > + [NR_PLL_CLKS] = NULL > + }, > + .num = NR_PLL_CLKS, > +}; > + > +static struct clk_regmap *const s4_pll_clk_regmaps[] = { > + &s4_fixed_pll_dco, > + &s4_fixed_pll, > + &s4_fclk_div2, > + &s4_fclk_div3, > + &s4_fclk_div4, > + &s4_fclk_div5, > + &s4_fclk_div7, > + &s4_fclk_div2p5, > + &s4_gp0_pll_dco, > + &s4_gp0_pll, > + &s4_hifi_pll_dco, > + &s4_hifi_pll, > + &s4_hdmi_pll_dco, > + &s4_hdmi_pll_od, > + &s4_hdmi_pll, > + &s4_mpll_50m, > + &s4_mpll0_div, > + &s4_mpll0, > + &s4_mpll1_div, > + &s4_mpll1, > + &s4_mpll2_div, > + &s4_mpll2, > + &s4_mpll3_div, > + &s4_mpll3, > +}; > + > +static const struct reg_sequence s4_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL0, .def = 0x00000543 }, > +}; > + > +static struct regmap_config clkc_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > +}; > + > +static int meson_s4_pll_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct regmap *regmap; > + void __iomem *base; > + int ret, i; > + > + base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(base)) > + return PTR_ERR(base); > + > + regmap = devm_regmap_init_mmio(dev, base, &clkc_regmap_config); > + if (IS_ERR(regmap)) > + return PTR_ERR(regmap); > + > + ret = regmap_multi_reg_write(regmap, s4_init_regs, ARRAY_SIZE(s4_init_regs)); > + if (ret) { > + dev_err(dev, "Failed to init registers\n"); > + return ret; > + } > + > + /* Populate regmap for the regmap backed clocks */ > + for (i = 0; i < ARRAY_SIZE(s4_pll_clk_regmaps); i++) > + s4_pll_clk_regmaps[i]->map = regmap; > + > + for (i = 0; i < s4_pll_hw_onecell_data.num; i++) { > + /* array might be sparse */ > + if (!s4_pll_hw_onecell_data.hws[i]) > + continue; > + > + ret = devm_clk_hw_register(dev, s4_pll_hw_onecell_data.hws[i]); > + if (ret) { > + dev_err(dev, "Clock registration failed\n"); > + return ret; > + } > + } > + > + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, > + &s4_pll_hw_onecell_data); > +} > + > +static const struct of_device_id clkc_match_table[] = { > + { > + .compatible = "amlogic,s4-pll-clkc", > + }, > + {} > +}; > + > +static struct platform_driver s4_driver = { > + .probe = meson_s4_pll_probe, > + .driver = { > + .name = "s4-pll-clkc", > + .of_match_table = clkc_match_table, > + }, > +}; > + > +module_platform_driver(s4_driver); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/clk/meson/s4-pll.h b/drivers/clk/meson/s4-pll.h > new file mode 100644 > index 000000000000..021d9f8b2b30 > --- /dev/null > +++ b/drivers/clk/meson/s4-pll.h > @@ -0,0 +1,87 @@ > +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ > +/* > + * Copyright (c) 2021 Amlogic, inc. > + * Author: Yu Tu <yu.tu@amlogic.com> > + */ > + > +#ifndef __MESON_S4_PLL_H__ > +#define __MESON_S4_PLL_H__ > + > +/* ANA_CTRL - Registers */ > + > +#define ANACTRL_FIXPLL_CTRL0 0x040 > +#define ANACTRL_FIXPLL_CTRL1 0x044 > +#define ANACTRL_FIXPLL_CTRL2 0x048 > +#define ANACTRL_FIXPLL_CTRL3 0x04c > +#define ANACTRL_FIXPLL_CTRL4 0x050 > +#define ANACTRL_FIXPLL_CTRL5 0x054 > +#define ANACTRL_FIXPLL_CTRL6 0x058 > +#define ANACTRL_FIXPLL_STS 0x05c > +#define ANACTRL_GP0PLL_CTRL0 0x080 > +#define ANACTRL_GP0PLL_CTRL1 0x084 > +#define ANACTRL_GP0PLL_CTRL2 0x088 > +#define ANACTRL_GP0PLL_CTRL3 0x08c > +#define ANACTRL_GP0PLL_CTRL4 0x090 > +#define ANACTRL_GP0PLL_CTRL5 0x094 > +#define ANACTRL_GP0PLL_CTRL6 0x098 > +#define ANACTRL_GP0PLL_STS 0x09c > +#define ANACTRL_HIFIPLL_CTRL0 0x100 > +#define ANACTRL_HIFIPLL_CTRL1 0x104 > +#define ANACTRL_HIFIPLL_CTRL2 0x108 > +#define ANACTRL_HIFIPLL_CTRL3 0x10c > +#define ANACTRL_HIFIPLL_CTRL4 0x110 > +#define ANACTRL_HIFIPLL_CTRL5 0x114 > +#define ANACTRL_HIFIPLL_CTRL6 0x118 > +#define ANACTRL_HIFIPLL_STS 0x11c > +#define ANACTRL_MPLL_CTRL0 0x180 > +#define ANACTRL_MPLL_CTRL1 0x184 > +#define ANACTRL_MPLL_CTRL2 0x188 > +#define ANACTRL_MPLL_CTRL3 0x18c > +#define ANACTRL_MPLL_CTRL4 0x190 > +#define ANACTRL_MPLL_CTRL5 0x194 > +#define ANACTRL_MPLL_CTRL6 0x198 > +#define ANACTRL_MPLL_CTRL7 0x19c > +#define ANACTRL_MPLL_CTRL8 0x1a0 > +#define ANACTRL_MPLL_STS 0x1a4 > +#define ANACTRL_HDMIPLL_CTRL0 0x1c0 > +#define ANACTRL_HDMIPLL_CTRL1 0x1c4 > +#define ANACTRL_HDMIPLL_CTRL2 0x1c8 > +#define ANACTRL_HDMIPLL_CTRL3 0x1cc > +#define ANACTRL_HDMIPLL_CTRL4 0x1d0 > +#define ANACTRL_HDMIPLL_CTRL5 0x1d4 > +#define ANACTRL_HDMIPLL_CTRL6 0x1d8 > +#define ANACTRL_HDMIPLL_STS 0x1dc > +#define ANACTRL_HDMIPLL_VLOCK 0x1e4 > + > +/* > + * CLKID index values > + * > + * These indices are entirely contrived and do not map onto the hardware. > + * It has now been decided to expose everything by default in the DT header: > + * include/dt-bindings/clock/amlogic,s4-pll-clkc.h. Only the clocks ids we don't > + * want to expose, such as the internal muxes and dividers of composite clocks, > + * will remain defined here. > + */ > +#define CLKID_FIXED_PLL_DCO 0 > +#define CLKID_FCLK_DIV2_DIV 2 > +#define CLKID_FCLK_DIV3_DIV 4 > +#define CLKID_FCLK_DIV4_DIV 6 > +#define CLKID_FCLK_DIV5_DIV 8 > +#define CLKID_FCLK_DIV7_DIV 10 > +#define CLKID_FCLK_DIV2P5_DIV 12 > +#define CLKID_GP0_PLL_DCO 14 > +#define CLKID_HIFI_PLL_DCO 16 > +#define CLKID_HDMI_PLL_DCO 18 > +#define CLKID_HDMI_PLL_OD 19 > +#define CLKID_MPLL_50M_DIV 21 > +#define CLKID_MPLL_PREDIV 23 > +#define CLKID_MPLL0_DIV 24 > +#define CLKID_MPLL1_DIV 26 > +#define CLKID_MPLL2_DIV 28 > +#define CLKID_MPLL3_DIV 30 > + > +#define NR_PLL_CLKS 32 > +/* include the CLKIDs that have been made part of the DT binding */ > +#include <dt-bindings/clock/amlogic,s4-pll-clkc.h> > + > +#endif /* __MESON_S4_PLL_H__ */
On Wed 17 May 2023 at 15:02, Yu Tu <yu.tu@amlogic.com> wrote: > Add the S4 PLL clock controller driver in the s4 SoC family. > > Signed-off-by: Yu Tu <yu.tu@amlogic.com> > --- > drivers/clk/meson/Kconfig | 13 + > drivers/clk/meson/Makefile | 1 + > drivers/clk/meson/s4-pll.c | 907 +++++++++++++++++++++++++++++++++++++ > drivers/clk/meson/s4-pll.h | 87 ++++ > 4 files changed, 1008 insertions(+) > create mode 100644 drivers/clk/meson/s4-pll.c > create mode 100644 drivers/clk/meson/s4-pll.h > > diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig > index fc002c155bc3..a663c90a3f3b 100644 > --- a/drivers/clk/meson/Kconfig > +++ b/drivers/clk/meson/Kconfig > @@ -115,4 +115,17 @@ config COMMON_CLK_G12A > help > Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2 > devices, aka g12a. Say Y if you want peripherals to work. > + > +config COMMON_CLK_S4_PLL > + tristate "S4 SoC PLL clock controllers support" > + depends on ARM64 > + default y > + select COMMON_CLK_MESON_MPLL > + select COMMON_CLK_MESON_PLL > + select COMMON_CLK_MESON_REGMAP > + help > + Support for the pll clock controller on Amlogic S805X2 and S905Y4 devices, > + aka s4. Amlogic S805X2 and S905Y4 devices include AQ222 and AQ229. I'm confused, what are the AQ222 and AQ229 ? are those SoC or boards like your reference designs ? If those are boards, it should not be mentionned here Also, what about the S905X4 ? is it another SoC family ? > + Say Y if you want the board to work, because plls are the parent of most > + peripherals. > endmenu > diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile > index 6eca2a406ee3..376f49cc13f1 100644 > --- a/drivers/clk/meson/Makefile > +++ b/drivers/clk/meson/Makefile > @@ -19,3 +19,4 @@ obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o > obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o > obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o > obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o > +obj-$(CONFIG_COMMON_CLK_S4_PLL) += s4-pll.o > diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c > new file mode 100644 > index 000000000000..c70a33586bcd > --- /dev/null > +++ b/drivers/clk/meson/s4-pll.c > @@ -0,0 +1,907 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Amlogic Meson-S4 PLL Clock Controller Driver > + * > + * Copyright (c) 2021 Amlogic, inc. > + * Author: Yu Tu <yu.tu@amlogic.com> > + */ > + > +#include <linux/clk-provider.h> > +#include <linux/of_device.h> > +#include <linux/platform_device.h> > + > +#include "clk-mpll.h" > +#include "clk-pll.h" > +#include "clk-regmap.h" > +#include "s4-pll.h" > + > +static DEFINE_SPINLOCK(meson_clk_lock); > + > +static struct clk_regmap s4_fixed_pll_dco = { > + .data = &(struct meson_clk_pll_data){ > + .en = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 28, > + .width = 1, > + }, > + .m = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 0, > + .width = 8, > + }, > + .n = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 10, > + .width = 5, > + }, > + .frac = { > + .reg_off = ANACTRL_FIXPLL_CTRL1, > + .shift = 0, > + .width = 17, > + }, > + .l = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 31, > + .width = 1, > + }, > + .rst = { > + .reg_off = ANACTRL_FIXPLL_CTRL0, > + .shift = 29, > + .width = 1, > + }, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fixed_pll_dco", > + /* > + * This clock is a fixed value (4GHz) that is initialized by ROMcode. > + * This clock won't ever change at runtime. > + * The chip design determines that this clock cannot be changed after > + * initialization. To prevent system crash caused by changing > + * fixed related register in kernel phase. This register is not > + * writable in the kernel phase. > + * So we use ro_ops. > + */ > + .ops = &meson_clk_pll_ro_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", } > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fixed_pll = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_FIXPLL_CTRL0, > + .shift = 16, > + .width = 2, > + .flags = CLK_DIVIDER_POWER_OF_TWO, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fixed_pll", > + /* > + * This clock is a fixed value (2GHz) that is initialized by ROMcode. > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_divider_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fixed_pll_dco.hw > + }, > + .num_parents = 1, > + /* > + * This clock won't ever change at runtime so > + * CLK_SET_RATE_PARENT is not required > + */ > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div2_div = { > + .mult = 1, > + .div = 2, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div2_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div2 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 24, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div2", > + /* > + * This clock is a fixed value (1GHz) that is initialized by ROMcode. > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div2_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div3_div = { > + .mult = 1, > + .div = 3, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div3_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div3 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 20, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div3", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div3_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div4_div = { > + .mult = 1, > + .div = 4, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div4_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div4 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 21, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div4", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div4_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div5_div = { > + .mult = 1, > + .div = 5, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div5_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div5 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 22, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div5", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div5_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div7_div = { > + .mult = 1, > + .div = 7, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div7_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div7 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 23, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div7", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div7_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_fixed_factor s4_fclk_div2p5_div = { > + .mult = 2, > + .div = 5, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div2p5_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fixed_pll.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_fclk_div2p5 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_FIXPLL_CTRL1, > + .bit_idx = 25, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "fclk_div2p5", > + /* > + * For more information, please refer to s4_fixed_pll_dco. > + */ > + .ops = &clk_regmap_gate_ro_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fclk_div2p5_div.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static const struct pll_mult_range s4_gp0_pll_mult_range = { > + .min = 125, > + .max = 250, > +}; > + > +/* > + * Internal gp0 pll emulation configuration parameters > + */ > +static const struct reg_sequence s4_gp0_init_regs[] = { > + { .reg = ANACTRL_GP0PLL_CTRL1, .def = 0x00000000 }, > + { .reg = ANACTRL_GP0PLL_CTRL2, .def = 0x00000000 }, > + { .reg = ANACTRL_GP0PLL_CTRL3, .def = 0x48681c00 }, > + { .reg = ANACTRL_GP0PLL_CTRL4, .def = 0x88770290 }, > + { .reg = ANACTRL_GP0PLL_CTRL5, .def = 0x39272000 }, > + { .reg = ANACTRL_GP0PLL_CTRL6, .def = 0x56540000 } > +}; > + > +static struct clk_regmap s4_gp0_pll_dco = { > + .data = &(struct meson_clk_pll_data){ > + .en = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 28, > + .width = 1, > + }, > + .m = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 0, > + .width = 8, > + }, > + .n = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 10, > + .width = 5, > + }, > + .frac = { > + .reg_off = ANACTRL_GP0PLL_CTRL1, > + .shift = 0, > + .width = 17, > + }, > + .l = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 31, > + .width = 1, > + }, > + .rst = { > + .reg_off = ANACTRL_GP0PLL_CTRL0, > + .shift = 29, > + .width = 1, > + }, > + .range = &s4_gp0_pll_mult_range, > + .init_regs = s4_gp0_init_regs, > + .init_count = ARRAY_SIZE(s4_gp0_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "gp0_pll_dco", > + .ops = &meson_clk_pll_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", } > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_gp0_pll = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_GP0PLL_CTRL0, > + .shift = 16, > + .width = 3, > + .flags = (CLK_DIVIDER_POWER_OF_TWO | > + CLK_DIVIDER_ROUND_CLOSEST), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "gp0_pll", > + .ops = &clk_regmap_divider_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_gp0_pll_dco.hw > + }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +/* > + * Internal hifi pll emulation configuration parameters > + */ > +static const struct reg_sequence s4_hifi_init_regs[] = { > + { .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x00010e56 }, > + { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00000000 }, > + { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x6a285c00 }, > + { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x65771290 }, > + { .reg = ANACTRL_HIFIPLL_CTRL5, .def = 0x39272000 }, > + { .reg = ANACTRL_HIFIPLL_CTRL6, .def = 0x56540000 } > +}; > + > +static struct clk_regmap s4_hifi_pll_dco = { > + .data = &(struct meson_clk_pll_data){ > + .en = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 28, > + .width = 1, > + }, > + .m = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 0, > + .width = 8, > + }, > + .n = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 10, > + .width = 5, > + }, > + .frac = { > + .reg_off = ANACTRL_HIFIPLL_CTRL1, > + .shift = 0, > + .width = 17, > + }, > + .l = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 31, > + .width = 1, > + }, > + .rst = { > + .reg_off = ANACTRL_HIFIPLL_CTRL0, > + .shift = 29, > + .width = 1, > + }, > + .range = &s4_gp0_pll_mult_range, > + .init_regs = s4_hifi_init_regs, > + .init_count = ARRAY_SIZE(s4_hifi_init_regs), > + .flags = CLK_MESON_PLL_ROUND_CLOSEST, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hifi_pll_dco", > + .ops = &meson_clk_pll_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", } > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_hifi_pll = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_HIFIPLL_CTRL0, > + .shift = 16, > + .width = 2, > + .flags = (CLK_DIVIDER_POWER_OF_TWO | > + CLK_DIVIDER_ROUND_CLOSEST), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hifi_pll", > + .ops = &clk_regmap_divider_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_hifi_pll_dco.hw > + }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static struct clk_regmap s4_hdmi_pll_dco = { > + .data = &(struct meson_clk_pll_data){ > + .en = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 28, > + .width = 1, > + }, > + .m = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 0, > + .width = 8, > + }, > + .n = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 10, > + .width = 5, > + }, > + .frac = { > + .reg_off = ANACTRL_HDMIPLL_CTRL1, > + .shift = 0, > + .width = 17, > + }, > + .l = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 31, > + .width = 1, > + }, > + .rst = { > + .reg_off = ANACTRL_HDMIPLL_CTRL0, > + .shift = 29, > + .width = 1, > + }, > + .range = &s4_gp0_pll_mult_range, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hdmi_pll_dco", > + .ops = &meson_clk_pll_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", } > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_hdmi_pll_od = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_HDMIPLL_CTRL0, > + .shift = 16, > + .width = 4, > + .flags = CLK_DIVIDER_POWER_OF_TWO, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hdmi_pll_od", > + .ops = &clk_regmap_divider_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_hdmi_pll_dco.hw > + }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static struct clk_regmap s4_hdmi_pll = { > + .data = &(struct clk_regmap_div_data){ > + .offset = ANACTRL_HDMIPLL_CTRL0, > + .shift = 20, > + .width = 2, > + .flags = CLK_DIVIDER_POWER_OF_TWO, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "hdmi_pll", > + .ops = &clk_regmap_divider_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_hdmi_pll_od.hw > + }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static struct clk_fixed_factor s4_mpll_50m_div = { > + .mult = 1, > + .div = 80, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll_50m_div", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fixed_pll_dco.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll_50m = { > + .data = &(struct clk_regmap_mux_data){ > + .offset = ANACTRL_FIXPLL_CTRL3, > + .mask = 0x1, > + .shift = 5, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll_50m", > + .ops = &clk_regmap_mux_ro_ops, > + .parent_data = (const struct clk_parent_data []) { > + { .fw_name = "xtal", }, > + { .hw = &s4_mpll_50m_div.hw }, > + }, > + .num_parents = 2, > + }, > +}; > + > +static struct clk_fixed_factor s4_mpll_prediv = { > + .mult = 1, > + .div = 2, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll_prediv", > + .ops = &clk_fixed_factor_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_fixed_pll_dco.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static const struct reg_sequence s4_mpll0_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL2, .def = 0x40000033 } > +}; > + > +static struct clk_regmap s4_mpll0_div = { > + .data = &(struct meson_clk_mpll_data){ > + .sdm = { > + .reg_off = ANACTRL_MPLL_CTRL1, > + .shift = 0, > + .width = 14, > + }, > + .sdm_en = { > + .reg_off = ANACTRL_MPLL_CTRL1, > + .shift = 30, > + .width = 1, > + }, > + .n2 = { > + .reg_off = ANACTRL_MPLL_CTRL1, > + .shift = 20, > + .width = 9, > + }, > + .ssen = { > + .reg_off = ANACTRL_MPLL_CTRL1, > + .shift = 29, > + .width = 1, > + }, > + .lock = &meson_clk_lock, > + .init_regs = s4_mpll0_init_regs, > + .init_count = ARRAY_SIZE(s4_mpll0_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll0_div", > + .ops = &meson_clk_mpll_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_mpll_prediv.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll0 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_MPLL_CTRL1, > + .bit_idx = 31, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll0", > + .ops = &clk_regmap_gate_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_mpll0_div.hw }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static const struct reg_sequence s4_mpll1_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL4, .def = 0x40000033 } > +}; > + > +static struct clk_regmap s4_mpll1_div = { > + .data = &(struct meson_clk_mpll_data){ > + .sdm = { > + .reg_off = ANACTRL_MPLL_CTRL3, > + .shift = 0, > + .width = 14, > + }, > + .sdm_en = { > + .reg_off = ANACTRL_MPLL_CTRL3, > + .shift = 30, > + .width = 1, > + }, > + .n2 = { > + .reg_off = ANACTRL_MPLL_CTRL3, > + .shift = 20, > + .width = 9, > + }, > + .ssen = { > + .reg_off = ANACTRL_MPLL_CTRL3, > + .shift = 29, > + .width = 1, > + }, > + .lock = &meson_clk_lock, > + .init_regs = s4_mpll1_init_regs, > + .init_count = ARRAY_SIZE(s4_mpll1_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll1_div", > + .ops = &meson_clk_mpll_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_mpll_prediv.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll1 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_MPLL_CTRL3, > + .bit_idx = 31, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll1", > + .ops = &clk_regmap_gate_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_mpll1_div.hw }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static const struct reg_sequence s4_mpll2_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL6, .def = 0x40000033 } > +}; > + > +static struct clk_regmap s4_mpll2_div = { > + .data = &(struct meson_clk_mpll_data){ > + .sdm = { > + .reg_off = ANACTRL_MPLL_CTRL5, > + .shift = 0, > + .width = 14, > + }, > + .sdm_en = { > + .reg_off = ANACTRL_MPLL_CTRL5, > + .shift = 30, > + .width = 1, > + }, > + .n2 = { > + .reg_off = ANACTRL_MPLL_CTRL5, > + .shift = 20, > + .width = 9, > + }, > + .ssen = { > + .reg_off = ANACTRL_MPLL_CTRL5, > + .shift = 29, > + .width = 1, > + }, > + .lock = &meson_clk_lock, > + .init_regs = s4_mpll2_init_regs, > + .init_count = ARRAY_SIZE(s4_mpll2_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll2_div", > + .ops = &meson_clk_mpll_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_mpll_prediv.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll2 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_MPLL_CTRL5, > + .bit_idx = 31, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll2", > + .ops = &clk_regmap_gate_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_mpll2_div.hw }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +static const struct reg_sequence s4_mpll3_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL8, .def = 0x40000033 } > +}; > + > +static struct clk_regmap s4_mpll3_div = { > + .data = &(struct meson_clk_mpll_data){ > + .sdm = { > + .reg_off = ANACTRL_MPLL_CTRL7, > + .shift = 0, > + .width = 14, > + }, > + .sdm_en = { > + .reg_off = ANACTRL_MPLL_CTRL7, > + .shift = 30, > + .width = 1, > + }, > + .n2 = { > + .reg_off = ANACTRL_MPLL_CTRL7, > + .shift = 20, > + .width = 9, > + }, > + .ssen = { > + .reg_off = ANACTRL_MPLL_CTRL7, > + .shift = 29, > + .width = 1, > + }, > + .lock = &meson_clk_lock, > + .init_regs = s4_mpll3_init_regs, > + .init_count = ARRAY_SIZE(s4_mpll3_init_regs), > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll3_div", > + .ops = &meson_clk_mpll_ops, > + .parent_hws = (const struct clk_hw *[]) { > + &s4_mpll_prediv.hw > + }, > + .num_parents = 1, > + }, > +}; > + > +static struct clk_regmap s4_mpll3 = { > + .data = &(struct clk_regmap_gate_data){ > + .offset = ANACTRL_MPLL_CTRL7, > + .bit_idx = 31, > + }, > + .hw.init = &(struct clk_init_data){ > + .name = "mpll3", > + .ops = &clk_regmap_gate_ops, > + .parent_hws = (const struct clk_hw *[]) { &s4_mpll3_div.hw }, > + .num_parents = 1, > + .flags = CLK_SET_RATE_PARENT, > + }, > +}; > + > +/* Array of all clocks provided by this provider */ > +static struct clk_hw_onecell_data s4_pll_hw_onecell_data = { > + .hws = { > + [CLKID_FIXED_PLL_DCO] = &s4_fixed_pll_dco.hw, > + [CLKID_FIXED_PLL] = &s4_fixed_pll.hw, > + [CLKID_FCLK_DIV2_DIV] = &s4_fclk_div2_div.hw, > + [CLKID_FCLK_DIV2] = &s4_fclk_div2.hw, > + [CLKID_FCLK_DIV3_DIV] = &s4_fclk_div3_div.hw, > + [CLKID_FCLK_DIV3] = &s4_fclk_div3.hw, > + [CLKID_FCLK_DIV4_DIV] = &s4_fclk_div4_div.hw, > + [CLKID_FCLK_DIV4] = &s4_fclk_div4.hw, > + [CLKID_FCLK_DIV5_DIV] = &s4_fclk_div5_div.hw, > + [CLKID_FCLK_DIV5] = &s4_fclk_div5.hw, > + [CLKID_FCLK_DIV7_DIV] = &s4_fclk_div7_div.hw, > + [CLKID_FCLK_DIV7] = &s4_fclk_div7.hw, > + [CLKID_FCLK_DIV2P5_DIV] = &s4_fclk_div2p5_div.hw, > + [CLKID_FCLK_DIV2P5] = &s4_fclk_div2p5.hw, > + [CLKID_GP0_PLL_DCO] = &s4_gp0_pll_dco.hw, > + [CLKID_GP0_PLL] = &s4_gp0_pll.hw, > + [CLKID_HIFI_PLL_DCO] = &s4_hifi_pll_dco.hw, > + [CLKID_HIFI_PLL] = &s4_hifi_pll.hw, > + [CLKID_HDMI_PLL_DCO] = &s4_hdmi_pll_dco.hw, > + [CLKID_HDMI_PLL_OD] = &s4_hdmi_pll_od.hw, > + [CLKID_HDMI_PLL] = &s4_hdmi_pll.hw, > + [CLKID_MPLL_50M_DIV] = &s4_mpll_50m_div.hw, > + [CLKID_MPLL_50M] = &s4_mpll_50m.hw, > + [CLKID_MPLL_PREDIV] = &s4_mpll_prediv.hw, > + [CLKID_MPLL0_DIV] = &s4_mpll0_div.hw, > + [CLKID_MPLL0] = &s4_mpll0.hw, > + [CLKID_MPLL1_DIV] = &s4_mpll1_div.hw, > + [CLKID_MPLL1] = &s4_mpll1.hw, > + [CLKID_MPLL2_DIV] = &s4_mpll2_div.hw, > + [CLKID_MPLL2] = &s4_mpll2.hw, > + [CLKID_MPLL3_DIV] = &s4_mpll3_div.hw, > + [CLKID_MPLL3] = &s4_mpll3.hw, > + [NR_PLL_CLKS] = NULL > + }, > + .num = NR_PLL_CLKS, > +}; > + > +static struct clk_regmap *const s4_pll_clk_regmaps[] = { > + &s4_fixed_pll_dco, > + &s4_fixed_pll, > + &s4_fclk_div2, > + &s4_fclk_div3, > + &s4_fclk_div4, > + &s4_fclk_div5, > + &s4_fclk_div7, > + &s4_fclk_div2p5, > + &s4_gp0_pll_dco, > + &s4_gp0_pll, > + &s4_hifi_pll_dco, > + &s4_hifi_pll, > + &s4_hdmi_pll_dco, > + &s4_hdmi_pll_od, > + &s4_hdmi_pll, > + &s4_mpll_50m, > + &s4_mpll0_div, > + &s4_mpll0, > + &s4_mpll1_div, > + &s4_mpll1, > + &s4_mpll2_div, > + &s4_mpll2, > + &s4_mpll3_div, > + &s4_mpll3, > +}; > + > +static const struct reg_sequence s4_init_regs[] = { > + { .reg = ANACTRL_MPLL_CTRL0, .def = 0x00000543 }, > +}; > + > +static struct regmap_config clkc_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > +}; > + > +static int meson_s4_pll_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct regmap *regmap; > + void __iomem *base; > + int ret, i; > + > + base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(base)) > + return PTR_ERR(base); > + > + regmap = devm_regmap_init_mmio(dev, base, &clkc_regmap_config); > + if (IS_ERR(regmap)) > + return PTR_ERR(regmap); > + > + ret = regmap_multi_reg_write(regmap, s4_init_regs, ARRAY_SIZE(s4_init_regs)); > + if (ret) { > + dev_err(dev, "Failed to init registers\n"); > + return ret; > + } > + > + /* Populate regmap for the regmap backed clocks */ > + for (i = 0; i < ARRAY_SIZE(s4_pll_clk_regmaps); i++) > + s4_pll_clk_regmaps[i]->map = regmap; > + > + for (i = 0; i < s4_pll_hw_onecell_data.num; i++) { > + /* array might be sparse */ > + if (!s4_pll_hw_onecell_data.hws[i]) > + continue; > + > + ret = devm_clk_hw_register(dev, s4_pll_hw_onecell_data.hws[i]); > + if (ret) { > + dev_err(dev, "Clock registration failed\n"); > + return ret; > + } > + } > + > + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, > + &s4_pll_hw_onecell_data); > +} > + > +static const struct of_device_id clkc_match_table[] = { > + { > + .compatible = "amlogic,s4-pll-clkc", > + }, > + {} > +}; > + > +static struct platform_driver s4_driver = { > + .probe = meson_s4_pll_probe, > + .driver = { > + .name = "s4-pll-clkc", > + .of_match_table = clkc_match_table, > + }, > +}; > + > +module_platform_driver(s4_driver); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/clk/meson/s4-pll.h b/drivers/clk/meson/s4-pll.h > new file mode 100644 > index 000000000000..021d9f8b2b30 > --- /dev/null > +++ b/drivers/clk/meson/s4-pll.h > @@ -0,0 +1,87 @@ > +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ > +/* > + * Copyright (c) 2021 Amlogic, inc. > + * Author: Yu Tu <yu.tu@amlogic.com> > + */ > + > +#ifndef __MESON_S4_PLL_H__ > +#define __MESON_S4_PLL_H__ > + > +/* ANA_CTRL - Registers */ > + > +#define ANACTRL_FIXPLL_CTRL0 0x040 > +#define ANACTRL_FIXPLL_CTRL1 0x044 > +#define ANACTRL_FIXPLL_CTRL2 0x048 > +#define ANACTRL_FIXPLL_CTRL3 0x04c > +#define ANACTRL_FIXPLL_CTRL4 0x050 > +#define ANACTRL_FIXPLL_CTRL5 0x054 > +#define ANACTRL_FIXPLL_CTRL6 0x058 > +#define ANACTRL_FIXPLL_STS 0x05c > +#define ANACTRL_GP0PLL_CTRL0 0x080 > +#define ANACTRL_GP0PLL_CTRL1 0x084 > +#define ANACTRL_GP0PLL_CTRL2 0x088 > +#define ANACTRL_GP0PLL_CTRL3 0x08c > +#define ANACTRL_GP0PLL_CTRL4 0x090 > +#define ANACTRL_GP0PLL_CTRL5 0x094 > +#define ANACTRL_GP0PLL_CTRL6 0x098 > +#define ANACTRL_GP0PLL_STS 0x09c > +#define ANACTRL_HIFIPLL_CTRL0 0x100 > +#define ANACTRL_HIFIPLL_CTRL1 0x104 > +#define ANACTRL_HIFIPLL_CTRL2 0x108 > +#define ANACTRL_HIFIPLL_CTRL3 0x10c > +#define ANACTRL_HIFIPLL_CTRL4 0x110 > +#define ANACTRL_HIFIPLL_CTRL5 0x114 > +#define ANACTRL_HIFIPLL_CTRL6 0x118 > +#define ANACTRL_HIFIPLL_STS 0x11c > +#define ANACTRL_MPLL_CTRL0 0x180 > +#define ANACTRL_MPLL_CTRL1 0x184 > +#define ANACTRL_MPLL_CTRL2 0x188 > +#define ANACTRL_MPLL_CTRL3 0x18c > +#define ANACTRL_MPLL_CTRL4 0x190 > +#define ANACTRL_MPLL_CTRL5 0x194 > +#define ANACTRL_MPLL_CTRL6 0x198 > +#define ANACTRL_MPLL_CTRL7 0x19c > +#define ANACTRL_MPLL_CTRL8 0x1a0 > +#define ANACTRL_MPLL_STS 0x1a4 > +#define ANACTRL_HDMIPLL_CTRL0 0x1c0 > +#define ANACTRL_HDMIPLL_CTRL1 0x1c4 > +#define ANACTRL_HDMIPLL_CTRL2 0x1c8 > +#define ANACTRL_HDMIPLL_CTRL3 0x1cc > +#define ANACTRL_HDMIPLL_CTRL4 0x1d0 > +#define ANACTRL_HDMIPLL_CTRL5 0x1d4 > +#define ANACTRL_HDMIPLL_CTRL6 0x1d8 > +#define ANACTRL_HDMIPLL_STS 0x1dc > +#define ANACTRL_HDMIPLL_VLOCK 0x1e4 > + > +/* > + * CLKID index values > + * > + * These indices are entirely contrived and do not map onto the hardware. > + * It has now been decided to expose everything by default in the DT header: > + * include/dt-bindings/clock/amlogic,s4-pll-clkc.h. Only the clocks ids we don't > + * want to expose, such as the internal muxes and dividers of composite clocks, > + * will remain defined here. > + */ > +#define CLKID_FIXED_PLL_DCO 0 > +#define CLKID_FCLK_DIV2_DIV 2 > +#define CLKID_FCLK_DIV3_DIV 4 > +#define CLKID_FCLK_DIV4_DIV 6 > +#define CLKID_FCLK_DIV5_DIV 8 > +#define CLKID_FCLK_DIV7_DIV 10 > +#define CLKID_FCLK_DIV2P5_DIV 12 > +#define CLKID_GP0_PLL_DCO 14 > +#define CLKID_HIFI_PLL_DCO 16 > +#define CLKID_HDMI_PLL_DCO 18 > +#define CLKID_HDMI_PLL_OD 19 > +#define CLKID_MPLL_50M_DIV 21 > +#define CLKID_MPLL_PREDIV 23 > +#define CLKID_MPLL0_DIV 24 > +#define CLKID_MPLL1_DIV 26 > +#define CLKID_MPLL2_DIV 28 > +#define CLKID_MPLL3_DIV 30 > + > +#define NR_PLL_CLKS 32 > +/* include the CLKIDs that have been made part of the DT binding */ > +#include <dt-bindings/clock/amlogic,s4-pll-clkc.h> > + > +#endif /* __MESON_S4_PLL_H__ */
On 2023/6/6 22:34, Jerome Brunet wrote: > [ EXTERNAL EMAIL ] > > On Wed 17 May 2023 at 15:02, Yu Tu <yu.tu@amlogic.com> wrote: > >> Add the S4 PLL clock controller driver in the s4 SoC family. >> >> Signed-off-by: Yu Tu <yu.tu@amlogic.com> >> --- >> drivers/clk/meson/Kconfig | 13 + >> drivers/clk/meson/Makefile | 1 + >> drivers/clk/meson/s4-pll.c | 907 +++++++++++++++++++++++++++++++++++++ >> drivers/clk/meson/s4-pll.h | 87 ++++ >> 4 files changed, 1008 insertions(+) >> create mode 100644 drivers/clk/meson/s4-pll.c >> create mode 100644 drivers/clk/meson/s4-pll.h >> >> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig >> index fc002c155bc3..a663c90a3f3b 100644 >> --- a/drivers/clk/meson/Kconfig >> +++ b/drivers/clk/meson/Kconfig >> @@ -115,4 +115,17 @@ config COMMON_CLK_G12A >> help >> Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2 >> devices, aka g12a. Say Y if you want peripherals to work. >> + >> +config COMMON_CLK_S4_PLL >> + tristate "S4 SoC PLL clock controllers support" >> + depends on ARM64 >> + default y >> + select COMMON_CLK_MESON_MPLL >> + select COMMON_CLK_MESON_PLL >> + select COMMON_CLK_MESON_REGMAP >> + help >> + Support for the pll clock controller on Amlogic S805X2 and S905Y4 devices, >> + aka s4. Amlogic S805X2 and S905Y4 devices include AQ222 and AQ229. > > I'm confused, what are the AQ222 and AQ229 ? are those SoC or boards > like your reference designs ? > > If those are boards, it should not be mentionned here Okay. I will reomove those boards name in next version. > > Also, what about the S905X4 ? is it another SoC family ? Yes. You're right. > >> + Say Y if you want the board to work, because plls are the parent of most >> + peripherals. >> endmenu >> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile >> index 6eca2a406ee3..376f49cc13f1 100644 >> --- a/drivers/clk/meson/Makefile >> +++ b/drivers/clk/meson/Makefile >> @@ -19,3 +19,4 @@ obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o >> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o >> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o >> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o >> +obj-$(CONFIG_COMMON_CLK_S4_PLL) += s4-pll.o >> diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c >> new file mode 100644 >> index 000000000000..c70a33586bcd >> --- /dev/null >> +++ b/drivers/clk/meson/s4-pll.c >> @@ -0,0 +1,907 @@ >> +// SPDX-License-Identifier: GPL-2.0+ >> +/* >> + * Amlogic Meson-S4 PLL Clock Controller Driver >> + * >> + * Copyright (c) 2021 Amlogic, inc. >> + * Author: Yu Tu <yu.tu@amlogic.com> >> + */ >> + >> +#include <linux/clk-provider.h> >> +#include <linux/of_device.h> >> +#include <linux/platform_device.h> >> + >> +#include "clk-mpll.h" >> +#include "clk-pll.h" >> +#include "clk-regmap.h" >> +#include "s4-pll.h" >> + >> +static DEFINE_SPINLOCK(meson_clk_lock); >> + >> +static struct clk_regmap s4_fixed_pll_dco = { >> + .data = &(struct meson_clk_pll_data){ >> + .en = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 28, >> + .width = 1, >> + }, >> + .m = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 0, >> + .width = 8, >> + }, >> + .n = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 10, >> + .width = 5, >> + }, >> + .frac = { >> + .reg_off = ANACTRL_FIXPLL_CTRL1, >> + .shift = 0, >> + .width = 17, >> + }, >> + .l = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 31, >> + .width = 1, >> + }, >> + .rst = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 29, >> + .width = 1, >> + }, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fixed_pll_dco", >> + /* >> + * This clock is a fixed value (4GHz) that is initialized by ROMcode. >> + * This clock won't ever change at runtime. >> + * The chip design determines that this clock cannot be changed after >> + * initialization. To prevent system crash caused by changing >> + * fixed related register in kernel phase. This register is not >> + * writable in the kernel phase. >> + * So we use ro_ops. >> + */ >> + .ops = &meson_clk_pll_ro_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", } >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fixed_pll = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_FIXPLL_CTRL0, >> + .shift = 16, >> + .width = 2, >> + .flags = CLK_DIVIDER_POWER_OF_TWO, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fixed_pll", >> + /* >> + * This clock is a fixed value (2GHz) that is initialized by ROMcode. >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_divider_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fixed_pll_dco.hw >> + }, >> + .num_parents = 1, >> + /* >> + * This clock won't ever change at runtime so >> + * CLK_SET_RATE_PARENT is not required >> + */ >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div2_div = { >> + .mult = 1, >> + .div = 2, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div2_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div2 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 24, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div2", >> + /* >> + * This clock is a fixed value (1GHz) that is initialized by ROMcode. >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div2_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div3_div = { >> + .mult = 1, >> + .div = 3, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div3_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div3 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 20, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div3", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div3_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div4_div = { >> + .mult = 1, >> + .div = 4, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div4_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div4 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 21, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div4", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div4_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div5_div = { >> + .mult = 1, >> + .div = 5, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div5_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div5 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 22, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div5", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div5_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div7_div = { >> + .mult = 1, >> + .div = 7, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div7_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div7 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 23, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div7", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div7_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div2p5_div = { >> + .mult = 2, >> + .div = 5, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div2p5_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fixed_pll.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div2p5 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 25, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div2p5", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div2p5_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static const struct pll_mult_range s4_gp0_pll_mult_range = { >> + .min = 125, >> + .max = 250, >> +}; >> + >> +/* >> + * Internal gp0 pll emulation configuration parameters >> + */ >> +static const struct reg_sequence s4_gp0_init_regs[] = { >> + { .reg = ANACTRL_GP0PLL_CTRL1, .def = 0x00000000 }, >> + { .reg = ANACTRL_GP0PLL_CTRL2, .def = 0x00000000 }, >> + { .reg = ANACTRL_GP0PLL_CTRL3, .def = 0x48681c00 }, >> + { .reg = ANACTRL_GP0PLL_CTRL4, .def = 0x88770290 }, >> + { .reg = ANACTRL_GP0PLL_CTRL5, .def = 0x39272000 }, >> + { .reg = ANACTRL_GP0PLL_CTRL6, .def = 0x56540000 } >> +}; >> + >> +static struct clk_regmap s4_gp0_pll_dco = { >> + .data = &(struct meson_clk_pll_data){ >> + .en = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 28, >> + .width = 1, >> + }, >> + .m = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 0, >> + .width = 8, >> + }, >> + .n = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 10, >> + .width = 5, >> + }, >> + .frac = { >> + .reg_off = ANACTRL_GP0PLL_CTRL1, >> + .shift = 0, >> + .width = 17, >> + }, >> + .l = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 31, >> + .width = 1, >> + }, >> + .rst = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 29, >> + .width = 1, >> + }, >> + .range = &s4_gp0_pll_mult_range, >> + .init_regs = s4_gp0_init_regs, >> + .init_count = ARRAY_SIZE(s4_gp0_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "gp0_pll_dco", >> + .ops = &meson_clk_pll_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", } >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_gp0_pll = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_GP0PLL_CTRL0, >> + .shift = 16, >> + .width = 3, >> + .flags = (CLK_DIVIDER_POWER_OF_TWO | >> + CLK_DIVIDER_ROUND_CLOSEST), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "gp0_pll", >> + .ops = &clk_regmap_divider_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_gp0_pll_dco.hw >> + }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +/* >> + * Internal hifi pll emulation configuration parameters >> + */ >> +static const struct reg_sequence s4_hifi_init_regs[] = { >> + { .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x00010e56 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00000000 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x6a285c00 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x65771290 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL5, .def = 0x39272000 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL6, .def = 0x56540000 } >> +}; >> + >> +static struct clk_regmap s4_hifi_pll_dco = { >> + .data = &(struct meson_clk_pll_data){ >> + .en = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 28, >> + .width = 1, >> + }, >> + .m = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 0, >> + .width = 8, >> + }, >> + .n = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 10, >> + .width = 5, >> + }, >> + .frac = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL1, >> + .shift = 0, >> + .width = 17, >> + }, >> + .l = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 31, >> + .width = 1, >> + }, >> + .rst = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 29, >> + .width = 1, >> + }, >> + .range = &s4_gp0_pll_mult_range, >> + .init_regs = s4_hifi_init_regs, >> + .init_count = ARRAY_SIZE(s4_hifi_init_regs), >> + .flags = CLK_MESON_PLL_ROUND_CLOSEST, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hifi_pll_dco", >> + .ops = &meson_clk_pll_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", } >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_hifi_pll = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 16, >> + .width = 2, >> + .flags = (CLK_DIVIDER_POWER_OF_TWO | >> + CLK_DIVIDER_ROUND_CLOSEST), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hifi_pll", >> + .ops = &clk_regmap_divider_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_hifi_pll_dco.hw >> + }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static struct clk_regmap s4_hdmi_pll_dco = { >> + .data = &(struct meson_clk_pll_data){ >> + .en = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 28, >> + .width = 1, >> + }, >> + .m = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 0, >> + .width = 8, >> + }, >> + .n = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 10, >> + .width = 5, >> + }, >> + .frac = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL1, >> + .shift = 0, >> + .width = 17, >> + }, >> + .l = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 31, >> + .width = 1, >> + }, >> + .rst = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 29, >> + .width = 1, >> + }, >> + .range = &s4_gp0_pll_mult_range, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hdmi_pll_dco", >> + .ops = &meson_clk_pll_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", } >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_hdmi_pll_od = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 16, >> + .width = 4, >> + .flags = CLK_DIVIDER_POWER_OF_TWO, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hdmi_pll_od", >> + .ops = &clk_regmap_divider_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_hdmi_pll_dco.hw >> + }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static struct clk_regmap s4_hdmi_pll = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 20, >> + .width = 2, >> + .flags = CLK_DIVIDER_POWER_OF_TWO, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hdmi_pll", >> + .ops = &clk_regmap_divider_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_hdmi_pll_od.hw >> + }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_mpll_50m_div = { >> + .mult = 1, >> + .div = 80, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll_50m_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fixed_pll_dco.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll_50m = { >> + .data = &(struct clk_regmap_mux_data){ >> + .offset = ANACTRL_FIXPLL_CTRL3, >> + .mask = 0x1, >> + .shift = 5, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll_50m", >> + .ops = &clk_regmap_mux_ro_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", }, >> + { .hw = &s4_mpll_50m_div.hw }, >> + }, >> + .num_parents = 2, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_mpll_prediv = { >> + .mult = 1, >> + .div = 2, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll_prediv", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fixed_pll_dco.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static const struct reg_sequence s4_mpll0_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL2, .def = 0x40000033 } >> +}; >> + >> +static struct clk_regmap s4_mpll0_div = { >> + .data = &(struct meson_clk_mpll_data){ >> + .sdm = { >> + .reg_off = ANACTRL_MPLL_CTRL1, >> + .shift = 0, >> + .width = 14, >> + }, >> + .sdm_en = { >> + .reg_off = ANACTRL_MPLL_CTRL1, >> + .shift = 30, >> + .width = 1, >> + }, >> + .n2 = { >> + .reg_off = ANACTRL_MPLL_CTRL1, >> + .shift = 20, >> + .width = 9, >> + }, >> + .ssen = { >> + .reg_off = ANACTRL_MPLL_CTRL1, >> + .shift = 29, >> + .width = 1, >> + }, >> + .lock = &meson_clk_lock, >> + .init_regs = s4_mpll0_init_regs, >> + .init_count = ARRAY_SIZE(s4_mpll0_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll0_div", >> + .ops = &meson_clk_mpll_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_mpll_prediv.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll0 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_MPLL_CTRL1, >> + .bit_idx = 31, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll0", >> + .ops = &clk_regmap_gate_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_mpll0_div.hw }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static const struct reg_sequence s4_mpll1_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL4, .def = 0x40000033 } >> +}; >> + >> +static struct clk_regmap s4_mpll1_div = { >> + .data = &(struct meson_clk_mpll_data){ >> + .sdm = { >> + .reg_off = ANACTRL_MPLL_CTRL3, >> + .shift = 0, >> + .width = 14, >> + }, >> + .sdm_en = { >> + .reg_off = ANACTRL_MPLL_CTRL3, >> + .shift = 30, >> + .width = 1, >> + }, >> + .n2 = { >> + .reg_off = ANACTRL_MPLL_CTRL3, >> + .shift = 20, >> + .width = 9, >> + }, >> + .ssen = { >> + .reg_off = ANACTRL_MPLL_CTRL3, >> + .shift = 29, >> + .width = 1, >> + }, >> + .lock = &meson_clk_lock, >> + .init_regs = s4_mpll1_init_regs, >> + .init_count = ARRAY_SIZE(s4_mpll1_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll1_div", >> + .ops = &meson_clk_mpll_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_mpll_prediv.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll1 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_MPLL_CTRL3, >> + .bit_idx = 31, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll1", >> + .ops = &clk_regmap_gate_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_mpll1_div.hw }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static const struct reg_sequence s4_mpll2_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL6, .def = 0x40000033 } >> +}; >> + >> +static struct clk_regmap s4_mpll2_div = { >> + .data = &(struct meson_clk_mpll_data){ >> + .sdm = { >> + .reg_off = ANACTRL_MPLL_CTRL5, >> + .shift = 0, >> + .width = 14, >> + }, >> + .sdm_en = { >> + .reg_off = ANACTRL_MPLL_CTRL5, >> + .shift = 30, >> + .width = 1, >> + }, >> + .n2 = { >> + .reg_off = ANACTRL_MPLL_CTRL5, >> + .shift = 20, >> + .width = 9, >> + }, >> + .ssen = { >> + .reg_off = ANACTRL_MPLL_CTRL5, >> + .shift = 29, >> + .width = 1, >> + }, >> + .lock = &meson_clk_lock, >> + .init_regs = s4_mpll2_init_regs, >> + .init_count = ARRAY_SIZE(s4_mpll2_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll2_div", >> + .ops = &meson_clk_mpll_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_mpll_prediv.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll2 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_MPLL_CTRL5, >> + .bit_idx = 31, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll2", >> + .ops = &clk_regmap_gate_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_mpll2_div.hw }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static const struct reg_sequence s4_mpll3_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL8, .def = 0x40000033 } >> +}; >> + >> +static struct clk_regmap s4_mpll3_div = { >> + .data = &(struct meson_clk_mpll_data){ >> + .sdm = { >> + .reg_off = ANACTRL_MPLL_CTRL7, >> + .shift = 0, >> + .width = 14, >> + }, >> + .sdm_en = { >> + .reg_off = ANACTRL_MPLL_CTRL7, >> + .shift = 30, >> + .width = 1, >> + }, >> + .n2 = { >> + .reg_off = ANACTRL_MPLL_CTRL7, >> + .shift = 20, >> + .width = 9, >> + }, >> + .ssen = { >> + .reg_off = ANACTRL_MPLL_CTRL7, >> + .shift = 29, >> + .width = 1, >> + }, >> + .lock = &meson_clk_lock, >> + .init_regs = s4_mpll3_init_regs, >> + .init_count = ARRAY_SIZE(s4_mpll3_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll3_div", >> + .ops = &meson_clk_mpll_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_mpll_prediv.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll3 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_MPLL_CTRL7, >> + .bit_idx = 31, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll3", >> + .ops = &clk_regmap_gate_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_mpll3_div.hw }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +/* Array of all clocks provided by this provider */ >> +static struct clk_hw_onecell_data s4_pll_hw_onecell_data = { >> + .hws = { >> + [CLKID_FIXED_PLL_DCO] = &s4_fixed_pll_dco.hw, >> + [CLKID_FIXED_PLL] = &s4_fixed_pll.hw, >> + [CLKID_FCLK_DIV2_DIV] = &s4_fclk_div2_div.hw, >> + [CLKID_FCLK_DIV2] = &s4_fclk_div2.hw, >> + [CLKID_FCLK_DIV3_DIV] = &s4_fclk_div3_div.hw, >> + [CLKID_FCLK_DIV3] = &s4_fclk_div3.hw, >> + [CLKID_FCLK_DIV4_DIV] = &s4_fclk_div4_div.hw, >> + [CLKID_FCLK_DIV4] = &s4_fclk_div4.hw, >> + [CLKID_FCLK_DIV5_DIV] = &s4_fclk_div5_div.hw, >> + [CLKID_FCLK_DIV5] = &s4_fclk_div5.hw, >> + [CLKID_FCLK_DIV7_DIV] = &s4_fclk_div7_div.hw, >> + [CLKID_FCLK_DIV7] = &s4_fclk_div7.hw, >> + [CLKID_FCLK_DIV2P5_DIV] = &s4_fclk_div2p5_div.hw, >> + [CLKID_FCLK_DIV2P5] = &s4_fclk_div2p5.hw, >> + [CLKID_GP0_PLL_DCO] = &s4_gp0_pll_dco.hw, >> + [CLKID_GP0_PLL] = &s4_gp0_pll.hw, >> + [CLKID_HIFI_PLL_DCO] = &s4_hifi_pll_dco.hw, >> + [CLKID_HIFI_PLL] = &s4_hifi_pll.hw, >> + [CLKID_HDMI_PLL_DCO] = &s4_hdmi_pll_dco.hw, >> + [CLKID_HDMI_PLL_OD] = &s4_hdmi_pll_od.hw, >> + [CLKID_HDMI_PLL] = &s4_hdmi_pll.hw, >> + [CLKID_MPLL_50M_DIV] = &s4_mpll_50m_div.hw, >> + [CLKID_MPLL_50M] = &s4_mpll_50m.hw, >> + [CLKID_MPLL_PREDIV] = &s4_mpll_prediv.hw, >> + [CLKID_MPLL0_DIV] = &s4_mpll0_div.hw, >> + [CLKID_MPLL0] = &s4_mpll0.hw, >> + [CLKID_MPLL1_DIV] = &s4_mpll1_div.hw, >> + [CLKID_MPLL1] = &s4_mpll1.hw, >> + [CLKID_MPLL2_DIV] = &s4_mpll2_div.hw, >> + [CLKID_MPLL2] = &s4_mpll2.hw, >> + [CLKID_MPLL3_DIV] = &s4_mpll3_div.hw, >> + [CLKID_MPLL3] = &s4_mpll3.hw, >> + [NR_PLL_CLKS] = NULL >> + }, >> + .num = NR_PLL_CLKS, >> +}; >> + >> +static struct clk_regmap *const s4_pll_clk_regmaps[] = { >> + &s4_fixed_pll_dco, >> + &s4_fixed_pll, >> + &s4_fclk_div2, >> + &s4_fclk_div3, >> + &s4_fclk_div4, >> + &s4_fclk_div5, >> + &s4_fclk_div7, >> + &s4_fclk_div2p5, >> + &s4_gp0_pll_dco, >> + &s4_gp0_pll, >> + &s4_hifi_pll_dco, >> + &s4_hifi_pll, >> + &s4_hdmi_pll_dco, >> + &s4_hdmi_pll_od, >> + &s4_hdmi_pll, >> + &s4_mpll_50m, >> + &s4_mpll0_div, >> + &s4_mpll0, >> + &s4_mpll1_div, >> + &s4_mpll1, >> + &s4_mpll2_div, >> + &s4_mpll2, >> + &s4_mpll3_div, >> + &s4_mpll3, >> +}; >> + >> +static const struct reg_sequence s4_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL0, .def = 0x00000543 }, >> +}; >> + >> +static struct regmap_config clkc_regmap_config = { >> + .reg_bits = 32, >> + .val_bits = 32, >> + .reg_stride = 4, >> +}; >> + >> +static int meson_s4_pll_probe(struct platform_device *pdev) >> +{ >> + struct device *dev = &pdev->dev; >> + struct regmap *regmap; >> + void __iomem *base; >> + int ret, i; >> + >> + base = devm_platform_ioremap_resource(pdev, 0); >> + if (IS_ERR(base)) >> + return PTR_ERR(base); >> + >> + regmap = devm_regmap_init_mmio(dev, base, &clkc_regmap_config); >> + if (IS_ERR(regmap)) >> + return PTR_ERR(regmap); >> + >> + ret = regmap_multi_reg_write(regmap, s4_init_regs, ARRAY_SIZE(s4_init_regs)); >> + if (ret) { >> + dev_err(dev, "Failed to init registers\n"); >> + return ret; >> + } >> + >> + /* Populate regmap for the regmap backed clocks */ >> + for (i = 0; i < ARRAY_SIZE(s4_pll_clk_regmaps); i++) >> + s4_pll_clk_regmaps[i]->map = regmap; >> + >> + for (i = 0; i < s4_pll_hw_onecell_data.num; i++) { >> + /* array might be sparse */ >> + if (!s4_pll_hw_onecell_data.hws[i]) >> + continue; >> + >> + ret = devm_clk_hw_register(dev, s4_pll_hw_onecell_data.hws[i]); >> + if (ret) { >> + dev_err(dev, "Clock registration failed\n"); >> + return ret; >> + } >> + } >> + >> + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, >> + &s4_pll_hw_onecell_data); >> +} >> + >> +static const struct of_device_id clkc_match_table[] = { >> + { >> + .compatible = "amlogic,s4-pll-clkc", >> + }, >> + {} >> +}; >> + >> +static struct platform_driver s4_driver = { >> + .probe = meson_s4_pll_probe, >> + .driver = { >> + .name = "s4-pll-clkc", >> + .of_match_table = clkc_match_table, >> + }, >> +}; >> + >> +module_platform_driver(s4_driver); >> +MODULE_LICENSE("GPL"); >> diff --git a/drivers/clk/meson/s4-pll.h b/drivers/clk/meson/s4-pll.h >> new file mode 100644 >> index 000000000000..021d9f8b2b30 >> --- /dev/null >> +++ b/drivers/clk/meson/s4-pll.h >> @@ -0,0 +1,87 @@ >> +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ >> +/* >> + * Copyright (c) 2021 Amlogic, inc. >> + * Author: Yu Tu <yu.tu@amlogic.com> >> + */ >> + >> +#ifndef __MESON_S4_PLL_H__ >> +#define __MESON_S4_PLL_H__ >> + >> +/* ANA_CTRL - Registers */ >> + >> +#define ANACTRL_FIXPLL_CTRL0 0x040 >> +#define ANACTRL_FIXPLL_CTRL1 0x044 >> +#define ANACTRL_FIXPLL_CTRL2 0x048 >> +#define ANACTRL_FIXPLL_CTRL3 0x04c >> +#define ANACTRL_FIXPLL_CTRL4 0x050 >> +#define ANACTRL_FIXPLL_CTRL5 0x054 >> +#define ANACTRL_FIXPLL_CTRL6 0x058 >> +#define ANACTRL_FIXPLL_STS 0x05c >> +#define ANACTRL_GP0PLL_CTRL0 0x080 >> +#define ANACTRL_GP0PLL_CTRL1 0x084 >> +#define ANACTRL_GP0PLL_CTRL2 0x088 >> +#define ANACTRL_GP0PLL_CTRL3 0x08c >> +#define ANACTRL_GP0PLL_CTRL4 0x090 >> +#define ANACTRL_GP0PLL_CTRL5 0x094 >> +#define ANACTRL_GP0PLL_CTRL6 0x098 >> +#define ANACTRL_GP0PLL_STS 0x09c >> +#define ANACTRL_HIFIPLL_CTRL0 0x100 >> +#define ANACTRL_HIFIPLL_CTRL1 0x104 >> +#define ANACTRL_HIFIPLL_CTRL2 0x108 >> +#define ANACTRL_HIFIPLL_CTRL3 0x10c >> +#define ANACTRL_HIFIPLL_CTRL4 0x110 >> +#define ANACTRL_HIFIPLL_CTRL5 0x114 >> +#define ANACTRL_HIFIPLL_CTRL6 0x118 >> +#define ANACTRL_HIFIPLL_STS 0x11c >> +#define ANACTRL_MPLL_CTRL0 0x180 >> +#define ANACTRL_MPLL_CTRL1 0x184 >> +#define ANACTRL_MPLL_CTRL2 0x188 >> +#define ANACTRL_MPLL_CTRL3 0x18c >> +#define ANACTRL_MPLL_CTRL4 0x190 >> +#define ANACTRL_MPLL_CTRL5 0x194 >> +#define ANACTRL_MPLL_CTRL6 0x198 >> +#define ANACTRL_MPLL_CTRL7 0x19c >> +#define ANACTRL_MPLL_CTRL8 0x1a0 >> +#define ANACTRL_MPLL_STS 0x1a4 >> +#define ANACTRL_HDMIPLL_CTRL0 0x1c0 >> +#define ANACTRL_HDMIPLL_CTRL1 0x1c4 >> +#define ANACTRL_HDMIPLL_CTRL2 0x1c8 >> +#define ANACTRL_HDMIPLL_CTRL3 0x1cc >> +#define ANACTRL_HDMIPLL_CTRL4 0x1d0 >> +#define ANACTRL_HDMIPLL_CTRL5 0x1d4 >> +#define ANACTRL_HDMIPLL_CTRL6 0x1d8 >> +#define ANACTRL_HDMIPLL_STS 0x1dc >> +#define ANACTRL_HDMIPLL_VLOCK 0x1e4 >> + >> +/* >> + * CLKID index values >> + * >> + * These indices are entirely contrived and do not map onto the hardware. >> + * It has now been decided to expose everything by default in the DT header: >> + * include/dt-bindings/clock/amlogic,s4-pll-clkc.h. Only the clocks ids we don't >> + * want to expose, such as the internal muxes and dividers of composite clocks, >> + * will remain defined here. >> + */ >> +#define CLKID_FIXED_PLL_DCO 0 >> +#define CLKID_FCLK_DIV2_DIV 2 >> +#define CLKID_FCLK_DIV3_DIV 4 >> +#define CLKID_FCLK_DIV4_DIV 6 >> +#define CLKID_FCLK_DIV5_DIV 8 >> +#define CLKID_FCLK_DIV7_DIV 10 >> +#define CLKID_FCLK_DIV2P5_DIV 12 >> +#define CLKID_GP0_PLL_DCO 14 >> +#define CLKID_HIFI_PLL_DCO 16 >> +#define CLKID_HDMI_PLL_DCO 18 >> +#define CLKID_HDMI_PLL_OD 19 >> +#define CLKID_MPLL_50M_DIV 21 >> +#define CLKID_MPLL_PREDIV 23 >> +#define CLKID_MPLL0_DIV 24 >> +#define CLKID_MPLL1_DIV 26 >> +#define CLKID_MPLL2_DIV 28 >> +#define CLKID_MPLL3_DIV 30 >> + >> +#define NR_PLL_CLKS 32 >> +/* include the CLKIDs that have been made part of the DT binding */ >> +#include <dt-bindings/clock/amlogic,s4-pll-clkc.h> >> + >> +#endif /* __MESON_S4_PLL_H__ */ >
On 2023/6/6 22:02, Jerome Brunet wrote: > [ EXTERNAL EMAIL ] > > On Wed 17 May 2023 at 15:02, Yu Tu <yu.tu@amlogic.com> wrote: > >> Add the S4 PLL clock controller driver in the s4 SoC family. >> >> Signed-off-by: Yu Tu <yu.tu@amlogic.com> >> --- >> drivers/clk/meson/Kconfig | 13 + >> drivers/clk/meson/Makefile | 1 + >> drivers/clk/meson/s4-pll.c | 907 +++++++++++++++++++++++++++++++++++++ >> drivers/clk/meson/s4-pll.h | 87 ++++ >> 4 files changed, 1008 insertions(+) >> create mode 100644 drivers/clk/meson/s4-pll.c >> create mode 100644 drivers/clk/meson/s4-pll.h >> >> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig >> index fc002c155bc3..a663c90a3f3b 100644 >> --- a/drivers/clk/meson/Kconfig >> +++ b/drivers/clk/meson/Kconfig >> @@ -115,4 +115,17 @@ config COMMON_CLK_G12A >> help >> Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2 >> devices, aka g12a. Say Y if you want peripherals to work. >> + >> +config COMMON_CLK_S4_PLL >> + tristate "S4 SoC PLL clock controllers support" >> + depends on ARM64 >> + default y >> + select COMMON_CLK_MESON_MPLL >> + select COMMON_CLK_MESON_PLL >> + select COMMON_CLK_MESON_REGMAP >> + help >> + Support for the pll clock controller on Amlogic S805X2 and S905Y4 devices, >> + aka s4. Amlogic S805X2 and S905Y4 devices include AQ222 and AQ229. >> + Say Y if you want the board to work, because plls are the parent of most >> + peripherals. >> endmenu >> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile >> index 6eca2a406ee3..376f49cc13f1 100644 >> --- a/drivers/clk/meson/Makefile >> +++ b/drivers/clk/meson/Makefile >> @@ -19,3 +19,4 @@ obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o >> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o >> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o >> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o >> +obj-$(CONFIG_COMMON_CLK_S4_PLL) += s4-pll.o >> diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c >> new file mode 100644 >> index 000000000000..c70a33586bcd >> --- /dev/null >> +++ b/drivers/clk/meson/s4-pll.c >> @@ -0,0 +1,907 @@ >> +// SPDX-License-Identifier: GPL-2.0+ >> +/* >> + * Amlogic Meson-S4 PLL Clock Controller Driver >> + * >> + * Copyright (c) 2021 Amlogic, inc. >> + * Author: Yu Tu <yu.tu@amlogic.com> >> + */ >> + >> +#include <linux/clk-provider.h> >> +#include <linux/of_device.h> >> +#include <linux/platform_device.h> >> + >> +#include "clk-mpll.h" >> +#include "clk-pll.h" >> +#include "clk-regmap.h" >> +#include "s4-pll.h" >> + >> +static DEFINE_SPINLOCK(meson_clk_lock); >> + >> +static struct clk_regmap s4_fixed_pll_dco = { >> + .data = &(struct meson_clk_pll_data){ >> + .en = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 28, >> + .width = 1, >> + }, >> + .m = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 0, >> + .width = 8, >> + }, >> + .n = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 10, >> + .width = 5, >> + }, >> + .frac = { >> + .reg_off = ANACTRL_FIXPLL_CTRL1, >> + .shift = 0, >> + .width = 17, >> + }, >> + .l = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 31, >> + .width = 1, >> + }, >> + .rst = { >> + .reg_off = ANACTRL_FIXPLL_CTRL0, >> + .shift = 29, >> + .width = 1, >> + }, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fixed_pll_dco", >> + /* >> + * This clock is a fixed value (4GHz) that is initialized by ROMcode. > > Really, isn't it the bl2 doing that ? Yes. The first step is ROMcode initialization. bl2 can also be initialized if any parameters need to be adjusted. > >> + * This clock won't ever change at runtime. >> + * The chip design determines that this clock cannot be changed after >> + * initialization. To prevent system crash caused by changing >> + * fixed related register in kernel phase. This register is not >> + * writable in the kernel phase. >> + * So we use ro_ops. >> + */ > > This explanation is good but we don't need mutliple copy/paste or > "please refer" for the same thing. That is very verbose (I think I > already commented on this previously) > > Just make one comment block, that clearly explain your intent and that > it applies to the fixed_pll group Okay. > >> + .ops = &meson_clk_pll_ro_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", } >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fixed_pll = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_FIXPLL_CTRL0, >> + .shift = 16, >> + .width = 2, >> + .flags = CLK_DIVIDER_POWER_OF_TWO, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fixed_pll", >> + /* >> + * This clock is a fixed value (2GHz) that is initialized by ROMcode. >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_divider_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fixed_pll_dco.hw >> + }, >> + .num_parents = 1, >> + /* >> + * This clock won't ever change at runtime so >> + * CLK_SET_RATE_PARENT is not required >> + */ >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div2_div = { >> + .mult = 1, >> + .div = 2, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div2_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div2 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 24, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div2", >> + /* >> + * This clock is a fixed value (1GHz) that is initialized by ROMcode. >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div2_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div3_div = { >> + .mult = 1, >> + .div = 3, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div3_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div3 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 20, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div3", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div3_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div4_div = { >> + .mult = 1, >> + .div = 4, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div4_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div4 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 21, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div4", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ > > While div2 and div3 got an explanation from previous SoCs, they others - > like div4/div7/etc ... - have been able to cope with rw ops so far. > > Why is the S4 different for all these clocks ? The chip was changed fixed pll for security reasons. Fixed PLL registers are not writable in the kernel phase. Write of fixed PLL-related register will cause the system to crash. > > Requiring RO ops (or fishy clock flags) is usually a sign that a clock > is used without an appropriate driver. > >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div4_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div5_div = { >> + .mult = 1, >> + .div = 5, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div5_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div5 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 22, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div5", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div5_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div7_div = { >> + .mult = 1, >> + .div = 7, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div7_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div7 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 23, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div7", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div7_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_fclk_div2p5_div = { >> + .mult = 2, >> + .div = 5, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div2p5_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fixed_pll.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_fclk_div2p5 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_FIXPLL_CTRL1, >> + .bit_idx = 25, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "fclk_div2p5", >> + /* >> + * For more information, please refer to s4_fixed_pll_dco. >> + */ >> + .ops = &clk_regmap_gate_ro_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fclk_div2p5_div.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static const struct pll_mult_range s4_gp0_pll_mult_range = { >> + .min = 125, >> + .max = 250, >> +}; >> + >> +/* >> + * Internal gp0 pll emulation configuration parameters >> + */ >> +static const struct reg_sequence s4_gp0_init_regs[] = { >> + { .reg = ANACTRL_GP0PLL_CTRL1, .def = 0x00000000 }, >> + { .reg = ANACTRL_GP0PLL_CTRL2, .def = 0x00000000 }, >> + { .reg = ANACTRL_GP0PLL_CTRL3, .def = 0x48681c00 }, >> + { .reg = ANACTRL_GP0PLL_CTRL4, .def = 0x88770290 }, >> + { .reg = ANACTRL_GP0PLL_CTRL5, .def = 0x39272000 }, >> + { .reg = ANACTRL_GP0PLL_CTRL6, .def = 0x56540000 } >> +}; >> + >> +static struct clk_regmap s4_gp0_pll_dco = { >> + .data = &(struct meson_clk_pll_data){ >> + .en = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 28, >> + .width = 1, >> + }, >> + .m = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 0, >> + .width = 8, >> + }, >> + .n = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 10, >> + .width = 5, >> + }, >> + .frac = { >> + .reg_off = ANACTRL_GP0PLL_CTRL1, >> + .shift = 0, >> + .width = 17, >> + }, >> + .l = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 31, >> + .width = 1, >> + }, >> + .rst = { >> + .reg_off = ANACTRL_GP0PLL_CTRL0, >> + .shift = 29, >> + .width = 1, >> + }, >> + .range = &s4_gp0_pll_mult_range, >> + .init_regs = s4_gp0_init_regs, >> + .init_count = ARRAY_SIZE(s4_gp0_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "gp0_pll_dco", >> + .ops = &meson_clk_pll_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", } >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_gp0_pll = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_GP0PLL_CTRL0, >> + .shift = 16, >> + .width = 3, >> + .flags = (CLK_DIVIDER_POWER_OF_TWO | >> + CLK_DIVIDER_ROUND_CLOSEST), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "gp0_pll", >> + .ops = &clk_regmap_divider_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_gp0_pll_dco.hw >> + }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +/* >> + * Internal hifi pll emulation configuration parameters >> + */ >> +static const struct reg_sequence s4_hifi_init_regs[] = { >> + { .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x00010e56 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00000000 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x6a285c00 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x65771290 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL5, .def = 0x39272000 }, >> + { .reg = ANACTRL_HIFIPLL_CTRL6, .def = 0x56540000 } >> +}; >> + >> +static struct clk_regmap s4_hifi_pll_dco = { >> + .data = &(struct meson_clk_pll_data){ >> + .en = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 28, >> + .width = 1, >> + }, >> + .m = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 0, >> + .width = 8, >> + }, >> + .n = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 10, >> + .width = 5, >> + }, >> + .frac = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL1, >> + .shift = 0, >> + .width = 17, >> + }, >> + .l = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 31, >> + .width = 1, >> + }, >> + .rst = { >> + .reg_off = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 29, >> + .width = 1, >> + }, >> + .range = &s4_gp0_pll_mult_range, >> + .init_regs = s4_hifi_init_regs, >> + .init_count = ARRAY_SIZE(s4_hifi_init_regs), >> + .flags = CLK_MESON_PLL_ROUND_CLOSEST, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hifi_pll_dco", >> + .ops = &meson_clk_pll_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", } >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_hifi_pll = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_HIFIPLL_CTRL0, >> + .shift = 16, >> + .width = 2, >> + .flags = (CLK_DIVIDER_POWER_OF_TWO | >> + CLK_DIVIDER_ROUND_CLOSEST), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hifi_pll", >> + .ops = &clk_regmap_divider_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_hifi_pll_dco.hw >> + }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static struct clk_regmap s4_hdmi_pll_dco = { >> + .data = &(struct meson_clk_pll_data){ >> + .en = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 28, >> + .width = 1, >> + }, >> + .m = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 0, >> + .width = 8, >> + }, >> + .n = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 10, >> + .width = 5, >> + }, >> + .frac = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL1, >> + .shift = 0, >> + .width = 17, >> + }, >> + .l = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 31, >> + .width = 1, >> + }, >> + .rst = { >> + .reg_off = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 29, >> + .width = 1, >> + }, >> + .range = &s4_gp0_pll_mult_range, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hdmi_pll_dco", >> + .ops = &meson_clk_pll_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", } >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_hdmi_pll_od = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 16, >> + .width = 4, >> + .flags = CLK_DIVIDER_POWER_OF_TWO, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hdmi_pll_od", >> + .ops = &clk_regmap_divider_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_hdmi_pll_dco.hw >> + }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static struct clk_regmap s4_hdmi_pll = { >> + .data = &(struct clk_regmap_div_data){ >> + .offset = ANACTRL_HDMIPLL_CTRL0, >> + .shift = 20, >> + .width = 2, >> + .flags = CLK_DIVIDER_POWER_OF_TWO, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "hdmi_pll", >> + .ops = &clk_regmap_divider_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_hdmi_pll_od.hw >> + }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_mpll_50m_div = { >> + .mult = 1, >> + .div = 80, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll_50m_div", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fixed_pll_dco.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll_50m = { >> + .data = &(struct clk_regmap_mux_data){ >> + .offset = ANACTRL_FIXPLL_CTRL3, >> + .mask = 0x1, >> + .shift = 5, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll_50m", >> + .ops = &clk_regmap_mux_ro_ops, >> + .parent_data = (const struct clk_parent_data []) { >> + { .fw_name = "xtal", }, >> + { .hw = &s4_mpll_50m_div.hw }, >> + }, >> + .num_parents = 2, >> + }, >> +}; >> + >> +static struct clk_fixed_factor s4_mpll_prediv = { >> + .mult = 1, >> + .div = 2, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll_prediv", >> + .ops = &clk_fixed_factor_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_fixed_pll_dco.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static const struct reg_sequence s4_mpll0_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL2, .def = 0x40000033 } >> +}; >> + >> +static struct clk_regmap s4_mpll0_div = { >> + .data = &(struct meson_clk_mpll_data){ >> + .sdm = { >> + .reg_off = ANACTRL_MPLL_CTRL1, >> + .shift = 0, >> + .width = 14, >> + }, >> + .sdm_en = { >> + .reg_off = ANACTRL_MPLL_CTRL1, >> + .shift = 30, >> + .width = 1, >> + }, >> + .n2 = { >> + .reg_off = ANACTRL_MPLL_CTRL1, >> + .shift = 20, >> + .width = 9, >> + }, >> + .ssen = { >> + .reg_off = ANACTRL_MPLL_CTRL1, >> + .shift = 29, >> + .width = 1, >> + }, >> + .lock = &meson_clk_lock, >> + .init_regs = s4_mpll0_init_regs, >> + .init_count = ARRAY_SIZE(s4_mpll0_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll0_div", >> + .ops = &meson_clk_mpll_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_mpll_prediv.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll0 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_MPLL_CTRL1, >> + .bit_idx = 31, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll0", >> + .ops = &clk_regmap_gate_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_mpll0_div.hw }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static const struct reg_sequence s4_mpll1_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL4, .def = 0x40000033 } >> +}; >> + >> +static struct clk_regmap s4_mpll1_div = { >> + .data = &(struct meson_clk_mpll_data){ >> + .sdm = { >> + .reg_off = ANACTRL_MPLL_CTRL3, >> + .shift = 0, >> + .width = 14, >> + }, >> + .sdm_en = { >> + .reg_off = ANACTRL_MPLL_CTRL3, >> + .shift = 30, >> + .width = 1, >> + }, >> + .n2 = { >> + .reg_off = ANACTRL_MPLL_CTRL3, >> + .shift = 20, >> + .width = 9, >> + }, >> + .ssen = { >> + .reg_off = ANACTRL_MPLL_CTRL3, >> + .shift = 29, >> + .width = 1, >> + }, >> + .lock = &meson_clk_lock, >> + .init_regs = s4_mpll1_init_regs, >> + .init_count = ARRAY_SIZE(s4_mpll1_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll1_div", >> + .ops = &meson_clk_mpll_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_mpll_prediv.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll1 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_MPLL_CTRL3, >> + .bit_idx = 31, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll1", >> + .ops = &clk_regmap_gate_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_mpll1_div.hw }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static const struct reg_sequence s4_mpll2_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL6, .def = 0x40000033 } >> +}; >> + >> +static struct clk_regmap s4_mpll2_div = { >> + .data = &(struct meson_clk_mpll_data){ >> + .sdm = { >> + .reg_off = ANACTRL_MPLL_CTRL5, >> + .shift = 0, >> + .width = 14, >> + }, >> + .sdm_en = { >> + .reg_off = ANACTRL_MPLL_CTRL5, >> + .shift = 30, >> + .width = 1, >> + }, >> + .n2 = { >> + .reg_off = ANACTRL_MPLL_CTRL5, >> + .shift = 20, >> + .width = 9, >> + }, >> + .ssen = { >> + .reg_off = ANACTRL_MPLL_CTRL5, >> + .shift = 29, >> + .width = 1, >> + }, >> + .lock = &meson_clk_lock, >> + .init_regs = s4_mpll2_init_regs, >> + .init_count = ARRAY_SIZE(s4_mpll2_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll2_div", >> + .ops = &meson_clk_mpll_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_mpll_prediv.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll2 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_MPLL_CTRL5, >> + .bit_idx = 31, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll2", >> + .ops = &clk_regmap_gate_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_mpll2_div.hw }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +static const struct reg_sequence s4_mpll3_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL8, .def = 0x40000033 } >> +}; >> + >> +static struct clk_regmap s4_mpll3_div = { >> + .data = &(struct meson_clk_mpll_data){ >> + .sdm = { >> + .reg_off = ANACTRL_MPLL_CTRL7, >> + .shift = 0, >> + .width = 14, >> + }, >> + .sdm_en = { >> + .reg_off = ANACTRL_MPLL_CTRL7, >> + .shift = 30, >> + .width = 1, >> + }, >> + .n2 = { >> + .reg_off = ANACTRL_MPLL_CTRL7, >> + .shift = 20, >> + .width = 9, >> + }, >> + .ssen = { >> + .reg_off = ANACTRL_MPLL_CTRL7, >> + .shift = 29, >> + .width = 1, >> + }, >> + .lock = &meson_clk_lock, >> + .init_regs = s4_mpll3_init_regs, >> + .init_count = ARRAY_SIZE(s4_mpll3_init_regs), >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll3_div", >> + .ops = &meson_clk_mpll_ops, >> + .parent_hws = (const struct clk_hw *[]) { >> + &s4_mpll_prediv.hw >> + }, >> + .num_parents = 1, >> + }, >> +}; >> + >> +static struct clk_regmap s4_mpll3 = { >> + .data = &(struct clk_regmap_gate_data){ >> + .offset = ANACTRL_MPLL_CTRL7, >> + .bit_idx = 31, >> + }, >> + .hw.init = &(struct clk_init_data){ >> + .name = "mpll3", >> + .ops = &clk_regmap_gate_ops, >> + .parent_hws = (const struct clk_hw *[]) { &s4_mpll3_div.hw }, >> + .num_parents = 1, >> + .flags = CLK_SET_RATE_PARENT, >> + }, >> +}; >> + >> +/* Array of all clocks provided by this provider */ >> +static struct clk_hw_onecell_data s4_pll_hw_onecell_data = { >> + .hws = { >> + [CLKID_FIXED_PLL_DCO] = &s4_fixed_pll_dco.hw, >> + [CLKID_FIXED_PLL] = &s4_fixed_pll.hw, >> + [CLKID_FCLK_DIV2_DIV] = &s4_fclk_div2_div.hw, >> + [CLKID_FCLK_DIV2] = &s4_fclk_div2.hw, >> + [CLKID_FCLK_DIV3_DIV] = &s4_fclk_div3_div.hw, >> + [CLKID_FCLK_DIV3] = &s4_fclk_div3.hw, >> + [CLKID_FCLK_DIV4_DIV] = &s4_fclk_div4_div.hw, >> + [CLKID_FCLK_DIV4] = &s4_fclk_div4.hw, >> + [CLKID_FCLK_DIV5_DIV] = &s4_fclk_div5_div.hw, >> + [CLKID_FCLK_DIV5] = &s4_fclk_div5.hw, >> + [CLKID_FCLK_DIV7_DIV] = &s4_fclk_div7_div.hw, >> + [CLKID_FCLK_DIV7] = &s4_fclk_div7.hw, >> + [CLKID_FCLK_DIV2P5_DIV] = &s4_fclk_div2p5_div.hw, >> + [CLKID_FCLK_DIV2P5] = &s4_fclk_div2p5.hw, >> + [CLKID_GP0_PLL_DCO] = &s4_gp0_pll_dco.hw, >> + [CLKID_GP0_PLL] = &s4_gp0_pll.hw, >> + [CLKID_HIFI_PLL_DCO] = &s4_hifi_pll_dco.hw, >> + [CLKID_HIFI_PLL] = &s4_hifi_pll.hw, >> + [CLKID_HDMI_PLL_DCO] = &s4_hdmi_pll_dco.hw, >> + [CLKID_HDMI_PLL_OD] = &s4_hdmi_pll_od.hw, >> + [CLKID_HDMI_PLL] = &s4_hdmi_pll.hw, >> + [CLKID_MPLL_50M_DIV] = &s4_mpll_50m_div.hw, >> + [CLKID_MPLL_50M] = &s4_mpll_50m.hw, >> + [CLKID_MPLL_PREDIV] = &s4_mpll_prediv.hw, >> + [CLKID_MPLL0_DIV] = &s4_mpll0_div.hw, >> + [CLKID_MPLL0] = &s4_mpll0.hw, >> + [CLKID_MPLL1_DIV] = &s4_mpll1_div.hw, >> + [CLKID_MPLL1] = &s4_mpll1.hw, >> + [CLKID_MPLL2_DIV] = &s4_mpll2_div.hw, >> + [CLKID_MPLL2] = &s4_mpll2.hw, >> + [CLKID_MPLL3_DIV] = &s4_mpll3_div.hw, >> + [CLKID_MPLL3] = &s4_mpll3.hw, >> + [NR_PLL_CLKS] = NULL >> + }, >> + .num = NR_PLL_CLKS, >> +}; >> + >> +static struct clk_regmap *const s4_pll_clk_regmaps[] = { >> + &s4_fixed_pll_dco, >> + &s4_fixed_pll, >> + &s4_fclk_div2, >> + &s4_fclk_div3, >> + &s4_fclk_div4, >> + &s4_fclk_div5, >> + &s4_fclk_div7, >> + &s4_fclk_div2p5, >> + &s4_gp0_pll_dco, >> + &s4_gp0_pll, >> + &s4_hifi_pll_dco, >> + &s4_hifi_pll, >> + &s4_hdmi_pll_dco, >> + &s4_hdmi_pll_od, >> + &s4_hdmi_pll, >> + &s4_mpll_50m, >> + &s4_mpll0_div, >> + &s4_mpll0, >> + &s4_mpll1_div, >> + &s4_mpll1, >> + &s4_mpll2_div, >> + &s4_mpll2, >> + &s4_mpll3_div, >> + &s4_mpll3, >> +}; >> + >> +static const struct reg_sequence s4_init_regs[] = { >> + { .reg = ANACTRL_MPLL_CTRL0, .def = 0x00000543 }, >> +}; >> + >> +static struct regmap_config clkc_regmap_config = { >> + .reg_bits = 32, >> + .val_bits = 32, >> + .reg_stride = 4, >> +}; >> + >> +static int meson_s4_pll_probe(struct platform_device *pdev) >> +{ >> + struct device *dev = &pdev->dev; >> + struct regmap *regmap; >> + void __iomem *base; >> + int ret, i; >> + >> + base = devm_platform_ioremap_resource(pdev, 0); >> + if (IS_ERR(base)) >> + return PTR_ERR(base); >> + >> + regmap = devm_regmap_init_mmio(dev, base, &clkc_regmap_config); >> + if (IS_ERR(regmap)) >> + return PTR_ERR(regmap); >> + >> + ret = regmap_multi_reg_write(regmap, s4_init_regs, ARRAY_SIZE(s4_init_regs)); >> + if (ret) { >> + dev_err(dev, "Failed to init registers\n"); >> + return ret; >> + } >> + >> + /* Populate regmap for the regmap backed clocks */ >> + for (i = 0; i < ARRAY_SIZE(s4_pll_clk_regmaps); i++) >> + s4_pll_clk_regmaps[i]->map = regmap; >> + >> + for (i = 0; i < s4_pll_hw_onecell_data.num; i++) { >> + /* array might be sparse */ >> + if (!s4_pll_hw_onecell_data.hws[i]) >> + continue; >> + >> + ret = devm_clk_hw_register(dev, s4_pll_hw_onecell_data.hws[i]); >> + if (ret) { >> + dev_err(dev, "Clock registration failed\n"); >> + return ret; >> + } >> + } >> + >> + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, >> + &s4_pll_hw_onecell_data); >> +} >> + >> +static const struct of_device_id clkc_match_table[] = { >> + { >> + .compatible = "amlogic,s4-pll-clkc", >> + }, >> + {} >> +}; >> + >> +static struct platform_driver s4_driver = { >> + .probe = meson_s4_pll_probe, >> + .driver = { >> + .name = "s4-pll-clkc", >> + .of_match_table = clkc_match_table, >> + }, >> +}; >> + >> +module_platform_driver(s4_driver); >> +MODULE_LICENSE("GPL"); >> diff --git a/drivers/clk/meson/s4-pll.h b/drivers/clk/meson/s4-pll.h >> new file mode 100644 >> index 000000000000..021d9f8b2b30 >> --- /dev/null >> +++ b/drivers/clk/meson/s4-pll.h >> @@ -0,0 +1,87 @@ >> +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ >> +/* >> + * Copyright (c) 2021 Amlogic, inc. >> + * Author: Yu Tu <yu.tu@amlogic.com> >> + */ >> + >> +#ifndef __MESON_S4_PLL_H__ >> +#define __MESON_S4_PLL_H__ >> + >> +/* ANA_CTRL - Registers */ >> + >> +#define ANACTRL_FIXPLL_CTRL0 0x040 >> +#define ANACTRL_FIXPLL_CTRL1 0x044 >> +#define ANACTRL_FIXPLL_CTRL2 0x048 >> +#define ANACTRL_FIXPLL_CTRL3 0x04c >> +#define ANACTRL_FIXPLL_CTRL4 0x050 >> +#define ANACTRL_FIXPLL_CTRL5 0x054 >> +#define ANACTRL_FIXPLL_CTRL6 0x058 >> +#define ANACTRL_FIXPLL_STS 0x05c >> +#define ANACTRL_GP0PLL_CTRL0 0x080 >> +#define ANACTRL_GP0PLL_CTRL1 0x084 >> +#define ANACTRL_GP0PLL_CTRL2 0x088 >> +#define ANACTRL_GP0PLL_CTRL3 0x08c >> +#define ANACTRL_GP0PLL_CTRL4 0x090 >> +#define ANACTRL_GP0PLL_CTRL5 0x094 >> +#define ANACTRL_GP0PLL_CTRL6 0x098 >> +#define ANACTRL_GP0PLL_STS 0x09c >> +#define ANACTRL_HIFIPLL_CTRL0 0x100 >> +#define ANACTRL_HIFIPLL_CTRL1 0x104 >> +#define ANACTRL_HIFIPLL_CTRL2 0x108 >> +#define ANACTRL_HIFIPLL_CTRL3 0x10c >> +#define ANACTRL_HIFIPLL_CTRL4 0x110 >> +#define ANACTRL_HIFIPLL_CTRL5 0x114 >> +#define ANACTRL_HIFIPLL_CTRL6 0x118 >> +#define ANACTRL_HIFIPLL_STS 0x11c >> +#define ANACTRL_MPLL_CTRL0 0x180 >> +#define ANACTRL_MPLL_CTRL1 0x184 >> +#define ANACTRL_MPLL_CTRL2 0x188 >> +#define ANACTRL_MPLL_CTRL3 0x18c >> +#define ANACTRL_MPLL_CTRL4 0x190 >> +#define ANACTRL_MPLL_CTRL5 0x194 >> +#define ANACTRL_MPLL_CTRL6 0x198 >> +#define ANACTRL_MPLL_CTRL7 0x19c >> +#define ANACTRL_MPLL_CTRL8 0x1a0 >> +#define ANACTRL_MPLL_STS 0x1a4 >> +#define ANACTRL_HDMIPLL_CTRL0 0x1c0 >> +#define ANACTRL_HDMIPLL_CTRL1 0x1c4 >> +#define ANACTRL_HDMIPLL_CTRL2 0x1c8 >> +#define ANACTRL_HDMIPLL_CTRL3 0x1cc >> +#define ANACTRL_HDMIPLL_CTRL4 0x1d0 >> +#define ANACTRL_HDMIPLL_CTRL5 0x1d4 >> +#define ANACTRL_HDMIPLL_CTRL6 0x1d8 >> +#define ANACTRL_HDMIPLL_STS 0x1dc >> +#define ANACTRL_HDMIPLL_VLOCK 0x1e4 >> + >> +/* >> + * CLKID index values >> + * >> + * These indices are entirely contrived and do not map onto the hardware. >> + * It has now been decided to expose everything by default in the DT header: >> + * include/dt-bindings/clock/amlogic,s4-pll-clkc.h. Only the clocks ids we don't >> + * want to expose, such as the internal muxes and dividers of composite clocks, >> + * will remain defined here. >> + */ >> +#define CLKID_FIXED_PLL_DCO 0 >> +#define CLKID_FCLK_DIV2_DIV 2 >> +#define CLKID_FCLK_DIV3_DIV 4 >> +#define CLKID_FCLK_DIV4_DIV 6 >> +#define CLKID_FCLK_DIV5_DIV 8 >> +#define CLKID_FCLK_DIV7_DIV 10 >> +#define CLKID_FCLK_DIV2P5_DIV 12 >> +#define CLKID_GP0_PLL_DCO 14 >> +#define CLKID_HIFI_PLL_DCO 16 >> +#define CLKID_HDMI_PLL_DCO 18 >> +#define CLKID_HDMI_PLL_OD 19 >> +#define CLKID_MPLL_50M_DIV 21 >> +#define CLKID_MPLL_PREDIV 23 >> +#define CLKID_MPLL0_DIV 24 >> +#define CLKID_MPLL1_DIV 26 >> +#define CLKID_MPLL2_DIV 28 >> +#define CLKID_MPLL3_DIV 30 >> + >> +#define NR_PLL_CLKS 32 >> +/* include the CLKIDs that have been made part of the DT binding */ >> +#include <dt-bindings/clock/amlogic,s4-pll-clkc.h> >> + >> +#endif /* __MESON_S4_PLL_H__ */ >
>>> + >>> +static struct clk_regmap s4_fclk_div4 = { >>> + .data = &(struct clk_regmap_gate_data){ >>> + .offset = ANACTRL_FIXPLL_CTRL1, >>> + .bit_idx = 21, >>> + }, >>> + .hw.init = &(struct clk_init_data){ >>> + .name = "fclk_div4", >>> + /* >>> + * For more information, please refer to s4_fixed_pll_dco. >>> + */ >> While div2 and div3 got an explanation from previous SoCs, they others - >> like div4/div7/etc ... - have been able to cope with rw ops so far. >> Why is the S4 different for all these clocks ? > > The chip was changed fixed pll for security reasons. > > Fixed PLL registers are not writable in the kernel phase. Write of fixed > PLL-related register will cause the system to crash. > That is definitely worth mentionning >> Requiring RO ops (or fishy clock flags) is usually a sign that a clock >> is used without an appropriate driver. >> Neil is currently dealing with the dt-bindings, please * Adjust your patchset accordingly * Wait for his v2 to land, you'll need it. >>> + .ops = &clk_regmap_gate_ro_ops, >>> + .parent_hws = (const struct clk_hw *[]) { >>> + &s4_fclk_div4_div.hw >>> + }, >>> + .num_parents = 1, >>> + }, >>> +}; >>> +
On 2023/6/8 16:53, Jerome Brunet wrote: > [ EXTERNAL EMAIL ] > >>>> + >>>> +static struct clk_regmap s4_fclk_div4 = { >>>> + .data = &(struct clk_regmap_gate_data){ >>>> + .offset = ANACTRL_FIXPLL_CTRL1, >>>> + .bit_idx = 21, >>>> + }, >>>> + .hw.init = &(struct clk_init_data){ >>>> + .name = "fclk_div4", >>>> + /* >>>> + * For more information, please refer to s4_fixed_pll_dco. >>>> + */ >>> While div2 and div3 got an explanation from previous SoCs, they others - >>> like div4/div7/etc ... - have been able to cope with rw ops so far. >>> Why is the S4 different for all these clocks ? >> >> The chip was changed fixed pll for security reasons. >> >> Fixed PLL registers are not writable in the kernel phase. Write of fixed >> PLL-related register will cause the system to crash. >> > > That is definitely worth mentionning Can I understand that you agree with the use of "ro_ops" for FIXed PLL related clocks later? > >>> Requiring RO ops (or fishy clock flags) is usually a sign that a clock >>> is used without an appropriate driver. >>> > > Neil is currently dealing with the dt-bindings, please > * Adjust your patchset accordingly > * Wait for his v2 to land, you'll need it. OKay. > >>>> + .ops = &clk_regmap_gate_ro_ops, >>>> + .parent_hws = (const struct clk_hw *[]) { >>>> + &s4_fclk_div4_div.hw >>>> + }, >>>> + .num_parents = 1, >>>> + }, >>>> +}; >>>> +
Hello Jerome, On Thu, Jun 08, 2023 at 10:53:31AM +0200, Jerome Brunet wrote: > > >>> + > >>> +static struct clk_regmap s4_fclk_div4 = { > >>> + .data = &(struct clk_regmap_gate_data){ > >>> + .offset = ANACTRL_FIXPLL_CTRL1, > >>> + .bit_idx = 21, > >>> + }, > >>> + .hw.init = &(struct clk_init_data){ > >>> + .name = "fclk_div4", > >>> + /* > >>> + * For more information, please refer to s4_fixed_pll_dco. > >>> + */ > >> While div2 and div3 got an explanation from previous SoCs, they others - > >> like div4/div7/etc ... - have been able to cope with rw ops so far. > >> Why is the S4 different for all these clocks ? > > > > The chip was changed fixed pll for security reasons. > > > > Fixed PLL registers are not writable in the kernel phase. Write of fixed > > PLL-related register will cause the system to crash. > > > > That is definitely worth mentionning > > >> Requiring RO ops (or fishy clock flags) is usually a sign that a clock > >> is used without an appropriate driver. > >> > > Neil is currently dealing with the dt-bindings, please > * Adjust your patchset accordingly > * Wait for his v2 to land, you'll need it. > I saw Neil patch series with merging 'private' and 'public' clock bindings parts. Should I send the same patchset for a1 clocks after v6.5 landed? > >>> + .ops = &clk_regmap_gate_ro_ops, > >>> + .parent_hws = (const struct clk_hw *[]) { > >>> + &s4_fclk_div4_div.hw > >>> + }, > >>> + .num_parents = 1, > >>> + }, > >>> +}; > >>> + > > _______________________________________________ > linux-amlogic mailing list > linux-amlogic@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-amlogic
On Thu 08 Jun 2023 at 14:32, Dmitry Rokosov <ddrokosov@sberdevices.ru> wrote: >> >> Neil is currently dealing with the dt-bindings, please >> * Adjust your patchset accordingly >> * Wait for his v2 to land, you'll need it. >> > > I saw Neil patch series with merging 'private' and 'public' clock > bindings parts. Should I send the same patchset for a1 clocks after v6.5 > landed? > I think Neil's patchset is already dealing with the a1. We'll see if it can be part of this cycle PR. I don't want to rush anything and rc6 is coming this monday. It may have to wait for the next cycle.
On Thu, Jun 08, 2023 at 02:46:54PM +0200, Jerome Brunet wrote: > > On Thu 08 Jun 2023 at 14:32, Dmitry Rokosov <ddrokosov@sberdevices.ru> wrote: > >> > >> Neil is currently dealing with the dt-bindings, please > >> * Adjust your patchset accordingly > >> * Wait for his v2 to land, you'll need it. > >> > > > > I saw Neil patch series with merging 'private' and 'public' clock > > bindings parts. Should I send the same patchset for a1 clocks after v6.5 > > landed? > > > > I think Neil's patchset is already dealing with the a1. > > We'll see if it can be part of this cycle PR. I don't want to rush > anything and rc6 is coming this monday. It may have to wait for the next cycle. Alright, I understand. Please let me know if and when you need assistance, as I am ready to help.
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index fc002c155bc3..a663c90a3f3b 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -115,4 +115,17 @@ config COMMON_CLK_G12A help Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2 devices, aka g12a. Say Y if you want peripherals to work. + +config COMMON_CLK_S4_PLL + tristate "S4 SoC PLL clock controllers support" + depends on ARM64 + default y + select COMMON_CLK_MESON_MPLL + select COMMON_CLK_MESON_PLL + select COMMON_CLK_MESON_REGMAP + help + Support for the pll clock controller on Amlogic S805X2 and S905Y4 devices, + aka s4. Amlogic S805X2 and S905Y4 devices include AQ222 and AQ229. + Say Y if you want the board to work, because plls are the parent of most + peripherals. endmenu diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 6eca2a406ee3..376f49cc13f1 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o +obj-$(CONFIG_COMMON_CLK_S4_PLL) += s4-pll.o diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c new file mode 100644 index 000000000000..c70a33586bcd --- /dev/null +++ b/drivers/clk/meson/s4-pll.c @@ -0,0 +1,907 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic Meson-S4 PLL Clock Controller Driver + * + * Copyright (c) 2021 Amlogic, inc. + * Author: Yu Tu <yu.tu@amlogic.com> + */ + +#include <linux/clk-provider.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include "clk-mpll.h" +#include "clk-pll.h" +#include "clk-regmap.h" +#include "s4-pll.h" + +static DEFINE_SPINLOCK(meson_clk_lock); + +static struct clk_regmap s4_fixed_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = ANACTRL_FIXPLL_CTRL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = ANACTRL_FIXPLL_CTRL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = ANACTRL_FIXPLL_CTRL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = ANACTRL_FIXPLL_CTRL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = ANACTRL_FIXPLL_CTRL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = ANACTRL_FIXPLL_CTRL0, + .shift = 29, + .width = 1, + }, + }, + .hw.init = &(struct clk_init_data){ + .name = "fixed_pll_dco", + /* + * This clock is a fixed value (4GHz) that is initialized by ROMcode. + * This clock won't ever change at runtime. + * The chip design determines that this clock cannot be changed after + * initialization. To prevent system crash caused by changing + * fixed related register in kernel phase. This register is not + * writable in the kernel phase. + * So we use ro_ops. + */ + .ops = &meson_clk_pll_ro_ops, + .parent_data = (const struct clk_parent_data []) { + { .fw_name = "xtal", } + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_fixed_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = ANACTRL_FIXPLL_CTRL0, + .shift = 16, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "fixed_pll", + /* + * This clock is a fixed value (2GHz) that is initialized by ROMcode. + * For more information, please refer to s4_fixed_pll_dco. + */ + .ops = &clk_regmap_divider_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fixed_pll_dco.hw + }, + .num_parents = 1, + /* + * This clock won't ever change at runtime so + * CLK_SET_RATE_PARENT is not required + */ + }, +}; + +static struct clk_fixed_factor s4_fclk_div2_div = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2_div", + .ops = &clk_fixed_factor_ops, + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_fclk_div2 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_FIXPLL_CTRL1, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2", + /* + * This clock is a fixed value (1GHz) that is initialized by ROMcode. + * For more information, please refer to s4_fixed_pll_dco. + */ + .ops = &clk_regmap_gate_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fclk_div2_div.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor s4_fclk_div3_div = { + .mult = 1, + .div = 3, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div3_div", + .ops = &clk_fixed_factor_ops, + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_fclk_div3 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_FIXPLL_CTRL1, + .bit_idx = 20, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div3", + /* + * For more information, please refer to s4_fixed_pll_dco. + */ + .ops = &clk_regmap_gate_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fclk_div3_div.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor s4_fclk_div4_div = { + .mult = 1, + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div4_div", + .ops = &clk_fixed_factor_ops, + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_fclk_div4 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_FIXPLL_CTRL1, + .bit_idx = 21, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div4", + /* + * For more information, please refer to s4_fixed_pll_dco. + */ + .ops = &clk_regmap_gate_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fclk_div4_div.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor s4_fclk_div5_div = { + .mult = 1, + .div = 5, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div5_div", + .ops = &clk_fixed_factor_ops, + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_fclk_div5 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_FIXPLL_CTRL1, + .bit_idx = 22, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div5", + /* + * For more information, please refer to s4_fixed_pll_dco. + */ + .ops = &clk_regmap_gate_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fclk_div5_div.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor s4_fclk_div7_div = { + .mult = 1, + .div = 7, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div7_div", + .ops = &clk_fixed_factor_ops, + .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_fclk_div7 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_FIXPLL_CTRL1, + .bit_idx = 23, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div7", + /* + * For more information, please refer to s4_fixed_pll_dco. + */ + .ops = &clk_regmap_gate_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fclk_div7_div.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor s4_fclk_div2p5_div = { + .mult = 2, + .div = 5, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2p5_div", + .ops = &clk_fixed_factor_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fixed_pll.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_fclk_div2p5 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_FIXPLL_CTRL1, + .bit_idx = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2p5", + /* + * For more information, please refer to s4_fixed_pll_dco. + */ + .ops = &clk_regmap_gate_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fclk_div2p5_div.hw + }, + .num_parents = 1, + }, +}; + +static const struct pll_mult_range s4_gp0_pll_mult_range = { + .min = 125, + .max = 250, +}; + +/* + * Internal gp0 pll emulation configuration parameters + */ +static const struct reg_sequence s4_gp0_init_regs[] = { + { .reg = ANACTRL_GP0PLL_CTRL1, .def = 0x00000000 }, + { .reg = ANACTRL_GP0PLL_CTRL2, .def = 0x00000000 }, + { .reg = ANACTRL_GP0PLL_CTRL3, .def = 0x48681c00 }, + { .reg = ANACTRL_GP0PLL_CTRL4, .def = 0x88770290 }, + { .reg = ANACTRL_GP0PLL_CTRL5, .def = 0x39272000 }, + { .reg = ANACTRL_GP0PLL_CTRL6, .def = 0x56540000 } +}; + +static struct clk_regmap s4_gp0_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = ANACTRL_GP0PLL_CTRL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = ANACTRL_GP0PLL_CTRL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = ANACTRL_GP0PLL_CTRL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = ANACTRL_GP0PLL_CTRL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = ANACTRL_GP0PLL_CTRL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = ANACTRL_GP0PLL_CTRL0, + .shift = 29, + .width = 1, + }, + .range = &s4_gp0_pll_mult_range, + .init_regs = s4_gp0_init_regs, + .init_count = ARRAY_SIZE(s4_gp0_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "gp0_pll_dco", + .ops = &meson_clk_pll_ops, + .parent_data = (const struct clk_parent_data []) { + { .fw_name = "xtal", } + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_gp0_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = ANACTRL_GP0PLL_CTRL0, + .shift = 16, + .width = 3, + .flags = (CLK_DIVIDER_POWER_OF_TWO | + CLK_DIVIDER_ROUND_CLOSEST), + }, + .hw.init = &(struct clk_init_data){ + .name = "gp0_pll", + .ops = &clk_regmap_divider_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_gp0_pll_dco.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* + * Internal hifi pll emulation configuration parameters + */ +static const struct reg_sequence s4_hifi_init_regs[] = { + { .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x00010e56 }, + { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00000000 }, + { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x6a285c00 }, + { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x65771290 }, + { .reg = ANACTRL_HIFIPLL_CTRL5, .def = 0x39272000 }, + { .reg = ANACTRL_HIFIPLL_CTRL6, .def = 0x56540000 } +}; + +static struct clk_regmap s4_hifi_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = ANACTRL_HIFIPLL_CTRL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = ANACTRL_HIFIPLL_CTRL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = ANACTRL_HIFIPLL_CTRL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = ANACTRL_HIFIPLL_CTRL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = ANACTRL_HIFIPLL_CTRL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = ANACTRL_HIFIPLL_CTRL0, + .shift = 29, + .width = 1, + }, + .range = &s4_gp0_pll_mult_range, + .init_regs = s4_hifi_init_regs, + .init_count = ARRAY_SIZE(s4_hifi_init_regs), + .flags = CLK_MESON_PLL_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "hifi_pll_dco", + .ops = &meson_clk_pll_ops, + .parent_data = (const struct clk_parent_data []) { + { .fw_name = "xtal", } + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_hifi_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = ANACTRL_HIFIPLL_CTRL0, + .shift = 16, + .width = 2, + .flags = (CLK_DIVIDER_POWER_OF_TWO | + CLK_DIVIDER_ROUND_CLOSEST), + }, + .hw.init = &(struct clk_init_data){ + .name = "hifi_pll", + .ops = &clk_regmap_divider_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_hifi_pll_dco.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap s4_hdmi_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = ANACTRL_HDMIPLL_CTRL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = ANACTRL_HDMIPLL_CTRL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = ANACTRL_HDMIPLL_CTRL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = ANACTRL_HDMIPLL_CTRL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = ANACTRL_HDMIPLL_CTRL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = ANACTRL_HDMIPLL_CTRL0, + .shift = 29, + .width = 1, + }, + .range = &s4_gp0_pll_mult_range, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll_dco", + .ops = &meson_clk_pll_ops, + .parent_data = (const struct clk_parent_data []) { + { .fw_name = "xtal", } + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_hdmi_pll_od = { + .data = &(struct clk_regmap_div_data){ + .offset = ANACTRL_HDMIPLL_CTRL0, + .shift = 16, + .width = 4, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll_od", + .ops = &clk_regmap_divider_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_hdmi_pll_dco.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap s4_hdmi_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = ANACTRL_HDMIPLL_CTRL0, + .shift = 20, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll", + .ops = &clk_regmap_divider_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_hdmi_pll_od.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_fixed_factor s4_mpll_50m_div = { + .mult = 1, + .div = 80, + .hw.init = &(struct clk_init_data){ + .name = "mpll_50m_div", + .ops = &clk_fixed_factor_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fixed_pll_dco.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_mpll_50m = { + .data = &(struct clk_regmap_mux_data){ + .offset = ANACTRL_FIXPLL_CTRL3, + .mask = 0x1, + .shift = 5, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll_50m", + .ops = &clk_regmap_mux_ro_ops, + .parent_data = (const struct clk_parent_data []) { + { .fw_name = "xtal", }, + { .hw = &s4_mpll_50m_div.hw }, + }, + .num_parents = 2, + }, +}; + +static struct clk_fixed_factor s4_mpll_prediv = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "mpll_prediv", + .ops = &clk_fixed_factor_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_fixed_pll_dco.hw + }, + .num_parents = 1, + }, +}; + +static const struct reg_sequence s4_mpll0_init_regs[] = { + { .reg = ANACTRL_MPLL_CTRL2, .def = 0x40000033 } +}; + +static struct clk_regmap s4_mpll0_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = ANACTRL_MPLL_CTRL1, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = ANACTRL_MPLL_CTRL1, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = ANACTRL_MPLL_CTRL1, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = ANACTRL_MPLL_CTRL1, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + .init_regs = s4_mpll0_init_regs, + .init_count = ARRAY_SIZE(s4_mpll0_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll0_div", + .ops = &meson_clk_mpll_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_mpll_prediv.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_mpll0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_MPLL_CTRL1, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll0", + .ops = &clk_regmap_gate_ops, + .parent_hws = (const struct clk_hw *[]) { &s4_mpll0_div.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static const struct reg_sequence s4_mpll1_init_regs[] = { + { .reg = ANACTRL_MPLL_CTRL4, .def = 0x40000033 } +}; + +static struct clk_regmap s4_mpll1_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = ANACTRL_MPLL_CTRL3, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = ANACTRL_MPLL_CTRL3, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = ANACTRL_MPLL_CTRL3, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = ANACTRL_MPLL_CTRL3, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + .init_regs = s4_mpll1_init_regs, + .init_count = ARRAY_SIZE(s4_mpll1_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll1_div", + .ops = &meson_clk_mpll_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_mpll_prediv.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_mpll1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_MPLL_CTRL3, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll1", + .ops = &clk_regmap_gate_ops, + .parent_hws = (const struct clk_hw *[]) { &s4_mpll1_div.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static const struct reg_sequence s4_mpll2_init_regs[] = { + { .reg = ANACTRL_MPLL_CTRL6, .def = 0x40000033 } +}; + +static struct clk_regmap s4_mpll2_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = ANACTRL_MPLL_CTRL5, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = ANACTRL_MPLL_CTRL5, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = ANACTRL_MPLL_CTRL5, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = ANACTRL_MPLL_CTRL5, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + .init_regs = s4_mpll2_init_regs, + .init_count = ARRAY_SIZE(s4_mpll2_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll2_div", + .ops = &meson_clk_mpll_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_mpll_prediv.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_mpll2 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_MPLL_CTRL5, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll2", + .ops = &clk_regmap_gate_ops, + .parent_hws = (const struct clk_hw *[]) { &s4_mpll2_div.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static const struct reg_sequence s4_mpll3_init_regs[] = { + { .reg = ANACTRL_MPLL_CTRL8, .def = 0x40000033 } +}; + +static struct clk_regmap s4_mpll3_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = ANACTRL_MPLL_CTRL7, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = ANACTRL_MPLL_CTRL7, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = ANACTRL_MPLL_CTRL7, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = ANACTRL_MPLL_CTRL7, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + .init_regs = s4_mpll3_init_regs, + .init_count = ARRAY_SIZE(s4_mpll3_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll3_div", + .ops = &meson_clk_mpll_ops, + .parent_hws = (const struct clk_hw *[]) { + &s4_mpll_prediv.hw + }, + .num_parents = 1, + }, +}; + +static struct clk_regmap s4_mpll3 = { + .data = &(struct clk_regmap_gate_data){ + .offset = ANACTRL_MPLL_CTRL7, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll3", + .ops = &clk_regmap_gate_ops, + .parent_hws = (const struct clk_hw *[]) { &s4_mpll3_div.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* Array of all clocks provided by this provider */ +static struct clk_hw_onecell_data s4_pll_hw_onecell_data = { + .hws = { + [CLKID_FIXED_PLL_DCO] = &s4_fixed_pll_dco.hw, + [CLKID_FIXED_PLL] = &s4_fixed_pll.hw, + [CLKID_FCLK_DIV2_DIV] = &s4_fclk_div2_div.hw, + [CLKID_FCLK_DIV2] = &s4_fclk_div2.hw, + [CLKID_FCLK_DIV3_DIV] = &s4_fclk_div3_div.hw, + [CLKID_FCLK_DIV3] = &s4_fclk_div3.hw, + [CLKID_FCLK_DIV4_DIV] = &s4_fclk_div4_div.hw, + [CLKID_FCLK_DIV4] = &s4_fclk_div4.hw, + [CLKID_FCLK_DIV5_DIV] = &s4_fclk_div5_div.hw, + [CLKID_FCLK_DIV5] = &s4_fclk_div5.hw, + [CLKID_FCLK_DIV7_DIV] = &s4_fclk_div7_div.hw, + [CLKID_FCLK_DIV7] = &s4_fclk_div7.hw, + [CLKID_FCLK_DIV2P5_DIV] = &s4_fclk_div2p5_div.hw, + [CLKID_FCLK_DIV2P5] = &s4_fclk_div2p5.hw, + [CLKID_GP0_PLL_DCO] = &s4_gp0_pll_dco.hw, + [CLKID_GP0_PLL] = &s4_gp0_pll.hw, + [CLKID_HIFI_PLL_DCO] = &s4_hifi_pll_dco.hw, + [CLKID_HIFI_PLL] = &s4_hifi_pll.hw, + [CLKID_HDMI_PLL_DCO] = &s4_hdmi_pll_dco.hw, + [CLKID_HDMI_PLL_OD] = &s4_hdmi_pll_od.hw, + [CLKID_HDMI_PLL] = &s4_hdmi_pll.hw, + [CLKID_MPLL_50M_DIV] = &s4_mpll_50m_div.hw, + [CLKID_MPLL_50M] = &s4_mpll_50m.hw, + [CLKID_MPLL_PREDIV] = &s4_mpll_prediv.hw, + [CLKID_MPLL0_DIV] = &s4_mpll0_div.hw, + [CLKID_MPLL0] = &s4_mpll0.hw, + [CLKID_MPLL1_DIV] = &s4_mpll1_div.hw, + [CLKID_MPLL1] = &s4_mpll1.hw, + [CLKID_MPLL2_DIV] = &s4_mpll2_div.hw, + [CLKID_MPLL2] = &s4_mpll2.hw, + [CLKID_MPLL3_DIV] = &s4_mpll3_div.hw, + [CLKID_MPLL3] = &s4_mpll3.hw, + [NR_PLL_CLKS] = NULL + }, + .num = NR_PLL_CLKS, +}; + +static struct clk_regmap *const s4_pll_clk_regmaps[] = { + &s4_fixed_pll_dco, + &s4_fixed_pll, + &s4_fclk_div2, + &s4_fclk_div3, + &s4_fclk_div4, + &s4_fclk_div5, + &s4_fclk_div7, + &s4_fclk_div2p5, + &s4_gp0_pll_dco, + &s4_gp0_pll, + &s4_hifi_pll_dco, + &s4_hifi_pll, + &s4_hdmi_pll_dco, + &s4_hdmi_pll_od, + &s4_hdmi_pll, + &s4_mpll_50m, + &s4_mpll0_div, + &s4_mpll0, + &s4_mpll1_div, + &s4_mpll1, + &s4_mpll2_div, + &s4_mpll2, + &s4_mpll3_div, + &s4_mpll3, +}; + +static const struct reg_sequence s4_init_regs[] = { + { .reg = ANACTRL_MPLL_CTRL0, .def = 0x00000543 }, +}; + +static struct regmap_config clkc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int meson_s4_pll_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap; + void __iomem *base; + int ret, i; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, &clkc_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = regmap_multi_reg_write(regmap, s4_init_regs, ARRAY_SIZE(s4_init_regs)); + if (ret) { + dev_err(dev, "Failed to init registers\n"); + return ret; + } + + /* Populate regmap for the regmap backed clocks */ + for (i = 0; i < ARRAY_SIZE(s4_pll_clk_regmaps); i++) + s4_pll_clk_regmaps[i]->map = regmap; + + for (i = 0; i < s4_pll_hw_onecell_data.num; i++) { + /* array might be sparse */ + if (!s4_pll_hw_onecell_data.hws[i]) + continue; + + ret = devm_clk_hw_register(dev, s4_pll_hw_onecell_data.hws[i]); + if (ret) { + dev_err(dev, "Clock registration failed\n"); + return ret; + } + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &s4_pll_hw_onecell_data); +} + +static const struct of_device_id clkc_match_table[] = { + { + .compatible = "amlogic,s4-pll-clkc", + }, + {} +}; + +static struct platform_driver s4_driver = { + .probe = meson_s4_pll_probe, + .driver = { + .name = "s4-pll-clkc", + .of_match_table = clkc_match_table, + }, +}; + +module_platform_driver(s4_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/meson/s4-pll.h b/drivers/clk/meson/s4-pll.h new file mode 100644 index 000000000000..021d9f8b2b30 --- /dev/null +++ b/drivers/clk/meson/s4-pll.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2021 Amlogic, inc. + * Author: Yu Tu <yu.tu@amlogic.com> + */ + +#ifndef __MESON_S4_PLL_H__ +#define __MESON_S4_PLL_H__ + +/* ANA_CTRL - Registers */ + +#define ANACTRL_FIXPLL_CTRL0 0x040 +#define ANACTRL_FIXPLL_CTRL1 0x044 +#define ANACTRL_FIXPLL_CTRL2 0x048 +#define ANACTRL_FIXPLL_CTRL3 0x04c +#define ANACTRL_FIXPLL_CTRL4 0x050 +#define ANACTRL_FIXPLL_CTRL5 0x054 +#define ANACTRL_FIXPLL_CTRL6 0x058 +#define ANACTRL_FIXPLL_STS 0x05c +#define ANACTRL_GP0PLL_CTRL0 0x080 +#define ANACTRL_GP0PLL_CTRL1 0x084 +#define ANACTRL_GP0PLL_CTRL2 0x088 +#define ANACTRL_GP0PLL_CTRL3 0x08c +#define ANACTRL_GP0PLL_CTRL4 0x090 +#define ANACTRL_GP0PLL_CTRL5 0x094 +#define ANACTRL_GP0PLL_CTRL6 0x098 +#define ANACTRL_GP0PLL_STS 0x09c +#define ANACTRL_HIFIPLL_CTRL0 0x100 +#define ANACTRL_HIFIPLL_CTRL1 0x104 +#define ANACTRL_HIFIPLL_CTRL2 0x108 +#define ANACTRL_HIFIPLL_CTRL3 0x10c +#define ANACTRL_HIFIPLL_CTRL4 0x110 +#define ANACTRL_HIFIPLL_CTRL5 0x114 +#define ANACTRL_HIFIPLL_CTRL6 0x118 +#define ANACTRL_HIFIPLL_STS 0x11c +#define ANACTRL_MPLL_CTRL0 0x180 +#define ANACTRL_MPLL_CTRL1 0x184 +#define ANACTRL_MPLL_CTRL2 0x188 +#define ANACTRL_MPLL_CTRL3 0x18c +#define ANACTRL_MPLL_CTRL4 0x190 +#define ANACTRL_MPLL_CTRL5 0x194 +#define ANACTRL_MPLL_CTRL6 0x198 +#define ANACTRL_MPLL_CTRL7 0x19c +#define ANACTRL_MPLL_CTRL8 0x1a0 +#define ANACTRL_MPLL_STS 0x1a4 +#define ANACTRL_HDMIPLL_CTRL0 0x1c0 +#define ANACTRL_HDMIPLL_CTRL1 0x1c4 +#define ANACTRL_HDMIPLL_CTRL2 0x1c8 +#define ANACTRL_HDMIPLL_CTRL3 0x1cc +#define ANACTRL_HDMIPLL_CTRL4 0x1d0 +#define ANACTRL_HDMIPLL_CTRL5 0x1d4 +#define ANACTRL_HDMIPLL_CTRL6 0x1d8 +#define ANACTRL_HDMIPLL_STS 0x1dc +#define ANACTRL_HDMIPLL_VLOCK 0x1e4 + +/* + * CLKID index values + * + * These indices are entirely contrived and do not map onto the hardware. + * It has now been decided to expose everything by default in the DT header: + * include/dt-bindings/clock/amlogic,s4-pll-clkc.h. Only the clocks ids we don't + * want to expose, such as the internal muxes and dividers of composite clocks, + * will remain defined here. + */ +#define CLKID_FIXED_PLL_DCO 0 +#define CLKID_FCLK_DIV2_DIV 2 +#define CLKID_FCLK_DIV3_DIV 4 +#define CLKID_FCLK_DIV4_DIV 6 +#define CLKID_FCLK_DIV5_DIV 8 +#define CLKID_FCLK_DIV7_DIV 10 +#define CLKID_FCLK_DIV2P5_DIV 12 +#define CLKID_GP0_PLL_DCO 14 +#define CLKID_HIFI_PLL_DCO 16 +#define CLKID_HDMI_PLL_DCO 18 +#define CLKID_HDMI_PLL_OD 19 +#define CLKID_MPLL_50M_DIV 21 +#define CLKID_MPLL_PREDIV 23 +#define CLKID_MPLL0_DIV 24 +#define CLKID_MPLL1_DIV 26 +#define CLKID_MPLL2_DIV 28 +#define CLKID_MPLL3_DIV 30 + +#define NR_PLL_CLKS 32 +/* include the CLKIDs that have been made part of the DT binding */ +#include <dt-bindings/clock/amlogic,s4-pll-clkc.h> + +#endif /* __MESON_S4_PLL_H__ */
Add the S4 PLL clock controller driver in the s4 SoC family. Signed-off-by: Yu Tu <yu.tu@amlogic.com> --- drivers/clk/meson/Kconfig | 13 + drivers/clk/meson/Makefile | 1 + drivers/clk/meson/s4-pll.c | 907 +++++++++++++++++++++++++++++++++++++ drivers/clk/meson/s4-pll.h | 87 ++++ 4 files changed, 1008 insertions(+) create mode 100644 drivers/clk/meson/s4-pll.c create mode 100644 drivers/clk/meson/s4-pll.h