From 536559af6aae3c99ac44182039b4737faa6b3acb Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 13 Sep 2021 11:24:43 +0300 Subject: [PATCH 01/24] clk: imx: Remove unused helpers Remove all the helpers that are not referenced anywhere anymore. Most of them are not clk_hw based. The rest are passing the device as an argument and were intented for BLK_CTL driver usage, but that is not the case anymore since the BLK_CTL is (or will be) implemented outside of CCF. - imx_clk_divider2 - imx_clk_gate2_shared2 - imx_clk_gate3 - imx_clk_gate4 - imx_clk_frac_pll - imx_clk_sscg_pll - imx_clk_pll14xx - imx_clk_pll14xx - imx_clk_divider2_flags - imx_dev_clk_hw_gate - imx_dev_clk_hw_gate_shared - imx_clk_gate3_flags - imx_clk_gate4_flags - imx_dev_clk_hw_mux - imx_clk_mux2 - imx_dev_clk_hw_mux_flags - imx8m_clk_composite_flags - __imx8m_clk_composite - imx8m_clk_composite - imx8m_clk_composite_critical Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/1631521490-17171-2-git-send-email-abel.vesa@nxp.com Reviewed-by: Stephen Boyd --- drivers/clk/imx/clk.h | 103 ------------------------------------------ 1 file changed, 103 deletions(-) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index e144f983fd8c..4a755498cb17 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -88,9 +88,6 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_divider(name, parent, reg, shift, width) \ to_clk(imx_clk_hw_divider(name, parent, reg, shift, width)) -#define imx_clk_divider2(name, parent, reg, shift, width) \ - to_clk(imx_clk_hw_divider2(name, parent, reg, shift, width)) - #define imx_clk_divider_flags(name, parent, reg, shift, width, flags) \ to_clk(imx_clk_hw_divider_flags(name, parent, reg, shift, width, flags)) @@ -106,15 +103,6 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_gate2_flags(name, parent, reg, shift, flags) \ to_clk(imx_clk_hw_gate2_flags(name, parent, reg, shift, flags)) -#define imx_clk_gate2_shared2(name, parent, reg, shift, share_count) \ - to_clk(imx_clk_hw_gate2_shared2(name, parent, reg, shift, share_count)) - -#define imx_clk_gate3(name, parent, reg, shift) \ - to_clk(imx_clk_hw_gate3(name, parent, reg, shift)) - -#define imx_clk_gate4(name, parent, reg, shift) \ - to_clk(imx_clk_hw_gate4(name, parent, reg, shift)) - #define imx_clk_mux(name, reg, shift, width, parents, num_parents) \ to_clk(imx_clk_hw_mux(name, reg, shift, width, parents, num_parents)) @@ -124,20 +112,6 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_pllv2(name, parent, base) \ to_clk(imx_clk_hw_pllv2(name, parent, base)) -#define imx_clk_frac_pll(name, parent_name, base) \ - to_clk(imx_clk_hw_frac_pll(name, parent_name, base)) - -#define imx_clk_sscg_pll(name, parent_names, num_parents, parent,\ - bypass1, bypass2, base, flags) \ - to_clk(imx_clk_hw_sscg_pll(name, parent_names, num_parents, parent,\ - bypass1, bypass2, base, flags)) - -struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, - void __iomem *base, const struct imx_pll14xx_clk *pll_clk); - -#define imx_clk_pll14xx(name, parent_name, base, pll_clk) \ - to_clk(imx_clk_hw_pll14xx(name, parent_name, base, pll_clk)) - struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk); @@ -301,15 +275,6 @@ static inline struct clk_hw *imx_clk_hw_divider2(const char *name, const char *p reg, shift, width, 0, &imx_ccm_lock); } -static inline struct clk *imx_clk_divider2_flags(const char *name, - const char *parent, void __iomem *reg, u8 shift, u8 width, - unsigned long flags) -{ - return clk_register_divider(NULL, name, parent, - flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, width, 0, &imx_ccm_lock); -} - static inline struct clk_hw *imx_clk_hw_gate_flags(const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned long flags) { @@ -324,13 +289,6 @@ static inline struct clk_hw *imx_clk_hw_gate(const char *name, const char *paren shift, 0, &imx_ccm_lock); } -static inline struct clk_hw *imx_dev_clk_hw_gate(struct device *dev, const char *name, - const char *parent, void __iomem *reg, u8 shift) -{ - return clk_hw_register_gate(dev, name, parent, CLK_SET_RATE_PARENT, reg, - shift, 0, &imx_ccm_lock); -} - static inline struct clk_hw *imx_clk_hw_gate_dis(const char *name, const char *parent, void __iomem *reg, u8 shift) { @@ -376,16 +334,6 @@ static inline struct clk_hw *imx_clk_hw_gate2_shared2(const char *name, &imx_ccm_lock, share_count); } -static inline struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, - const char *name, const char *parent, - void __iomem *reg, u8 shift, - unsigned int *share_count) -{ - return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT | - CLK_OPS_PARENT_ENABLE, reg, shift, 0x1, - 0x1, 0, &imx_ccm_lock, share_count); -} - static inline struct clk *imx_clk_gate2_cgr(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 cgr_val) { @@ -410,9 +358,6 @@ static inline struct clk_hw *imx_clk_hw_gate3_flags(const char *name, reg, shift, 0, &imx_ccm_lock); } -#define imx_clk_gate3_flags(name, parent, reg, shift, flags) \ - to_clk(imx_clk_hw_gate3_flags(name, parent, reg, shift, flags)) - static inline struct clk_hw *imx_clk_hw_gate4(const char *name, const char *parent, void __iomem *reg, u8 shift) { @@ -430,9 +375,6 @@ static inline struct clk_hw *imx_clk_hw_gate4_flags(const char *name, reg, shift, 0x3, 0x3, 0, &imx_ccm_lock, NULL); } -#define imx_clk_gate4_flags(name, parent, reg, shift, flags) \ - to_clk(imx_clk_hw_gate4_flags(name, parent, reg, shift, flags)) - static inline struct clk_hw *imx_clk_hw_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents) @@ -442,24 +384,6 @@ static inline struct clk_hw *imx_clk_hw_mux(const char *name, void __iomem *reg, width, 0, &imx_ccm_lock); } -static inline struct clk_hw *imx_dev_clk_hw_mux(struct device *dev, - const char *name, void __iomem *reg, u8 shift, - u8 width, const char * const *parents, int num_parents) -{ - return clk_hw_register_mux(dev, name, parents, num_parents, - CLK_SET_RATE_NO_REPARENT | CLK_SET_PARENT_GATE, - reg, shift, width, 0, &imx_ccm_lock); -} - -static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg, - u8 shift, u8 width, const char * const *parents, - int num_parents) -{ - return clk_register_mux(NULL, name, parents, num_parents, - CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, width, 0, &imx_ccm_lock); -} - static inline struct clk_hw *imx_clk_hw_mux2(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, @@ -513,19 +437,6 @@ static inline struct clk_hw *imx_clk_hw_mux_flags(const char *name, reg, shift, width, 0, &imx_ccm_lock); } -static inline struct clk_hw *imx_dev_clk_hw_mux_flags(struct device *dev, - const char *name, - void __iomem *reg, u8 shift, - u8 width, - const char * const *parents, - int num_parents, - unsigned long flags) -{ - return clk_hw_register_mux(dev, name, parents, num_parents, - flags | CLK_SET_RATE_NO_REPARENT, - reg, shift, width, 0, &imx_ccm_lock); -} - struct clk_hw *imx_clk_hw_cpu(const char *name, const char *parent_name, struct clk *div, struct clk *mux, struct clk *pll, struct clk *step); @@ -558,11 +469,6 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const char *name, IMX_COMPOSITE_CORE, \ CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) -#define imx8m_clk_composite_flags(name, parent_names, num_parents, reg, \ - flags) \ - to_clk(imx8m_clk_hw_composite_flags(name, parent_names, \ - num_parents, reg, 0, flags)) - #define __imx8m_clk_hw_composite(name, parent_names, reg, flags) \ imx8m_clk_hw_composite_flags(name, parent_names, \ ARRAY_SIZE(parent_names), reg, 0, \ @@ -579,21 +485,12 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const char *name, #define imx8m_clk_hw_fw_managed_composite_critical(name, parent_names, reg) \ __imx8m_clk_hw_fw_managed_composite(name, parent_names, reg, CLK_IS_CRITICAL) -#define __imx8m_clk_composite(name, parent_names, reg, flags) \ - to_clk(__imx8m_clk_hw_composite(name, parent_names, reg, flags)) - #define imx8m_clk_hw_composite(name, parent_names, reg) \ __imx8m_clk_hw_composite(name, parent_names, reg, 0) -#define imx8m_clk_composite(name, parent_names, reg) \ - __imx8m_clk_composite(name, parent_names, reg, 0) - #define imx8m_clk_hw_composite_critical(name, parent_names, reg) \ __imx8m_clk_hw_composite(name, parent_names, reg, CLK_IS_CRITICAL) -#define imx8m_clk_composite_critical(name, parent_names, reg) \ - __imx8m_clk_composite(name, parent_names, reg, CLK_IS_CRITICAL) - struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, const struct clk_div_table *table, From 4e6b7e75386b6ad59826cc38542ba6a99c0eda56 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 13 Sep 2021 11:24:44 +0300 Subject: [PATCH 02/24] clk: imx: Make mux/mux2 clk based helpers use clk_hw based ones Implement the clk based helpers as macros rather than as inline functions. Once all the provider drivers have switch to clk_hw, all the clk based macros will go away. Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/1631521490-17171-3-git-send-email-abel.vesa@nxp.com Reviewed-by: Stephen Boyd --- drivers/clk/imx/clk.h | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 4a755498cb17..371d8de417ef 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -106,6 +106,12 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_mux(name, reg, shift, width, parents, num_parents) \ to_clk(imx_clk_hw_mux(name, reg, shift, width, parents, num_parents)) +#define imx_clk_mux_flags(name, reg, shift, width, parents, num_parents, flags) \ + to_clk(imx_clk_hw_mux_flags(name, reg, shift, width, parents, num_parents, flags)) + +#define imx_clk_mux2_flags(name, reg, shift, width, parents, num_parents, flags) \ + to_clk(imx_clk_hw_mux2_flags(name, reg, shift, width, parents, num_parents, flags)) + #define imx_clk_pllv1(type, name, parent, base) \ to_clk(imx_clk_hw_pllv1(type, name, parent, base)) @@ -395,16 +401,6 @@ static inline struct clk_hw *imx_clk_hw_mux2(const char *name, void __iomem *reg reg, shift, width, 0, &imx_ccm_lock); } -static inline struct clk *imx_clk_mux_flags(const char *name, - void __iomem *reg, u8 shift, u8 width, - const char * const *parents, int num_parents, - unsigned long flags) -{ - return clk_register_mux(NULL, name, parents, num_parents, - flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0, - &imx_ccm_lock); -} - static inline struct clk_hw *imx_clk_hw_mux2_flags(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, @@ -415,16 +411,6 @@ static inline struct clk_hw *imx_clk_hw_mux2_flags(const char *name, reg, shift, width, 0, &imx_ccm_lock); } -static inline struct clk *imx_clk_mux2_flags(const char *name, - void __iomem *reg, u8 shift, u8 width, - const char * const *parents, - int num_parents, unsigned long flags) -{ - return clk_register_mux(NULL, name, parents, num_parents, - flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, width, 0, &imx_ccm_lock); -} - static inline struct clk_hw *imx_clk_hw_mux_flags(const char *name, void __iomem *reg, u8 shift, u8 width, From f121cca26cccfcc7ba4de53dda95f18be3f68840 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 13 Sep 2021 11:24:45 +0300 Subject: [PATCH 03/24] clk: imx: Rework all clk_hw_register_gate wrappers Instead of having multiple inline functions that were calling clk_hw_register_gate, implement a generic low-level __imx_clk_hw_gate and implement the rest as macros that pass on as arguments whatever is needed in each case. Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/1631521490-17171-4-git-send-email-abel.vesa@nxp.com Reviewed-by: Stephen Boyd --- drivers/clk/imx/clk.h | 64 ++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 371d8de417ef..2928ce40434e 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -118,6 +118,24 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_pllv2(name, parent, base) \ to_clk(imx_clk_hw_pllv2(name, parent, base)) +#define imx_clk_hw_gate(name, parent, reg, shift) \ + imx_clk_hw_gate_flags(name, parent, reg, shift, 0) + +#define imx_clk_hw_gate_dis(name, parent, reg, shift) \ + imx_clk_hw_gate_dis_flags(name, parent, reg, shift, 0) + +#define imx_clk_hw_gate_dis_flags(name, parent, reg, shift, flags) \ + __imx_clk_hw_gate(name, parent, reg, shift, flags, CLK_GATE_SET_TO_DISABLE) + +#define imx_clk_hw_gate_flags(name, parent, reg, shift, flags) \ + __imx_clk_hw_gate(name, parent, reg, shift, flags, 0) + +#define imx_clk_hw_gate3(name, parent, reg, shift) \ + imx_clk_hw_gate3_flags(name, parent, reg, shift, 0) + +#define imx_clk_hw_gate3_flags(name, parent, reg, shift, flags) \ + __imx_clk_hw_gate(name, parent, reg, shift, flags | CLK_OPS_PARENT_ENABLE, 0) + struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk); @@ -281,32 +299,13 @@ static inline struct clk_hw *imx_clk_hw_divider2(const char *name, const char *p reg, shift, width, 0, &imx_ccm_lock); } -static inline struct clk_hw *imx_clk_hw_gate_flags(const char *name, const char *parent, - void __iomem *reg, u8 shift, unsigned long flags) +static inline struct clk_hw *__imx_clk_hw_gate(const char *name, const char *parent, + void __iomem *reg, u8 shift, + unsigned long flags, + unsigned long clk_gate_flags) { return clk_hw_register_gate(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg, - shift, 0, &imx_ccm_lock); -} - -static inline struct clk_hw *imx_clk_hw_gate(const char *name, const char *parent, - void __iomem *reg, u8 shift) -{ - return clk_hw_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, 0, &imx_ccm_lock); -} - -static inline struct clk_hw *imx_clk_hw_gate_dis(const char *name, const char *parent, - void __iomem *reg, u8 shift) -{ - return clk_hw_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, CLK_GATE_SET_TO_DISABLE, &imx_ccm_lock); -} - -static inline struct clk_hw *imx_clk_hw_gate_dis_flags(const char *name, const char *parent, - void __iomem *reg, u8 shift, unsigned long flags) -{ - return clk_hw_register_gate(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg, - shift, CLK_GATE_SET_TO_DISABLE, &imx_ccm_lock); + shift, clk_gate_flags, &imx_ccm_lock); } static inline struct clk_hw *imx_clk_hw_gate2(const char *name, const char *parent, @@ -347,23 +346,6 @@ static inline struct clk *imx_clk_gate2_cgr(const char *name, shift, cgr_val, 0x3, 0, &imx_ccm_lock, NULL); } -static inline struct clk_hw *imx_clk_hw_gate3(const char *name, const char *parent, - void __iomem *reg, u8 shift) -{ - return clk_hw_register_gate(NULL, name, parent, - CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, 0, &imx_ccm_lock); -} - -static inline struct clk_hw *imx_clk_hw_gate3_flags(const char *name, - const char *parent, void __iomem *reg, u8 shift, - unsigned long flags) -{ - return clk_hw_register_gate(NULL, name, parent, - flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, 0, &imx_ccm_lock); -} - static inline struct clk_hw *imx_clk_hw_gate4(const char *name, const char *parent, void __iomem *reg, u8 shift) { From 66173dbe9feabccb6a15f94100e8311323d0c2b8 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 13 Sep 2021 11:24:46 +0300 Subject: [PATCH 04/24] clk: imx: Rework all clk_hw_register_gate2 wrappers Instead of having multiple inline functions that were calling clk_hw_register_gate2, implement a generic low-level __imx_clk_hw_gate2 and implement the rest as macros that pass on as arguments whatever is needed in each case. Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/1631521490-17171-5-git-send-email-abel.vesa@nxp.com Reviewed-by: Stephen Boyd --- drivers/clk/imx/clk.h | 77 +++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 51 deletions(-) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 2928ce40434e..d90526ad0537 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -100,6 +100,9 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_gate2(name, parent, reg, shift) \ to_clk(imx_clk_hw_gate2(name, parent, reg, shift)) +#define imx_clk_gate2_cgr(name, parent, reg, shift, cgr_val) \ + to_clk(__imx_clk_hw_gate2(name, parent, reg, shift, cgr_val, 0, NULL)) + #define imx_clk_gate2_flags(name, parent, reg, shift, flags) \ to_clk(imx_clk_hw_gate2_flags(name, parent, reg, shift, flags)) @@ -121,6 +124,9 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_hw_gate(name, parent, reg, shift) \ imx_clk_hw_gate_flags(name, parent, reg, shift, 0) +#define imx_clk_hw_gate2(name, parent, reg, shift) \ + imx_clk_hw_gate2_flags(name, parent, reg, shift, 0) + #define imx_clk_hw_gate_dis(name, parent, reg, shift) \ imx_clk_hw_gate_dis_flags(name, parent, reg, shift, 0) @@ -130,12 +136,27 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_hw_gate_flags(name, parent, reg, shift, flags) \ __imx_clk_hw_gate(name, parent, reg, shift, flags, 0) +#define imx_clk_hw_gate2_flags(name, parent, reg, shift, flags) \ + __imx_clk_hw_gate2(name, parent, reg, shift, 0x3, flags, NULL) + +#define imx_clk_hw_gate2_shared(name, parent, reg, shift, shared_count) \ + __imx_clk_hw_gate2(name, parent, reg, shift, 0x3, 0, shared_count) + +#define imx_clk_hw_gate2_shared2(name, parent, reg, shift, shared_count) \ + __imx_clk_hw_gate2(name, parent, reg, shift, 0x3, CLK_OPS_PARENT_ENABLE, shared_count) + #define imx_clk_hw_gate3(name, parent, reg, shift) \ imx_clk_hw_gate3_flags(name, parent, reg, shift, 0) #define imx_clk_hw_gate3_flags(name, parent, reg, shift, flags) \ __imx_clk_hw_gate(name, parent, reg, shift, flags | CLK_OPS_PARENT_ENABLE, 0) +#define imx_clk_hw_gate4(name, parent, reg, shift) \ + imx_clk_hw_gate4_flags(name, parent, reg, shift, 0) + +#define imx_clk_hw_gate4_flags(name, parent, reg, shift, flags) \ + imx_clk_hw_gate2_flags(name, parent, reg, shift, flags | CLK_OPS_PARENT_ENABLE) + struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk); @@ -308,59 +329,13 @@ static inline struct clk_hw *__imx_clk_hw_gate(const char *name, const char *par shift, clk_gate_flags, &imx_ccm_lock); } -static inline struct clk_hw *imx_clk_hw_gate2(const char *name, const char *parent, - void __iomem *reg, u8 shift) -{ - return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, 0x3, 0x3, 0, &imx_ccm_lock, NULL); -} - -static inline struct clk_hw *imx_clk_hw_gate2_flags(const char *name, const char *parent, - void __iomem *reg, u8 shift, unsigned long flags) +static inline struct clk_hw *__imx_clk_hw_gate2(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 cgr_val, + unsigned long flags, + unsigned int *share_count) { return clk_hw_register_gate2(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg, - shift, 0x3, 0x3, 0, &imx_ccm_lock, NULL); -} - -static inline struct clk_hw *imx_clk_hw_gate2_shared(const char *name, - const char *parent, void __iomem *reg, u8 shift, - unsigned int *share_count) -{ - return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, 0x3, 0x3, 0, &imx_ccm_lock, share_count); -} - -static inline struct clk_hw *imx_clk_hw_gate2_shared2(const char *name, - const char *parent, void __iomem *reg, u8 shift, - unsigned int *share_count) -{ - return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT | - CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0x3, 0, - &imx_ccm_lock, share_count); -} - -static inline struct clk *imx_clk_gate2_cgr(const char *name, - const char *parent, void __iomem *reg, u8 shift, u8 cgr_val) -{ - return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, cgr_val, 0x3, 0, &imx_ccm_lock, NULL); -} - -static inline struct clk_hw *imx_clk_hw_gate4(const char *name, const char *parent, - void __iomem *reg, u8 shift) -{ - return clk_hw_register_gate2(NULL, name, parent, - CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, 0x3, 0x3, 0, &imx_ccm_lock, NULL); -} - -static inline struct clk_hw *imx_clk_hw_gate4_flags(const char *name, - const char *parent, void __iomem *reg, u8 shift, - unsigned long flags) -{ - return clk_hw_register_gate2(NULL, name, parent, - flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, 0x3, 0x3, 0, &imx_ccm_lock, NULL); + shift, cgr_val, 0x3, 0, &imx_ccm_lock, share_count); } static inline struct clk_hw *imx_clk_hw_mux(const char *name, void __iomem *reg, From 004989ab28486489f816870d3a4b78d12793be7c Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 13 Sep 2021 11:24:47 +0300 Subject: [PATCH 05/24] clk: imx: Rework all clk_hw_register_mux wrappers Instead of having multiple inline functions that were calling clk_hw_register_mux, implement a generic low-level __imx_clk_hw_mux and implement the rest as macros that pass on as arguments whatever is needed in each case. Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/1631521490-17171-6-git-send-email-abel.vesa@nxp.com Reviewed-by: Stephen Boyd --- drivers/clk/imx/clk.h | 68 ++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index d90526ad0537..d48e0db75f33 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -121,6 +121,9 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_pllv2(name, parent, base) \ to_clk(imx_clk_hw_pllv2(name, parent, base)) +#define imx_clk_mux_flags(name, reg, shift, width, parents, num_parents, flags) \ + to_clk(imx_clk_hw_mux_flags(name, reg, shift, width, parents, num_parents, flags)) + #define imx_clk_hw_gate(name, parent, reg, shift) \ imx_clk_hw_gate_flags(name, parent, reg, shift, 0) @@ -157,6 +160,21 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_hw_gate4_flags(name, parent, reg, shift, flags) \ imx_clk_hw_gate2_flags(name, parent, reg, shift, flags | CLK_OPS_PARENT_ENABLE) +#define imx_clk_hw_mux2(name, reg, shift, width, parents, num_parents) \ + imx_clk_hw_mux2_flags(name, reg, shift, width, parents, num_parents, 0) + +#define imx_clk_hw_mux(name, reg, shift, width, parents, num_parents) \ + __imx_clk_hw_mux(name, reg, shift, width, parents, num_parents, 0, 0) + +#define imx_clk_hw_mux_flags(name, reg, shift, width, parents, num_parents, flags) \ + __imx_clk_hw_mux(name, reg, shift, width, parents, num_parents, flags, 0) + +#define imx_clk_hw_mux_ldb(name, reg, shift, width, parents, num_parents) \ + __imx_clk_hw_mux(name, reg, shift, width, parents, num_parents, CLK_SET_RATE_PARENT, CLK_MUX_READ_ONLY) + +#define imx_clk_hw_mux2_flags(name, reg, shift, width, parents, num_parents, flags) \ + __imx_clk_hw_mux(name, reg, shift, width, parents, num_parents, flags | CLK_OPS_PARENT_ENABLE, 0) + struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk); @@ -278,15 +296,6 @@ static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate) return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate); } -static inline struct clk_hw *imx_clk_hw_mux_ldb(const char *name, void __iomem *reg, - u8 shift, u8 width, const char * const *parents, - int num_parents) -{ - return clk_hw_register_mux(NULL, name, parents, num_parents, - CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg, - shift, width, CLK_MUX_READ_ONLY, &imx_ccm_lock); -} - static inline struct clk_hw *imx_clk_hw_fixed_factor(const char *name, const char *parent, unsigned int mult, unsigned int div) { @@ -338,46 +347,13 @@ static inline struct clk_hw *__imx_clk_hw_gate2(const char *name, const char *pa shift, cgr_val, 0x3, 0, &imx_ccm_lock, share_count); } -static inline struct clk_hw *imx_clk_hw_mux(const char *name, void __iomem *reg, +static inline struct clk_hw *__imx_clk_hw_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, - int num_parents) + int num_parents, unsigned long flags, unsigned long clk_mux_flags) { return clk_hw_register_mux(NULL, name, parents, num_parents, - CLK_SET_RATE_NO_REPARENT, reg, shift, - width, 0, &imx_ccm_lock); -} - -static inline struct clk_hw *imx_clk_hw_mux2(const char *name, void __iomem *reg, - u8 shift, u8 width, - const char * const *parents, - int num_parents) -{ - return clk_hw_register_mux(NULL, name, parents, num_parents, - CLK_SET_RATE_NO_REPARENT | - CLK_OPS_PARENT_ENABLE, - reg, shift, width, 0, &imx_ccm_lock); -} - -static inline struct clk_hw *imx_clk_hw_mux2_flags(const char *name, - void __iomem *reg, u8 shift, u8 width, - const char * const *parents, - int num_parents, unsigned long flags) -{ - return clk_hw_register_mux(NULL, name, parents, num_parents, - flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, width, 0, &imx_ccm_lock); -} - -static inline struct clk_hw *imx_clk_hw_mux_flags(const char *name, - void __iomem *reg, u8 shift, - u8 width, - const char * const *parents, - int num_parents, - unsigned long flags) -{ - return clk_hw_register_mux(NULL, name, parents, num_parents, - flags | CLK_SET_RATE_NO_REPARENT, - reg, shift, width, 0, &imx_ccm_lock); + flags | CLK_SET_RATE_NO_REPARENT, reg, shift, + width, clk_mux_flags, &imx_ccm_lock); } struct clk_hw *imx_clk_hw_cpu(const char *name, const char *parent_name, From b170586afc9514676b8217ccf7087f78a8e2febb Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 13 Sep 2021 11:24:48 +0300 Subject: [PATCH 06/24] clk: imx: Rework all clk_hw_register_divider wrappers Instead of having multiple inline functions that were calling clk_hw_register_divider, implement a generic low-level __imx_clk_hw_divider and implement the rest as macros that pass on as arguments whatever is needed in each case. Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/1631521490-17171-7-git-send-email-abel.vesa@nxp.com Reviewed-by: Stephen Boyd --- drivers/clk/imx/clk.h | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index d48e0db75f33..940f7d16c87a 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -175,6 +175,16 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_hw_mux2_flags(name, reg, shift, width, parents, num_parents, flags) \ __imx_clk_hw_mux(name, reg, shift, width, parents, num_parents, flags | CLK_OPS_PARENT_ENABLE, 0) +#define imx_clk_hw_divider(name, parent, reg, shift, width) \ + __imx_clk_hw_divider(name, parent, reg, shift, width, CLK_SET_RATE_PARENT) + +#define imx_clk_hw_divider2(name, parent, reg, shift, width) \ + __imx_clk_hw_divider(name, parent, reg, shift, width, \ + CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE) + +#define imx_clk_hw_divider_flags(name, parent, reg, shift, width, flags) \ + __imx_clk_hw_divider(name, parent, reg, shift, width, flags) + struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk); @@ -303,16 +313,7 @@ static inline struct clk_hw *imx_clk_hw_fixed_factor(const char *name, CLK_SET_RATE_PARENT, mult, div); } -static inline struct clk_hw *imx_clk_hw_divider(const char *name, - const char *parent, - void __iomem *reg, u8 shift, - u8 width) -{ - return clk_hw_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, - reg, shift, width, 0, &imx_ccm_lock); -} - -static inline struct clk_hw *imx_clk_hw_divider_flags(const char *name, +static inline struct clk_hw *__imx_clk_hw_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width, unsigned long flags) @@ -321,14 +322,6 @@ static inline struct clk_hw *imx_clk_hw_divider_flags(const char *name, reg, shift, width, 0, &imx_ccm_lock); } -static inline struct clk_hw *imx_clk_hw_divider2(const char *name, const char *parent, - void __iomem *reg, u8 shift, u8 width) -{ - return clk_hw_register_divider(NULL, name, parent, - CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, width, 0, &imx_ccm_lock); -} - static inline struct clk_hw *__imx_clk_hw_gate(const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned long flags, From a60fe746df94037151d101f0500f619cb6e1d1a0 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 13 Sep 2021 11:24:49 +0300 Subject: [PATCH 07/24] clk: imx: Rework all imx_clk_hw_composite wrappers Rather than having multiple different macros for each different type of imx8m_clk_hw_composite, implement them in such a way so we can take advantage the most of the already defined simpler types. Basically, we end up having one low-level __imx8m_clk_hw_composite function, a wrapper to simplify the parents related arguments called _imx8m_clk_hw_composite and then all the types can use those for each specific case. Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/1631521490-17171-8-git-send-email-abel.vesa@nxp.com Reviewed-by: Stephen Boyd --- drivers/clk/imx/clk-composite-8m.c | 4 +- drivers/clk/imx/clk.h | 74 ++++++++++++++++-------------- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index 04e728538cef..2dfd6149e528 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -171,7 +171,7 @@ static const struct clk_ops imx8m_clk_composite_mux_ops = { .determine_rate = imx8m_clk_composite_mux_determine_rate, }; -struct clk_hw *imx8m_clk_hw_composite_flags(const char *name, +struct clk_hw *__imx8m_clk_hw_composite(const char *name, const char * const *parent_names, int num_parents, void __iomem *reg, u32 composite_flags, @@ -246,4 +246,4 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const char *name, kfree(mux); return ERR_CAST(hw); } -EXPORT_SYMBOL_GPL(imx8m_clk_hw_composite_flags); +EXPORT_SYMBOL_GPL(__imx8m_clk_hw_composite); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 940f7d16c87a..0507b5ff0cf4 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -357,51 +357,55 @@ struct clk_hw *imx_clk_hw_cpu(const char *name, const char *parent_name, #define IMX_COMPOSITE_BUS BIT(1) #define IMX_COMPOSITE_FW_MANAGED BIT(2) -struct clk_hw *imx8m_clk_hw_composite_flags(const char *name, +#define IMX_COMPOSITE_CLK_FLAGS_DEFAULT \ + (CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) +#define IMX_COMPOSITE_CLK_FLAGS_CRITICAL \ + (IMX_COMPOSITE_CLK_FLAGS_DEFAULT | CLK_IS_CRITICAL) +#define IMX_COMPOSITE_CLK_FLAGS_GET_RATE_NO_CACHE \ + (IMX_COMPOSITE_CLK_FLAGS_DEFAULT | CLK_GET_RATE_NOCACHE) +#define IMX_COMPOSITE_CLK_FLAGS_CRITICAL_GET_RATE_NO_CACHE \ + (IMX_COMPOSITE_CLK_FLAGS_GET_RATE_NO_CACHE | CLK_IS_CRITICAL) + +struct clk_hw *__imx8m_clk_hw_composite(const char *name, const char * const *parent_names, int num_parents, void __iomem *reg, u32 composite_flags, unsigned long flags); -#define imx8m_clk_hw_composite_bus(name, parent_names, reg) \ - imx8m_clk_hw_composite_flags(name, parent_names, \ - ARRAY_SIZE(parent_names), reg, \ - IMX_COMPOSITE_BUS, \ - CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) - -#define imx8m_clk_hw_composite_bus_critical(name, parent_names, reg) \ - imx8m_clk_hw_composite_flags(name, parent_names, ARRAY_SIZE(parent_names), reg, \ - IMX_COMPOSITE_BUS, \ - CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE | CLK_IS_CRITICAL) - -#define imx8m_clk_hw_composite_core(name, parent_names, reg) \ - imx8m_clk_hw_composite_flags(name, parent_names, \ - ARRAY_SIZE(parent_names), reg, \ - IMX_COMPOSITE_CORE, \ - CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) - -#define __imx8m_clk_hw_composite(name, parent_names, reg, flags) \ - imx8m_clk_hw_composite_flags(name, parent_names, \ - ARRAY_SIZE(parent_names), reg, 0, \ - flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) - -#define __imx8m_clk_hw_fw_managed_composite(name, parent_names, reg, flags) \ - imx8m_clk_hw_composite_flags(name, parent_names, \ - ARRAY_SIZE(parent_names), reg, IMX_COMPOSITE_FW_MANAGED, \ - flags | CLK_GET_RATE_NOCACHE | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) - -#define imx8m_clk_hw_fw_managed_composite(name, parent_names, reg) \ - __imx8m_clk_hw_fw_managed_composite(name, parent_names, reg, 0) - -#define imx8m_clk_hw_fw_managed_composite_critical(name, parent_names, reg) \ - __imx8m_clk_hw_fw_managed_composite(name, parent_names, reg, CLK_IS_CRITICAL) +#define _imx8m_clk_hw_composite(name, parent_names, reg, composite_flags, flags) \ + __imx8m_clk_hw_composite(name, parent_names, \ + ARRAY_SIZE(parent_names), reg, composite_flags, flags) #define imx8m_clk_hw_composite(name, parent_names, reg) \ - __imx8m_clk_hw_composite(name, parent_names, reg, 0) + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_CORE, IMX_COMPOSITE_CLK_FLAGS_DEFAULT) #define imx8m_clk_hw_composite_critical(name, parent_names, reg) \ - __imx8m_clk_hw_composite(name, parent_names, reg, CLK_IS_CRITICAL) + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_CORE, IMX_COMPOSITE_CLK_FLAGS_CRITICAL) + +#define imx8m_clk_hw_composite_bus(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_BUS, IMX_COMPOSITE_CLK_FLAGS_DEFAULT) + +#define imx8m_clk_hw_composite_bus_critical(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_BUS, IMX_COMPOSITE_CLK_FLAGS_CRITICAL) + +#define imx8m_clk_hw_composite_core(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_CORE, IMX_COMPOSITE_CLK_FLAGS_DEFAULT) + +#define imx8m_clk_hw_fw_managed_composite(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_FW_MANAGED, \ + IMX_COMPOSITE_CLK_FLAGS_GET_RATE_NO_CACHE) + +#define imx8m_clk_hw_fw_managed_composite_critical(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_FW_MANAGED, \ + IMX_COMPOSITE_CLK_FLAGS_CRITICAL_GET_RATE_NO_CACHE) struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, From 6b4a6b7f07883d96e10924b8e702cf62acdc9b90 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 13 Sep 2021 11:24:50 +0300 Subject: [PATCH 08/24] clk: imx: Rework imx_clk_hw_pll14xx wrapper It looks much cleaner to just have a macro compared to having a function that passes NULL as dev to the lower-level imx_dev_clk_hw_pll14xx. Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/1631521490-17171-9-git-send-email-abel.vesa@nxp.com Reviewed-by: Stephen Boyd --- drivers/clk/imx/clk.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 0507b5ff0cf4..e36d01411a4e 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -185,6 +185,9 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_hw_divider_flags(name, parent, reg, shift, width, flags) \ __imx_clk_hw_divider(name, parent, reg, shift, width, flags) +#define imx_clk_hw_pll14xx(name, parent_name, base, pll_clk) \ + imx_dev_clk_hw_pll14xx(NULL, name, parent_name, base, pll_clk) + struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk); @@ -294,13 +297,6 @@ static inline struct clk *to_clk(struct clk_hw *hw) return hw->clk; } -static inline struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char *parent_name, - void __iomem *base, - const struct imx_pll14xx_clk *pll_clk) -{ - return imx_dev_clk_hw_pll14xx(NULL, name, parent_name, base, pll_clk); -} - static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate) { return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate); From d48f12d9ae73c76f792eaaf057d4e64900730c0e Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Tue, 14 Sep 2021 14:52:00 +0800 Subject: [PATCH 09/24] dt-bindings: clock: Add imx8ulp clock support Add the clock dt-binding file for i.MX8ULP. For pcc node, it will also be used as a reset controller, so add the '#reset-cells' property description and add the pcc reset IDs. Signed-off-by: Jacky Bai Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210914065208.3582128-2-ping.bai@nxp.com Signed-off-by: Abel Vesa --- .../bindings/clock/imx8ulp-cgc-clock.yaml | 43 +++ .../bindings/clock/imx8ulp-pcc-clock.yaml | 50 ++++ include/dt-bindings/clock/imx8ulp-clock.h | 258 ++++++++++++++++++ include/dt-bindings/reset/imx8ulp-pcc-reset.h | 59 ++++ 4 files changed, 410 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/imx8ulp-cgc-clock.yaml create mode 100644 Documentation/devicetree/bindings/clock/imx8ulp-pcc-clock.yaml create mode 100644 include/dt-bindings/clock/imx8ulp-clock.h create mode 100644 include/dt-bindings/reset/imx8ulp-pcc-reset.h diff --git a/Documentation/devicetree/bindings/clock/imx8ulp-cgc-clock.yaml b/Documentation/devicetree/bindings/clock/imx8ulp-cgc-clock.yaml new file mode 100644 index 000000000000..71f7186b135b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx8ulp-cgc-clock.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/imx8ulp-cgc-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP i.MX8ULP Clock Generation & Control(CGC) Module Binding + +maintainers: + - Jacky Bai + +description: | + On i.MX8ULP, The clock sources generation, distribution and management is + under the control of several CGCs & PCCs modules. The CGC modules generate + and distribute clocks on the device. + +properties: + compatible: + enum: + - fsl,imx8ulp-cgc1 + - fsl,imx8ulp-cgc2 + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false + +examples: + # Clock Generation & Control Module node: + - | + clock-controller@292c0000 { + compatible = "fsl,imx8ulp-cgc1"; + reg = <0x292c0000 0x10000>; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/imx8ulp-pcc-clock.yaml b/Documentation/devicetree/bindings/clock/imx8ulp-pcc-clock.yaml new file mode 100644 index 000000000000..00612725bf8b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx8ulp-pcc-clock.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/imx8ulp-pcc-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP i.MX8ULP Peripheral Clock Controller(PCC) Module Binding + +maintainers: + - Jacky Bai + +description: | + On i.MX8ULP, The clock sources generation, distribution and management is + under the control of several CGCs & PCCs modules. The PCC modules control + software reset, clock selection, optional division and clock gating mode + for peripherals. + +properties: + compatible: + enum: + - fsl,imx8ulp-pcc3 + - fsl,imx8ulp-pcc4 + - fsl,imx8ulp-pcc5 + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + '#reset-cells': + const: 1 + +required: + - compatible + - reg + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + # Peripheral Clock Control Module node: + - | + clock-controller@292d0000 { + compatible = "fsl,imx8ulp-pcc3"; + reg = <0x292d0000 0x10000>; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/include/dt-bindings/clock/imx8ulp-clock.h b/include/dt-bindings/clock/imx8ulp-clock.h new file mode 100644 index 000000000000..953ecfe8ebcc --- /dev/null +++ b/include/dt-bindings/clock/imx8ulp-clock.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR MIT */ +/* + * Copyright 2021 NXP + */ + +#ifndef __DT_BINDINGS_CLOCK_IMX8ULP_H +#define __DT_BINDINGS_CLOCK_IMX8ULP_H + +#define IMX8ULP_CLK_DUMMY 0 + +/* CGC1 */ +#define IMX8ULP_CLK_SPLL2 5 +#define IMX8ULP_CLK_SPLL3 6 +#define IMX8ULP_CLK_A35_SEL 7 +#define IMX8ULP_CLK_A35_DIV 8 +#define IMX8ULP_CLK_SPLL2_PRE_SEL 9 +#define IMX8ULP_CLK_SPLL3_PRE_SEL 10 +#define IMX8ULP_CLK_SPLL3_PFD0 11 +#define IMX8ULP_CLK_SPLL3_PFD1 12 +#define IMX8ULP_CLK_SPLL3_PFD2 13 +#define IMX8ULP_CLK_SPLL3_PFD3 14 +#define IMX8ULP_CLK_SPLL3_PFD0_DIV1 15 +#define IMX8ULP_CLK_SPLL3_PFD0_DIV2 16 +#define IMX8ULP_CLK_SPLL3_PFD1_DIV1 17 +#define IMX8ULP_CLK_SPLL3_PFD1_DIV2 18 +#define IMX8ULP_CLK_SPLL3_PFD2_DIV1 19 +#define IMX8ULP_CLK_SPLL3_PFD2_DIV2 20 +#define IMX8ULP_CLK_SPLL3_PFD3_DIV1 21 +#define IMX8ULP_CLK_SPLL3_PFD3_DIV2 22 +#define IMX8ULP_CLK_NIC_SEL 23 +#define IMX8ULP_CLK_NIC_AD_DIVPLAT 24 +#define IMX8ULP_CLK_NIC_PER_DIVPLAT 25 +#define IMX8ULP_CLK_XBAR_SEL 26 +#define IMX8ULP_CLK_XBAR_AD_DIVPLAT 27 +#define IMX8ULP_CLK_XBAR_DIVBUS 28 +#define IMX8ULP_CLK_XBAR_AD_SLOW 29 +#define IMX8ULP_CLK_SOSC_DIV1 30 +#define IMX8ULP_CLK_SOSC_DIV2 31 +#define IMX8ULP_CLK_SOSC_DIV3 32 +#define IMX8ULP_CLK_FROSC_DIV1 33 +#define IMX8ULP_CLK_FROSC_DIV2 34 +#define IMX8ULP_CLK_FROSC_DIV3 35 +#define IMX8ULP_CLK_SPLL3_VCODIV 36 +#define IMX8ULP_CLK_SPLL3_PFD0_DIV1_GATE 37 +#define IMX8ULP_CLK_SPLL3_PFD0_DIV2_GATE 38 +#define IMX8ULP_CLK_SPLL3_PFD1_DIV1_GATE 39 +#define IMX8ULP_CLK_SPLL3_PFD1_DIV2_GATE 40 +#define IMX8ULP_CLK_SPLL3_PFD2_DIV1_GATE 41 +#define IMX8ULP_CLK_SPLL3_PFD2_DIV2_GATE 42 +#define IMX8ULP_CLK_SPLL3_PFD3_DIV1_GATE 43 +#define IMX8ULP_CLK_SPLL3_PFD3_DIV2_GATE 44 +#define IMX8ULP_CLK_SOSC_DIV1_GATE 45 +#define IMX8ULP_CLK_SOSC_DIV2_GATE 46 +#define IMX8ULP_CLK_SOSC_DIV3_GATE 47 +#define IMX8ULP_CLK_FROSC_DIV1_GATE 48 +#define IMX8ULP_CLK_FROSC_DIV2_GATE 49 +#define IMX8ULP_CLK_FROSC_DIV3_GATE 50 +#define IMX8ULP_CLK_SAI4_SEL 51 +#define IMX8ULP_CLK_SAI5_SEL 52 +#define IMX8ULP_CLK_AUD_CLK1 53 +#define IMX8ULP_CLK_ARM 54 +#define IMX8ULP_CLK_ENET_TS_SEL 55 + +#define IMX8ULP_CLK_CGC1_END 56 + +/* CGC2 */ +#define IMX8ULP_CLK_PLL4_PRE_SEL 0 +#define IMX8ULP_CLK_PLL4 1 +#define IMX8ULP_CLK_PLL4_VCODIV 2 +#define IMX8ULP_CLK_DDR_SEL 3 +#define IMX8ULP_CLK_DDR_DIV 4 +#define IMX8ULP_CLK_LPAV_AXI_SEL 5 +#define IMX8ULP_CLK_LPAV_AXI_DIV 6 +#define IMX8ULP_CLK_LPAV_AHB_DIV 7 +#define IMX8ULP_CLK_LPAV_BUS_DIV 8 +#define IMX8ULP_CLK_PLL4_PFD0 9 +#define IMX8ULP_CLK_PLL4_PFD1 10 +#define IMX8ULP_CLK_PLL4_PFD2 11 +#define IMX8ULP_CLK_PLL4_PFD3 12 +#define IMX8ULP_CLK_PLL4_PFD0_DIV1_GATE 13 +#define IMX8ULP_CLK_PLL4_PFD0_DIV2_GATE 14 +#define IMX8ULP_CLK_PLL4_PFD1_DIV1_GATE 15 +#define IMX8ULP_CLK_PLL4_PFD1_DIV2_GATE 16 +#define IMX8ULP_CLK_PLL4_PFD2_DIV1_GATE 17 +#define IMX8ULP_CLK_PLL4_PFD2_DIV2_GATE 18 +#define IMX8ULP_CLK_PLL4_PFD3_DIV1_GATE 19 +#define IMX8ULP_CLK_PLL4_PFD3_DIV2_GATE 20 +#define IMX8ULP_CLK_PLL4_PFD0_DIV1 21 +#define IMX8ULP_CLK_PLL4_PFD0_DIV2 22 +#define IMX8ULP_CLK_PLL4_PFD1_DIV1 23 +#define IMX8ULP_CLK_PLL4_PFD1_DIV2 24 +#define IMX8ULP_CLK_PLL4_PFD2_DIV1 25 +#define IMX8ULP_CLK_PLL4_PFD2_DIV2 26 +#define IMX8ULP_CLK_PLL4_PFD3_DIV1 27 +#define IMX8ULP_CLK_PLL4_PFD3_DIV2 28 +#define IMX8ULP_CLK_CGC2_SOSC_DIV1_GATE 29 +#define IMX8ULP_CLK_CGC2_SOSC_DIV2_GATE 30 +#define IMX8ULP_CLK_CGC2_SOSC_DIV3_GATE 31 +#define IMX8ULP_CLK_CGC2_SOSC_DIV1 32 +#define IMX8ULP_CLK_CGC2_SOSC_DIV2 33 +#define IMX8ULP_CLK_CGC2_SOSC_DIV3 34 +#define IMX8ULP_CLK_CGC2_FROSC_DIV1_GATE 35 +#define IMX8ULP_CLK_CGC2_FROSC_DIV2_GATE 36 +#define IMX8ULP_CLK_CGC2_FROSC_DIV3_GATE 37 +#define IMX8ULP_CLK_CGC2_FROSC_DIV1 38 +#define IMX8ULP_CLK_CGC2_FROSC_DIV2 39 +#define IMX8ULP_CLK_CGC2_FROSC_DIV3 40 +#define IMX8ULP_CLK_AUD_CLK2 41 +#define IMX8ULP_CLK_SAI6_SEL 42 +#define IMX8ULP_CLK_SAI7_SEL 43 +#define IMX8ULP_CLK_SPDIF_SEL 44 +#define IMX8ULP_CLK_HIFI_SEL 45 +#define IMX8ULP_CLK_HIFI_DIVCORE 46 +#define IMX8ULP_CLK_HIFI_DIVPLAT 47 +#define IMX8ULP_CLK_DSI_PHY_REF 48 + +#define IMX8ULP_CLK_CGC2_END 49 + +/* PCC3 */ +#define IMX8ULP_CLK_WDOG3 0 +#define IMX8ULP_CLK_WDOG4 1 +#define IMX8ULP_CLK_LPIT1 2 +#define IMX8ULP_CLK_TPM4 3 +#define IMX8ULP_CLK_TPM5 4 +#define IMX8ULP_CLK_FLEXIO1 5 +#define IMX8ULP_CLK_I3C2 6 +#define IMX8ULP_CLK_LPI2C4 7 +#define IMX8ULP_CLK_LPI2C5 8 +#define IMX8ULP_CLK_LPUART4 9 +#define IMX8ULP_CLK_LPUART5 10 +#define IMX8ULP_CLK_LPSPI4 11 +#define IMX8ULP_CLK_LPSPI5 12 +#define IMX8ULP_CLK_DMA1_MP 13 +#define IMX8ULP_CLK_DMA1_CH0 14 +#define IMX8ULP_CLK_DMA1_CH1 15 +#define IMX8ULP_CLK_DMA1_CH2 16 +#define IMX8ULP_CLK_DMA1_CH3 17 +#define IMX8ULP_CLK_DMA1_CH4 18 +#define IMX8ULP_CLK_DMA1_CH5 19 +#define IMX8ULP_CLK_DMA1_CH6 20 +#define IMX8ULP_CLK_DMA1_CH7 21 +#define IMX8ULP_CLK_DMA1_CH8 22 +#define IMX8ULP_CLK_DMA1_CH9 23 +#define IMX8ULP_CLK_DMA1_CH10 24 +#define IMX8ULP_CLK_DMA1_CH11 25 +#define IMX8ULP_CLK_DMA1_CH12 26 +#define IMX8ULP_CLK_DMA1_CH13 27 +#define IMX8ULP_CLK_DMA1_CH14 28 +#define IMX8ULP_CLK_DMA1_CH15 29 +#define IMX8ULP_CLK_DMA1_CH16 30 +#define IMX8ULP_CLK_DMA1_CH17 31 +#define IMX8ULP_CLK_DMA1_CH18 32 +#define IMX8ULP_CLK_DMA1_CH19 33 +#define IMX8ULP_CLK_DMA1_CH20 34 +#define IMX8ULP_CLK_DMA1_CH21 35 +#define IMX8ULP_CLK_DMA1_CH22 36 +#define IMX8ULP_CLK_DMA1_CH23 37 +#define IMX8ULP_CLK_DMA1_CH24 38 +#define IMX8ULP_CLK_DMA1_CH25 39 +#define IMX8ULP_CLK_DMA1_CH26 40 +#define IMX8ULP_CLK_DMA1_CH27 41 +#define IMX8ULP_CLK_DMA1_CH28 42 +#define IMX8ULP_CLK_DMA1_CH29 43 +#define IMX8ULP_CLK_DMA1_CH30 44 +#define IMX8ULP_CLK_DMA1_CH31 45 +#define IMX8ULP_CLK_MU3_A 46 +#define IMX8ULP_CLK_MU0_B 47 + +#define IMX8ULP_CLK_PCC3_END 48 + +/* PCC4 */ +#define IMX8ULP_CLK_FLEXSPI2 0 +#define IMX8ULP_CLK_TPM6 1 +#define IMX8ULP_CLK_TPM7 2 +#define IMX8ULP_CLK_LPI2C6 3 +#define IMX8ULP_CLK_LPI2C7 4 +#define IMX8ULP_CLK_LPUART6 5 +#define IMX8ULP_CLK_LPUART7 6 +#define IMX8ULP_CLK_SAI4 7 +#define IMX8ULP_CLK_SAI5 8 +#define IMX8ULP_CLK_PCTLE 9 +#define IMX8ULP_CLK_PCTLF 10 +#define IMX8ULP_CLK_USDHC0 11 +#define IMX8ULP_CLK_USDHC1 12 +#define IMX8ULP_CLK_USDHC2 13 +#define IMX8ULP_CLK_USB0 14 +#define IMX8ULP_CLK_USB0_PHY 15 +#define IMX8ULP_CLK_USB1 16 +#define IMX8ULP_CLK_USB1_PHY 17 +#define IMX8ULP_CLK_USB_XBAR 18 +#define IMX8ULP_CLK_ENET 19 +#define IMX8ULP_CLK_SFA1 20 +#define IMX8ULP_CLK_RGPIOE 21 +#define IMX8ULP_CLK_RGPIOF 22 + +#define IMX8ULP_CLK_PCC4_END 23 + +/* PCC5 */ +#define IMX8ULP_CLK_TPM8 0 +#define IMX8ULP_CLK_SAI6 1 +#define IMX8ULP_CLK_SAI7 2 +#define IMX8ULP_CLK_SPDIF 3 +#define IMX8ULP_CLK_ISI 4 +#define IMX8ULP_CLK_CSI_REGS 5 +#define IMX8ULP_CLK_PCTLD 6 +#define IMX8ULP_CLK_CSI 7 +#define IMX8ULP_CLK_DSI 8 +#define IMX8ULP_CLK_WDOG5 9 +#define IMX8ULP_CLK_EPDC 10 +#define IMX8ULP_CLK_PXP 11 +#define IMX8ULP_CLK_SFA2 12 +#define IMX8ULP_CLK_GPU2D 13 +#define IMX8ULP_CLK_GPU3D 14 +#define IMX8ULP_CLK_DC_NANO 15 +#define IMX8ULP_CLK_CSI_CLK_UI 16 +#define IMX8ULP_CLK_CSI_CLK_ESC 17 +#define IMX8ULP_CLK_RGPIOD 18 +#define IMX8ULP_CLK_DMA2_MP 19 +#define IMX8ULP_CLK_DMA2_CH0 20 +#define IMX8ULP_CLK_DMA2_CH1 21 +#define IMX8ULP_CLK_DMA2_CH2 22 +#define IMX8ULP_CLK_DMA2_CH3 23 +#define IMX8ULP_CLK_DMA2_CH4 24 +#define IMX8ULP_CLK_DMA2_CH5 25 +#define IMX8ULP_CLK_DMA2_CH6 26 +#define IMX8ULP_CLK_DMA2_CH7 27 +#define IMX8ULP_CLK_DMA2_CH8 28 +#define IMX8ULP_CLK_DMA2_CH9 29 +#define IMX8ULP_CLK_DMA2_CH10 30 +#define IMX8ULP_CLK_DMA2_CH11 31 +#define IMX8ULP_CLK_DMA2_CH12 32 +#define IMX8ULP_CLK_DMA2_CH13 33 +#define IMX8ULP_CLK_DMA2_CH14 34 +#define IMX8ULP_CLK_DMA2_CH15 35 +#define IMX8ULP_CLK_DMA2_CH16 36 +#define IMX8ULP_CLK_DMA2_CH17 37 +#define IMX8ULP_CLK_DMA2_CH18 38 +#define IMX8ULP_CLK_DMA2_CH19 39 +#define IMX8ULP_CLK_DMA2_CH20 40 +#define IMX8ULP_CLK_DMA2_CH21 41 +#define IMX8ULP_CLK_DMA2_CH22 42 +#define IMX8ULP_CLK_DMA2_CH23 43 +#define IMX8ULP_CLK_DMA2_CH24 44 +#define IMX8ULP_CLK_DMA2_CH25 45 +#define IMX8ULP_CLK_DMA2_CH26 46 +#define IMX8ULP_CLK_DMA2_CH27 47 +#define IMX8ULP_CLK_DMA2_CH28 48 +#define IMX8ULP_CLK_DMA2_CH29 49 +#define IMX8ULP_CLK_DMA2_CH30 50 +#define IMX8ULP_CLK_DMA2_CH31 51 +#define IMX8ULP_CLK_MU2_B 52 +#define IMX8ULP_CLK_MU3_B 53 +#define IMX8ULP_CLK_AVD_SIM 54 +#define IMX8ULP_CLK_DSI_TX_ESC 55 + +#define IMX8ULP_CLK_PCC5_END 56 + +#endif diff --git a/include/dt-bindings/reset/imx8ulp-pcc-reset.h b/include/dt-bindings/reset/imx8ulp-pcc-reset.h new file mode 100644 index 000000000000..e99a4735c3c4 --- /dev/null +++ b/include/dt-bindings/reset/imx8ulp-pcc-reset.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2021 NXP + */ + +#ifndef DT_BINDING_PCC_RESET_IMX8ULP_H +#define DT_BINDING_PCC_RESET_IMX8ULP_H + +/* PCC3 */ +#define PCC3_WDOG3_SWRST 0 +#define PCC3_WDOG4_SWRST 1 +#define PCC3_LPIT1_SWRST 2 +#define PCC3_TPM4_SWRST 3 +#define PCC3_TPM5_SWRST 4 +#define PCC3_FLEXIO1_SWRST 5 +#define PCC3_I3C2_SWRST 6 +#define PCC3_LPI2C4_SWRST 7 +#define PCC3_LPI2C5_SWRST 8 +#define PCC3_LPUART4_SWRST 9 +#define PCC3_LPUART5_SWRST 10 +#define PCC3_LPSPI4_SWRST 11 +#define PCC3_LPSPI5_SWRST 12 + +/* PCC4 */ +#define PCC4_FLEXSPI2_SWRST 0 +#define PCC4_TPM6_SWRST 1 +#define PCC4_TPM7_SWRST 2 +#define PCC4_LPI2C6_SWRST 3 +#define PCC4_LPI2C7_SWRST 4 +#define PCC4_LPUART6_SWRST 5 +#define PCC4_LPUART7_SWRST 6 +#define PCC4_SAI4_SWRST 7 +#define PCC4_SAI5_SWRST 8 +#define PCC4_USDHC0_SWRST 9 +#define PCC4_USDHC1_SWRST 10 +#define PCC4_USDHC2_SWRST 11 +#define PCC4_USB0_SWRST 12 +#define PCC4_USB0_PHY_SWRST 13 +#define PCC4_USB1_SWRST 14 +#define PCC4_USB1_PHY_SWRST 15 +#define PCC4_ENET_SWRST 16 + +/* PCC5 */ +#define PCC5_TPM8_SWRST 0 +#define PCC5_SAI6_SWRST 1 +#define PCC5_SAI7_SWRST 2 +#define PCC5_SPDIF_SWRST 3 +#define PCC5_ISI_SWRST 4 +#define PCC5_CSI_REGS_SWRST 5 +#define PCC5_CSI_SWRST 6 +#define PCC5_DSI_SWRST 7 +#define PCC5_WDOG5_SWRST 8 +#define PCC5_EPDC_SWRST 9 +#define PCC5_PXP_SWRST 10 +#define PCC5_GPU2D_SWRST 11 +#define PCC5_GPU3D_SWRST 12 +#define PCC5_DC_NANO_SWRST 13 + +#endif /*DT_BINDING_RESET_IMX8ULP_H */ From 5f0601c47c336ae75aec9ed308b6c4428c7d179b Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Tue, 14 Sep 2021 14:52:01 +0800 Subject: [PATCH 10/24] clk: imx: Update the pllv4 to support imx8ulp The PLLs used on i.MX8ULP is mostly the same as on i.MX7ULP, except the PLL register offset is changed. Change the PLLv4 driver for code reuse on i.MX7ULP and i.MX8ULP. Signed-off-by: Jacky Bai Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210914065208.3582128-3-ping.bai@nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx7ulp.c | 4 ++-- drivers/clk/imx/clk-pllv4.c | 34 +++++++++++++++++++++++++--------- drivers/clk/imx/clk.h | 9 +++++++-- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c index 779e09105da7..ba50d6db8097 100644 --- a/drivers/clk/imx/clk-imx7ulp.c +++ b/drivers/clk/imx/clk-imx7ulp.c @@ -78,8 +78,8 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np) hws[IMX7ULP_CLK_SPLL_PRE_DIV] = imx_clk_hw_divider_flags("spll_pre_div", "spll_pre_sel", base + 0x608, 8, 3, CLK_SET_RATE_GATE); /* name parent_name base */ - hws[IMX7ULP_CLK_APLL] = imx_clk_hw_pllv4("apll", "apll_pre_div", base + 0x500); - hws[IMX7ULP_CLK_SPLL] = imx_clk_hw_pllv4("spll", "spll_pre_div", base + 0x600); + hws[IMX7ULP_CLK_APLL] = imx_clk_hw_pllv4(IMX_PLLV4_IMX7ULP, "apll", "apll_pre_div", base + 0x500); + hws[IMX7ULP_CLK_SPLL] = imx_clk_hw_pllv4(IMX_PLLV4_IMX7ULP, "spll", "spll_pre_div", base + 0x600); /* APLL PFDs */ hws[IMX7ULP_CLK_APLL_PFD0] = imx_clk_hw_pfdv2("apll_pfd0", "apll", base + 0x50c, 0); diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c index 8ec703f27417..3c750ccbee25 100644 --- a/drivers/clk/imx/clk-pllv4.c +++ b/drivers/clk/imx/clk-pllv4.c @@ -23,14 +23,17 @@ /* PLL Configuration Register (xPLLCFG) */ #define PLL_CFG_OFFSET 0x08 +#define IMX8ULP_PLL_CFG_OFFSET 0x10 #define BP_PLL_MULT 16 #define BM_PLL_MULT (0x7f << 16) /* PLL Numerator Register (xPLLNUM) */ #define PLL_NUM_OFFSET 0x10 +#define IMX8ULP_PLL_NUM_OFFSET 0x1c /* PLL Denominator Register (xPLLDENOM) */ #define PLL_DENOM_OFFSET 0x14 +#define IMX8ULP_PLL_DENOM_OFFSET 0x18 #define MAX_MFD 0x3fffffff #define DEFAULT_MFD 1000000 @@ -38,6 +41,9 @@ struct clk_pllv4 { struct clk_hw hw; void __iomem *base; + u32 cfg_offset; + u32 num_offset; + u32 denom_offset; }; /* Valid PLL MULT Table */ @@ -72,12 +78,12 @@ static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw, u32 mult, mfn, mfd; u64 temp64; - mult = readl_relaxed(pll->base + PLL_CFG_OFFSET); + mult = readl_relaxed(pll->base + pll->cfg_offset); mult &= BM_PLL_MULT; mult >>= BP_PLL_MULT; - mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET); - mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET); + mfn = readl_relaxed(pll->base + pll->num_offset); + mfd = readl_relaxed(pll->base + pll->denom_offset); temp64 = parent_rate; temp64 *= mfn; do_div(temp64, mfd); @@ -165,13 +171,13 @@ static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate, do_div(temp64, parent_rate); mfn = temp64; - val = readl_relaxed(pll->base + PLL_CFG_OFFSET); + val = readl_relaxed(pll->base + pll->cfg_offset); val &= ~BM_PLL_MULT; val |= mult << BP_PLL_MULT; - writel_relaxed(val, pll->base + PLL_CFG_OFFSET); + writel_relaxed(val, pll->base + pll->cfg_offset); - writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET); - writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET); + writel_relaxed(mfn, pll->base + pll->num_offset); + writel_relaxed(mfd, pll->base + pll->denom_offset); return 0; } @@ -207,8 +213,8 @@ static const struct clk_ops clk_pllv4_ops = { .is_prepared = clk_pllv4_is_prepared, }; -struct clk_hw *imx_clk_hw_pllv4(const char *name, const char *parent_name, - void __iomem *base) +struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name, + const char *parent_name, void __iomem *base) { struct clk_pllv4 *pll; struct clk_hw *hw; @@ -221,6 +227,16 @@ struct clk_hw *imx_clk_hw_pllv4(const char *name, const char *parent_name, pll->base = base; + if (type == IMX_PLLV4_IMX8ULP) { + pll->cfg_offset = IMX8ULP_PLL_CFG_OFFSET; + pll->num_offset = IMX8ULP_PLL_NUM_OFFSET; + pll->denom_offset = IMX8ULP_PLL_DENOM_OFFSET; + } else { + pll->cfg_offset = PLL_CFG_OFFSET; + pll->num_offset = PLL_NUM_OFFSET; + pll->denom_offset = PLL_DENOM_OFFSET; + } + init.name = name; init.ops = &clk_pllv4_ops; init.parent_names = &parent_name; diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index e36d01411a4e..5af70d351993 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -42,6 +42,11 @@ enum imx_pll14xx_type { PLL_1443X, }; +enum imx_pllv4_type { + IMX_PLLV4_IMX7ULP, + IMX_PLLV4_IMX8ULP, +}; + /* NOTE: Rate table should be kept sorted in descending order. */ struct imx_pll14xx_rate_table { unsigned int rate; @@ -241,8 +246,8 @@ struct clk_hw *imx_clk_hw_pllv3(enum imx_pllv3_type type, const char *name, .kdiv = (_k), \ } -struct clk_hw *imx_clk_hw_pllv4(const char *name, const char *parent_name, - void __iomem *base); +struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name, + const char *parent_name, void __iomem *base); struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, From b40ba8065347dcf70604acd4be6f3c28bdbf2b91 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Tue, 14 Sep 2021 14:52:02 +0800 Subject: [PATCH 11/24] clk: imx: Update the compsite driver to support imx8ulp On i.MX8ULP, some peripherals have a sw_rst control resides in the per device PCC clock control register, all others are same as i.MX7ULP, so update the 7ulp clock composite driver to support i.MX8ULP to maxmimize the code reuse. Signed-off-by: Peng Fan Signed-off-by: Jacky Bai Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210914065208.3582128-4-ping.bai@nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-composite-7ulp.c | 61 ++++++++++++++++++++++++++-- drivers/clk/imx/clk.h | 6 +++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c index d85ba78abbb1..50ed383320bf 100644 --- a/drivers/clk/imx/clk-composite-7ulp.c +++ b/drivers/clk/imx/clk-composite-7ulp.c @@ -23,11 +23,50 @@ #define PCG_PCD_WIDTH 3 #define PCG_PCD_MASK 0x7 -struct clk_hw *imx7ulp_clk_hw_composite(const char *name, +#define SW_RST BIT(28) + +static int pcc_gate_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + u32 val; + int ret; + + ret = clk_gate_ops.enable(hw); + if (ret) + return ret; + + /* + * release the sw reset for peripherals associated with + * with this pcc clock. + */ + val = readl(gate->reg); + val |= SW_RST; + writel(val, gate->reg); + + return 0; +} + +static void pcc_gate_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static int pcc_gate_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops pcc_gate_ops = { + .enable = pcc_gate_enable, + .disable = pcc_gate_disable, + .is_enabled = pcc_gate_is_enabled, +}; + +static struct clk_hw *imx_ulp_clk_hw_composite(const char *name, const char * const *parent_names, int num_parents, bool mux_present, bool rate_present, bool gate_present, - void __iomem *reg) + void __iomem *reg, bool has_swrst) { struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL; struct clk_fractional_divider *fd = NULL; @@ -77,7 +116,7 @@ struct clk_hw *imx7ulp_clk_hw_composite(const char *name, hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, mux_hw, &clk_mux_ops, fd_hw, &clk_fractional_divider_ops, gate_hw, - &clk_gate_ops, CLK_SET_RATE_GATE | + has_swrst ? &pcc_gate_ops : &clk_gate_ops, CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE); if (IS_ERR(hw)) { kfree(mux); @@ -87,3 +126,19 @@ struct clk_hw *imx7ulp_clk_hw_composite(const char *name, return hw; } + +struct clk_hw *imx7ulp_clk_hw_composite(const char *name, const char * const *parent_names, + int num_parents, bool mux_present, bool rate_present, + bool gate_present, void __iomem *reg) +{ + return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present, + gate_present, reg, false); +} + +struct clk_hw *imx8ulp_clk_hw_composite(const char *name, const char * const *parent_names, + int num_parents, bool mux_present, bool rate_present, + bool gate_present, void __iomem *reg, bool has_swrst) +{ + return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present, + gate_present, reg, has_swrst); +} diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 5af70d351993..a81dca00e8d1 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -287,6 +287,12 @@ struct clk_hw *imx7ulp_clk_hw_composite(const char *name, bool rate_present, bool gate_present, void __iomem *reg); +struct clk_hw *imx8ulp_clk_hw_composite(const char *name, + const char * const *parent_names, + int num_parents, bool mux_present, + bool rate_present, bool gate_present, + void __iomem *reg, bool has_swrst); + struct clk_hw *imx_clk_hw_fixup_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width, void (*fixup)(u32 *val)); From 0f6e3c15ec335e2282dcc64cd15dcf07891ce7ac Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 14 Sep 2021 14:52:03 +0800 Subject: [PATCH 12/24] clk: imx: disable i.mx7ulp composite clock during initialization i.MX7ULP peripheral clock ONLY allow parent/rate to be changed with clock gated, however, during clock tree initialization, the peripheral clock could be enabled by bootloader, but the prepare count in clock tree is still zero, so clock core driver will allow parent/rate changed even with CLK_SET_RATE_GATE/CLK_SET_PARENT_GATE set, but the change will fail due to HW NOT allow parent/rate change with clock enabled. It will cause clock HW status mismatch with clock tree info and lead to function issue. Below is an example: usdhc0's pcc clock value is 0xC5000000 during kernel boot up, it means usdhc0 clock is enabled, its parent is APLL_PFD1. In DT file, the usdhc0 clock settings are as below: assigned-clocks = <&pcc2 IMX7ULP_CLK_USDHC0>; assigned-clock-parents = <&scg1 IMX7ULP_CLK_NIC1_DIV>; when kernel boot up, the clock tree info is as below, but the usdhc0 PCC register is still 0xC5000000, which means its parent is still from APLL_PFD1, which is incorrect and cause usdhc0 NOT work. nic1_clk 2 2 0 176000000 0 0 50000 usdhc0 0 0 0 176000000 0 0 50000 After making sure the peripheral clock is disabled during clock tree initialization, the usdhc0 is working, and this change is necessary for all i.MX7ULP peripheral clocks. Signed-off-by: Anson Huang Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210914065208.3582128-5-ping.bai@nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-composite-7ulp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c index 50ed383320bf..92908ee4509d 100644 --- a/drivers/clk/imx/clk-composite-7ulp.c +++ b/drivers/clk/imx/clk-composite-7ulp.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "../clk-fractional-divider.h" @@ -73,6 +74,7 @@ static struct clk_hw *imx_ulp_clk_hw_composite(const char *name, struct clk_gate *gate = NULL; struct clk_mux *mux = NULL; struct clk_hw *hw; + u32 val; if (mux_present) { mux = kzalloc(sizeof(*mux), GFP_KERNEL); @@ -111,6 +113,18 @@ static struct clk_hw *imx_ulp_clk_hw_composite(const char *name, gate_hw = &gate->hw; gate->reg = reg; gate->bit_idx = PCG_CGC_SHIFT; + /* + * make sure clock is gated during clock tree initialization, + * the HW ONLY allow clock parent/rate changed with clock gated, + * during clock tree initialization, clocks could be enabled + * by bootloader, so the HW status will mismatch with clock tree + * prepare count, then clock core driver will allow parent/rate + * change since the prepare count is zero, but HW actually + * prevent the parent/rate change due to the clock is enabled. + */ + val = readl_relaxed(reg); + val &= ~(1 << PCG_CGC_SHIFT); + writel_relaxed(val, reg); } hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, From 75c6f1a0191a8d0c5c8e9cc5d33daa47d88783e1 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Tue, 14 Sep 2021 14:52:04 +0800 Subject: [PATCH 13/24] clk: imx: Add 'CLK_SET_RATE_NO_REPARENT' for composite-7ulp For the imx_composite-7ulp clock type, The clock parent should be changed explicitly by end user of this clock, if the the 'CLK_SET_RATE_NO_REPARENT' flag is not set, when user want to set a clock frequency that can NOT get from HW accurately, then the clock's parent will be switch to another clock parent sometimes. This is NOT what we expected and introduced some additional debug effort, so add the 'CLK_SET_RATE_NO_REPARENT' to avoid such unexpected result. Signed-off-by: Jacky Bai Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210914065208.3582128-6-ping.bai@nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-composite-7ulp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c index 92908ee4509d..9ce8c630ee32 100644 --- a/drivers/clk/imx/clk-composite-7ulp.c +++ b/drivers/clk/imx/clk-composite-7ulp.c @@ -131,7 +131,7 @@ static struct clk_hw *imx_ulp_clk_hw_composite(const char *name, mux_hw, &clk_mux_ops, fd_hw, &clk_fractional_divider_ops, gate_hw, has_swrst ? &pcc_gate_ops : &clk_gate_ops, CLK_SET_RATE_GATE | - CLK_SET_PARENT_GATE); + CLK_SET_PARENT_GATE | CLK_SET_RATE_NO_REPARENT); if (IS_ERR(hw)) { kfree(mux); kfree(fd); From ae8a10d697cd771fb6d28d55c78d1a75b4a4152e Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Tue, 14 Sep 2021 14:52:05 +0800 Subject: [PATCH 14/24] clk: imx: disable the pfd when set pfdv2 clock rate It is possible that a PFD is enabled in HW but not in SW. That means the enable count & prepare count of the PFD clock is '0', so the 'CLK_SET_RATE' flag can do nothing when the rate is changed while the PFD is hw enabled. In order to safely change the pfd rate, we can disable the PFD directly if it is hw enabled but not used by SW end user. Signed-off-by: Jacky Bai Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210914065208.3582128-7-ping.bai@nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-pfdv2.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/clk/imx/clk-pfdv2.c b/drivers/clk/imx/clk-pfdv2.c index 6b744c84278e..9cba83521988 100644 --- a/drivers/clk/imx/clk-pfdv2.c +++ b/drivers/clk/imx/clk-pfdv2.c @@ -161,8 +161,17 @@ static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate, if (!rate) return -EINVAL; - /* PFD can NOT change rate without gating */ - WARN_ON(clk_pfdv2_is_enabled(hw)); + /* + * PFD can NOT change rate without gating. + * as the PFDs may enabled in HW by default but no + * consumer used it, the enable count is '0', so the + * 'SET_RATE_GATE' can NOT help on blocking the set_rate + * ops especially for 'assigned-clock-xxx'. In order + * to simplify the case, just disable the PFD if it is + * enabled in HW but not in SW. + */ + if (clk_pfdv2_is_enabled(hw)) + clk_pfdv2_disable(hw); tmp = tmp * 18 + rate / 2; do_div(tmp, rate); From 9179d23919312634e3076c96948d01f756832c10 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Tue, 14 Sep 2021 14:52:06 +0800 Subject: [PATCH 15/24] clk: imx: Update the pfdv2 for 8ulp specific support On i.MX8ULP, the 'CLK_SET_RATE_PARENT' flag should NOT be set and according to the laest RM, the PFD divider value range seems will be changed in the future, so update the pfdv2 to include the specific support for i.MX8ULP. Signed-off-by: Jacky Bai Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210914065208.3582128-8-ping.bai@nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx7ulp.c | 16 ++++++++-------- drivers/clk/imx/clk-pfdv2.c | 9 ++++++--- drivers/clk/imx/clk.h | 9 +++++++-- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c index ba50d6db8097..b6e45e77ee39 100644 --- a/drivers/clk/imx/clk-imx7ulp.c +++ b/drivers/clk/imx/clk-imx7ulp.c @@ -82,16 +82,16 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np) hws[IMX7ULP_CLK_SPLL] = imx_clk_hw_pllv4(IMX_PLLV4_IMX7ULP, "spll", "spll_pre_div", base + 0x600); /* APLL PFDs */ - hws[IMX7ULP_CLK_APLL_PFD0] = imx_clk_hw_pfdv2("apll_pfd0", "apll", base + 0x50c, 0); - hws[IMX7ULP_CLK_APLL_PFD1] = imx_clk_hw_pfdv2("apll_pfd1", "apll", base + 0x50c, 1); - hws[IMX7ULP_CLK_APLL_PFD2] = imx_clk_hw_pfdv2("apll_pfd2", "apll", base + 0x50c, 2); - hws[IMX7ULP_CLK_APLL_PFD3] = imx_clk_hw_pfdv2("apll_pfd3", "apll", base + 0x50c, 3); + hws[IMX7ULP_CLK_APLL_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd0", "apll", base + 0x50c, 0); + hws[IMX7ULP_CLK_APLL_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd1", "apll", base + 0x50c, 1); + hws[IMX7ULP_CLK_APLL_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd2", "apll", base + 0x50c, 2); + hws[IMX7ULP_CLK_APLL_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd3", "apll", base + 0x50c, 3); /* SPLL PFDs */ - hws[IMX7ULP_CLK_SPLL_PFD0] = imx_clk_hw_pfdv2("spll_pfd0", "spll", base + 0x60C, 0); - hws[IMX7ULP_CLK_SPLL_PFD1] = imx_clk_hw_pfdv2("spll_pfd1", "spll", base + 0x60C, 1); - hws[IMX7ULP_CLK_SPLL_PFD2] = imx_clk_hw_pfdv2("spll_pfd2", "spll", base + 0x60C, 2); - hws[IMX7ULP_CLK_SPLL_PFD3] = imx_clk_hw_pfdv2("spll_pfd3", "spll", base + 0x60C, 3); + hws[IMX7ULP_CLK_SPLL_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd0", "spll", base + 0x60C, 0); + hws[IMX7ULP_CLK_SPLL_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd1", "spll", base + 0x60C, 1); + hws[IMX7ULP_CLK_SPLL_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd2", "spll", base + 0x60C, 2); + hws[IMX7ULP_CLK_SPLL_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd3", "spll", base + 0x60C, 3); /* PLL Mux */ hws[IMX7ULP_CLK_APLL_PFD_SEL] = imx_clk_hw_mux_flags("apll_pfd_sel", base + 0x508, 14, 2, apll_pfd_sels, ARRAY_SIZE(apll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE); diff --git a/drivers/clk/imx/clk-pfdv2.c b/drivers/clk/imx/clk-pfdv2.c index 9cba83521988..42505669cdfb 100644 --- a/drivers/clk/imx/clk-pfdv2.c +++ b/drivers/clk/imx/clk-pfdv2.c @@ -200,8 +200,8 @@ static const struct clk_ops clk_pfdv2_ops = { .is_enabled = clk_pfdv2_is_enabled, }; -struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name, - void __iomem *reg, u8 idx) +struct clk_hw *imx_clk_hw_pfdv2(enum imx_pfdv2_type type, const char *name, + const char *parent_name, void __iomem *reg, u8 idx) { struct clk_init_data init; struct clk_pfdv2 *pfd; @@ -223,7 +223,10 @@ struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name, init.ops = &clk_pfdv2_ops; init.parent_names = &parent_name; init.num_parents = 1; - init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; + if (type == IMX_PFDV2_IMX7ULP) + init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; + else + init.flags = CLK_SET_RATE_GATE; pfd->hw.init = &init; diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index a81dca00e8d1..819949973db1 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -47,6 +47,11 @@ enum imx_pllv4_type { IMX_PLLV4_IMX8ULP, }; +enum imx_pfdv2_type { + IMX_PFDV2_IMX7ULP, + IMX_PFDV2_IMX8ULP, +}; + /* NOTE: Rate table should be kept sorted in descending order. */ struct imx_pll14xx_rate_table { unsigned int rate; @@ -270,8 +275,8 @@ struct clk_hw *imx_clk_hw_gate_exclusive(const char *name, const char *parent, struct clk_hw *imx_clk_hw_pfd(const char *name, const char *parent_name, void __iomem *reg, u8 idx); -struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name, - void __iomem *reg, u8 idx); +struct clk_hw *imx_clk_hw_pfdv2(enum imx_pfdv2_type type, const char *name, + const char *parent_name, void __iomem *reg, u8 idx); struct clk_hw *imx_clk_hw_busy_divider(const char *name, const char *parent_name, void __iomem *reg, u8 shift, u8 width, From c43a801a57890b15e16a0502edf145d59c91baf7 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Tue, 14 Sep 2021 14:52:07 +0800 Subject: [PATCH 16/24] clk: imx: Add clock driver for imx8ulp Add clock driver for i.MX8ULP. Signed-off-by: Peng Fan Signed-off-by: Jacky Bai Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210914065208.3582128-9-ping.bai@nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/Kconfig | 6 + drivers/clk/imx/Makefile | 2 + drivers/clk/imx/clk-imx8ulp.c | 459 ++++++++++++++++++++++++++++++++++ 3 files changed, 467 insertions(+) create mode 100644 drivers/clk/imx/clk-imx8ulp.c diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index 47d9ec3abd2f..b81d6437ed95 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -98,3 +98,9 @@ config CLK_IMX8QXP select MXC_CLK_SCU help Build the driver for IMX8QXP SCU based clocks. + +config CLK_IMX8ULP + tristate "IMX8ULP CCM Clock Driver" + depends on ARCH_MXC || COMPILE_TEST + help + Build the driver for i.MX8ULP CCM Clock Driver diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index c24a2acbfa56..b5e040026dfb 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -31,6 +31,8 @@ clk-imx-scu-$(CONFIG_CLK_IMX8QXP) += clk-scu.o clk-imx8qxp.o \ clk-imx8qxp-rsrc.o clk-imx8qm-rsrc.o clk-imx-lpcg-scu-$(CONFIG_CLK_IMX8QXP) += clk-lpcg-scu.o clk-imx8qxp-lpcg.o +obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp.o + obj-$(CONFIG_CLK_IMX1) += clk-imx1.o obj-$(CONFIG_CLK_IMX25) += clk-imx25.o obj-$(CONFIG_CLK_IMX27) += clk-imx27.o diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c new file mode 100644 index 000000000000..6aad04114658 --- /dev/null +++ b/drivers/clk/imx/clk-imx8ulp.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +static const char * const pll_pre_sels[] = { "sosc", "frosc", }; +static const char * const a35_sels[] = { "frosc", "spll2", "sosc", "lvds", }; +static const char * const nic_sels[] = { "frosc", "spll3_pfd0", "sosc", "lvds", }; +static const char * const pcc3_periph_bus_sels[] = { "dummy", "lposc", "sosc_div2", + "frosc_div2", "xbar_divbus", "spll3_pfd1_div1", + "spll3_pfd0_div2", "spll3_pfd0_div1", }; +static const char * const pcc4_periph_bus_sels[] = { "dummy", "dummy", "lposc", + "sosc_div2", "frosc_div2", "xbar_divbus", + "spll3_vcodiv", "spll3_pfd0_div1", }; +static const char * const pcc4_periph_plat_sels[] = { "dummy", "sosc_div1", "frosc_div1", + "spll3_pfd3_div2", "spll3_pfd3_div1", + "spll3_pfd2_div2", "spll3_pfd2_div1", + "spll3_pfd1_div2", }; +static const char * const pcc5_periph_bus_sels[] = { "dummy", "dummy", "lposc", + "sosc_div2", "frosc_div2", "lpav_bus_clk", + "pll4_vcodiv", "pll4_pfd3_div1", }; +static const char * const pcc5_periph_plat_sels[] = { "dummy", "pll4_pfd3_div2", "pll4_pfd2_div2", + "pll4_pfd2_div1", "pll4_pfd1_div2", + "pll4_pfd1_div1", "pll4_pfd0_div2", + "pll4_pfd0_div1", }; +static const char * const hifi_sels[] = { "frosc", "pll4", "pll4_pfd0", "sosc", + "lvds", "dummy", "dummy", "dummy", }; +static const char * const ddr_sels[] = { "frosc", "pll4_pfd1", "sosc", "lvds", + "pll4", "pll4", "pll4", "pll4", }; +static const char * const lpav_sels[] = { "frosc", "pll4_pfd1", "sosc", "lvds", }; +static const char * const sai45_sels[] = { "spll3_pfd1_div1", "aud_clk1", "aud_clk2", "sosc", }; +static const char * const sai67_sels[] = { "spll1_pfd2_div", "spll3_pfd1_div1", "aud_clk0", "aud_clk1", "aud_clk2", "sosc", "dummy", "dummy", }; +static const char * const aud_clk1_sels[] = { "ext_aud_mclk2", "sai4_rx_bclk", "sai4_tx_bclk", "sai5_rx_bclk", "sai5_tx_bclk", "dummy", "dummy", "dummy", }; +static const char * const aud_clk2_sels[] = { "ext_aud_mclk3", "sai6_rx_bclk", "sai6_tx_bclk", "sai7_rx_bclk", "sai7_tx_bclk", "spdif_rx", "dummy", "dummy", }; +static const char * const enet_ts_sels[] = { "ext_rmii_clk", "ext_ts_clk", "rosc", "ext_aud_mclk", "sosc", "dummy", "dummy", "dummy"}; +static const char * const xbar_divbus[] = { "xbar_divbus" }; +static const char * const nic_per_divplat[] = { "nic_per_divplat" }; +static const char * const lpav_axi_div[] = { "lpav_axi_div" }; +static const char * const lpav_bus_div[] = { "lpav_bus_div" }; + +static int imx8ulp_clk_cgc1_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_CGC1_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_CGC1_END; + clks = clk_data->hws; + + clks[IMX8ULP_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + + /* CGC1 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_SPLL2_PRE_SEL] = imx_clk_hw_mux_flags("spll2_pre_sel", base + 0x510, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + clks[IMX8ULP_CLK_SPLL3_PRE_SEL] = imx_clk_hw_mux_flags("spll3_pre_sel", base + 0x610, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + + clks[IMX8ULP_CLK_SPLL2] = imx_clk_hw_pllv4(IMX_PLLV4_IMX8ULP, "spll2", "spll2_pre_sel", base + 0x500); + clks[IMX8ULP_CLK_SPLL3] = imx_clk_hw_pllv4(IMX_PLLV4_IMX8ULP, "spll3", "spll3_pre_sel", base + 0x600); + clks[IMX8ULP_CLK_SPLL3_VCODIV] = imx_clk_hw_divider("spll3_vcodiv", "spll3", base + 0x604, 0, 6); + + clks[IMX8ULP_CLK_SPLL3_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "spll3_pfd0", "spll3_vcodiv", base + 0x614, 0); + clks[IMX8ULP_CLK_SPLL3_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "spll3_pfd1", "spll3_vcodiv", base + 0x614, 1); + clks[IMX8ULP_CLK_SPLL3_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "spll3_pfd2", "spll3_vcodiv", base + 0x614, 2); + clks[IMX8ULP_CLK_SPLL3_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "spll3_pfd3", "spll3_vcodiv", base + 0x614, 3); + + clks[IMX8ULP_CLK_SPLL3_PFD0_DIV1_GATE] = imx_clk_hw_gate_dis("spll3_pfd0_div1_gate", "spll3_pfd0", base + 0x608, 7); + clks[IMX8ULP_CLK_SPLL3_PFD0_DIV2_GATE] = imx_clk_hw_gate_dis("spll3_pfd0_div2_gate", "spll3_pfd0", base + 0x608, 15); + clks[IMX8ULP_CLK_SPLL3_PFD1_DIV1_GATE] = imx_clk_hw_gate_dis("spll3_pfd1_div1_gate", "spll3_pfd1", base + 0x608, 23); + clks[IMX8ULP_CLK_SPLL3_PFD1_DIV2_GATE] = imx_clk_hw_gate_dis("spll3_pfd1_div2_gate", "spll3_pfd1", base + 0x608, 31); + clks[IMX8ULP_CLK_SPLL3_PFD2_DIV1_GATE] = imx_clk_hw_gate_dis("spll3_pfd2_div1_gate", "spll3_pfd2", base + 0x60c, 7); + clks[IMX8ULP_CLK_SPLL3_PFD2_DIV2_GATE] = imx_clk_hw_gate_dis("spll3_pfd2_div2_gate", "spll3_pfd2", base + 0x60c, 15); + clks[IMX8ULP_CLK_SPLL3_PFD3_DIV1_GATE] = imx_clk_hw_gate_dis("spll3_pfd3_div1_gate", "spll3_pfd3", base + 0x60c, 23); + clks[IMX8ULP_CLK_SPLL3_PFD3_DIV2_GATE] = imx_clk_hw_gate_dis("spll3_pfd3_div2_gate", "spll3_pfd3", base + 0x60c, 31); + clks[IMX8ULP_CLK_SPLL3_PFD0_DIV1] = imx_clk_hw_divider("spll3_pfd0_div1", "spll3_pfd0_div1_gate", base + 0x608, 0, 6); + clks[IMX8ULP_CLK_SPLL3_PFD0_DIV2] = imx_clk_hw_divider("spll3_pfd0_div2", "spll3_pfd0_div2_gate", base + 0x608, 8, 6); + clks[IMX8ULP_CLK_SPLL3_PFD1_DIV1] = imx_clk_hw_divider("spll3_pfd1_div1", "spll3_pfd1_div1_gate", base + 0x608, 16, 6); + clks[IMX8ULP_CLK_SPLL3_PFD1_DIV2] = imx_clk_hw_divider("spll3_pfd1_div2", "spll3_pfd1_div2_gate", base + 0x608, 24, 6); + clks[IMX8ULP_CLK_SPLL3_PFD2_DIV1] = imx_clk_hw_divider("spll3_pfd2_div1", "spll3_pfd2_div1_gate", base + 0x60c, 0, 6); + clks[IMX8ULP_CLK_SPLL3_PFD2_DIV2] = imx_clk_hw_divider("spll3_pfd2_div2", "spll3_pfd2_div2_gate", base + 0x60c, 8, 6); + clks[IMX8ULP_CLK_SPLL3_PFD3_DIV1] = imx_clk_hw_divider("spll3_pfd3_div1", "spll3_pfd3_div1_gate", base + 0x60c, 16, 6); + clks[IMX8ULP_CLK_SPLL3_PFD3_DIV2] = imx_clk_hw_divider("spll3_pfd3_div2", "spll3_pfd3_div2_gate", base + 0x60c, 24, 6); + + clks[IMX8ULP_CLK_A35_SEL] = imx_clk_hw_mux2("a35_sel", base + 0x14, 28, 2, a35_sels, ARRAY_SIZE(a35_sels)); + clks[IMX8ULP_CLK_A35_DIV] = imx_clk_hw_divider_flags("a35_div", "a35_sel", base + 0x14, 21, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + + clks[IMX8ULP_CLK_NIC_SEL] = imx_clk_hw_mux2("nic_sel", base + 0x34, 28, 2, nic_sels, ARRAY_SIZE(nic_sels)); + clks[IMX8ULP_CLK_NIC_AD_DIVPLAT] = imx_clk_hw_divider_flags("nic_ad_divplat", "nic_sel", base + 0x34, 21, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_NIC_PER_DIVPLAT] = imx_clk_hw_divider_flags("nic_per_divplat", "nic_ad_divplat", base + 0x34, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_XBAR_AD_DIVPLAT] = imx_clk_hw_divider_flags("xbar_ad_divplat", "nic_ad_divplat", base + 0x38, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_XBAR_DIVBUS] = imx_clk_hw_divider_flags("xbar_divbus", "nic_ad_divplat", base + 0x38, 7, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_XBAR_AD_SLOW] = imx_clk_hw_divider_flags("xbar_ad_slow", "nic_ad_divplat", base + 0x38, 0, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + + clks[IMX8ULP_CLK_SOSC_DIV1_GATE] = imx_clk_hw_gate_dis("sosc_div1_gate", "sosc", base + 0x108, 7); + clks[IMX8ULP_CLK_SOSC_DIV2_GATE] = imx_clk_hw_gate_dis("sosc_div2_gate", "sosc", base + 0x108, 15); + clks[IMX8ULP_CLK_SOSC_DIV3_GATE] = imx_clk_hw_gate_dis("sosc_div3_gate", "sosc", base + 0x108, 23); + clks[IMX8ULP_CLK_SOSC_DIV1] = imx_clk_hw_divider("sosc_div1", "sosc_div1_gate", base + 0x108, 0, 6); + clks[IMX8ULP_CLK_SOSC_DIV2] = imx_clk_hw_divider("sosc_div2", "sosc_div2_gate", base + 0x108, 8, 6); + clks[IMX8ULP_CLK_SOSC_DIV3] = imx_clk_hw_divider("sosc_div3", "sosc_div3_gate", base + 0x108, 16, 6); + + clks[IMX8ULP_CLK_FROSC_DIV1_GATE] = imx_clk_hw_gate_dis("frosc_div1_gate", "frosc", base + 0x208, 7); + clks[IMX8ULP_CLK_FROSC_DIV2_GATE] = imx_clk_hw_gate_dis("frosc_div2_gate", "frosc", base + 0x208, 15); + clks[IMX8ULP_CLK_FROSC_DIV3_GATE] = imx_clk_hw_gate_dis("frosc_div3_gate", "frosc", base + 0x208, 23); + clks[IMX8ULP_CLK_FROSC_DIV1] = imx_clk_hw_divider("frosc_div1", "frosc_div1_gate", base + 0x208, 0, 6); + clks[IMX8ULP_CLK_FROSC_DIV2] = imx_clk_hw_divider("frosc_div2", "frosc_div2_gate", base + 0x208, 8, 6); + clks[IMX8ULP_CLK_FROSC_DIV3] = imx_clk_hw_divider("frosc_div3", "frosc_div3_gate", base + 0x208, 16, 6); + clks[IMX8ULP_CLK_AUD_CLK1] = imx_clk_hw_mux2("aud_clk1", base + 0x900, 0, 3, aud_clk1_sels, ARRAY_SIZE(aud_clk1_sels)); + clks[IMX8ULP_CLK_SAI4_SEL] = imx_clk_hw_mux2("sai4_sel", base + 0x904, 0, 2, sai45_sels, ARRAY_SIZE(sai45_sels)); + clks[IMX8ULP_CLK_SAI5_SEL] = imx_clk_hw_mux2("sai5_sel", base + 0x904, 8, 2, sai45_sels, ARRAY_SIZE(sai45_sels)); + clks[IMX8ULP_CLK_ENET_TS_SEL] = imx_clk_hw_mux2("enet_ts", base + 0x700, 24, 3, enet_ts_sels, ARRAY_SIZE(enet_ts_sels)); + + imx_check_clk_hws(clks, clk_data->num); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} + +static int imx8ulp_clk_cgc2_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_CGC2_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_CGC2_END; + clks = clk_data->hws; + + /* CGC2 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_PLL4_PRE_SEL] = imx_clk_hw_mux_flags("pll4_pre_sel", base + 0x610, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + + clks[IMX8ULP_CLK_PLL4] = imx_clk_hw_pllv4(IMX_PLLV4_IMX8ULP, "pll4", "pll4_pre_sel", base + 0x600); + clks[IMX8ULP_CLK_PLL4_VCODIV] = imx_clk_hw_divider("pll4_vcodiv", "pll4", base + 0x604, 0, 6); + + clks[IMX8ULP_CLK_HIFI_SEL] = imx_clk_hw_mux_flags("hifi_sel", base + 0x14, 28, 3, hifi_sels, ARRAY_SIZE(hifi_sels), CLK_SET_PARENT_GATE); + clks[IMX8ULP_CLK_HIFI_DIVCORE] = imx_clk_hw_divider("hifi_core_div", "hifi_sel", base + 0x14, 21, 6); + clks[IMX8ULP_CLK_HIFI_DIVPLAT] = imx_clk_hw_divider("hifi_plat_div", "hifi_core_div", base + 0x14, 14, 6); + + clks[IMX8ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x40, 28, 3, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_PARENT_GATE); + clks[IMX8ULP_CLK_DDR_DIV] = imx_clk_hw_divider_flags("ddr_div", "ddr_sel", base + 0x40, 21, 6, CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_LPAV_AXI_SEL] = imx_clk_hw_mux("lpav_sel", base + 0x3c, 28, 2, lpav_sels, ARRAY_SIZE(lpav_sels)); + clks[IMX8ULP_CLK_LPAV_AXI_DIV] = imx_clk_hw_divider_flags("lpav_axi_div", "lpav_sel", base + 0x3c, 21, 6, CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_LPAV_AHB_DIV] = imx_clk_hw_divider_flags("lpav_ahb_div", "lpav_axi_div", base + 0x3c, 14, 6, CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_LPAV_BUS_DIV] = imx_clk_hw_divider_flags("lpav_bus_div", "lpav_axi_div", base + 0x3c, 7, 6, CLK_IS_CRITICAL); + + clks[IMX8ULP_CLK_PLL4_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "pll4_pfd0", "pll4_vcodiv", base + 0x614, 0); + clks[IMX8ULP_CLK_PLL4_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "pll4_pfd1", "pll4_vcodiv", base + 0x614, 1); + clks[IMX8ULP_CLK_PLL4_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "pll4_pfd2", "pll4_vcodiv", base + 0x614, 2); + clks[IMX8ULP_CLK_PLL4_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "pll4_pfd3", "pll4_vcodiv", base + 0x614, 3); + + clks[IMX8ULP_CLK_PLL4_PFD0_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd0_div1_gate", "pll4_pfd0", base + 0x608, 7); + clks[IMX8ULP_CLK_PLL4_PFD0_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd0_div2_gate", "pll4_pfd0", base + 0x608, 15); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd1_div1_gate", "pll4_pfd1", base + 0x608, 23); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd1_div2_gate", "pll4_pfd1", base + 0x608, 31); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd2_div1_gate", "pll4_pfd2", base + 0x60c, 7); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd2_div2_gate", "pll4_pfd2", base + 0x60c, 15); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd3_div1_gate", "pll4_pfd3", base + 0x60c, 23); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd3_div2_gate", "pll4_pfd3", base + 0x60c, 31); + clks[IMX8ULP_CLK_PLL4_PFD0_DIV1] = imx_clk_hw_divider("pll4_pfd0_div1", "pll4_pfd0_div1_gate", base + 0x608, 0, 6); + clks[IMX8ULP_CLK_PLL4_PFD0_DIV2] = imx_clk_hw_divider("pll4_pfd0_div2", "pll4_pfd0_div2_gate", base + 0x608, 8, 6); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV1] = imx_clk_hw_divider("pll4_pfd1_div1", "pll4_pfd1_div1_gate", base + 0x608, 16, 6); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV2] = imx_clk_hw_divider("pll4_pfd1_div2", "pll4_pfd1_div2_gate", base + 0x608, 24, 6); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV1] = imx_clk_hw_divider("pll4_pfd2_div1", "pll4_pfd2_div1_gate", base + 0x60c, 0, 6); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV2] = imx_clk_hw_divider("pll4_pfd2_div2", "pll4_pfd2_div2_gate", base + 0x60c, 8, 6); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV1] = imx_clk_hw_divider("pll4_pfd3_div1", "pll4_pfd3_div1_gate", base + 0x60c, 16, 6); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV2] = imx_clk_hw_divider("pll4_pfd3_div2", "pll4_pfd3_div2_gate", base + 0x60c, 24, 6); + + clks[IMX8ULP_CLK_CGC2_SOSC_DIV1_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div1_gate", "sosc", base + 0x108, 7); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV2_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div2_gate", "sosc", base + 0x108, 15); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV3_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div3_gate", "sosc", base + 0x108, 23); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV1] = imx_clk_hw_divider("cgc2_sosc_div1", "cgc2_sosc_div1_gate", base + 0x108, 0, 6); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV2] = imx_clk_hw_divider("cgc2_sosc_div2", "cgc2_sosc_div2_gate", base + 0x108, 8, 6); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV3] = imx_clk_hw_divider("cgc2_sosc_div3", "cgc2_sosc_div3_gate", base + 0x108, 16, 6); + + clks[IMX8ULP_CLK_CGC2_FROSC_DIV1_GATE] = imx_clk_hw_gate_dis("cgc2_frosc_div1_gate", "frosc", base + 0x208, 7); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV2_GATE] = imx_clk_hw_gate_dis("cgc2_frosc_div2_gate", "frosc", base + 0x208, 15); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV3_GATE] = imx_clk_hw_gate_dis("cgc2_frosc_div3_gate", "frosc", base + 0x208, 23); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV1] = imx_clk_hw_divider("cgc2_frosc_div1", "cgc2_frosc_div1_gate", base + 0x208, 0, 6); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV2] = imx_clk_hw_divider("cgc2_frosc_div2", "cgc2_frosc_div2_gate", base + 0x208, 8, 6); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV3] = imx_clk_hw_divider("cgc2_frosc_div3", "cgc2_frosc_div3_gate", base + 0x208, 16, 6); + clks[IMX8ULP_CLK_AUD_CLK2] = imx_clk_hw_mux2("aud_clk2", base + 0x900, 0, 3, aud_clk2_sels, ARRAY_SIZE(aud_clk2_sels)); + clks[IMX8ULP_CLK_SAI6_SEL] = imx_clk_hw_mux2("sai6_sel", base + 0x904, 0, 3, sai67_sels, ARRAY_SIZE(sai67_sels)); + clks[IMX8ULP_CLK_SAI7_SEL] = imx_clk_hw_mux2("sai7_sel", base + 0x904, 8, 3, sai67_sels, ARRAY_SIZE(sai67_sels)); + clks[IMX8ULP_CLK_SPDIF_SEL] = imx_clk_hw_mux2("spdif_sel", base + 0x910, 0, 3, sai67_sels, ARRAY_SIZE(sai67_sels)); + clks[IMX8ULP_CLK_DSI_PHY_REF] = imx_clk_hw_fixed("dsi_phy_ref", 24000000); + + imx_check_clk_hws(clks, clk_data->num); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} + +static int imx8ulp_clk_pcc3_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + int ret; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC3_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_PCC3_END; + clks = clk_data->hws; + + /* PCC3 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_WDOG3] = imx8ulp_clk_hw_composite("wdog3", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xa8, 1); + clks[IMX8ULP_CLK_WDOG4] = imx8ulp_clk_hw_composite("wdog4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xac, 1); + clks[IMX8ULP_CLK_LPIT1] = imx8ulp_clk_hw_composite("lpit1", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xc8, 1); + clks[IMX8ULP_CLK_TPM4] = imx8ulp_clk_hw_composite("tpm4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xcc, 1); + clks[IMX8ULP_CLK_TPM5] = imx8ulp_clk_hw_composite("tpm5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd0, 1); + clks[IMX8ULP_CLK_FLEXIO1] = imx8ulp_clk_hw_composite("flexio1", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd4, 1); + clks[IMX8ULP_CLK_I3C2] = imx8ulp_clk_hw_composite("i3c2", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd8, 1); + clks[IMX8ULP_CLK_LPI2C4] = imx8ulp_clk_hw_composite("lpi2c4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xdc, 1); + clks[IMX8ULP_CLK_LPI2C5] = imx8ulp_clk_hw_composite("lpi2c5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xe0, 1); + clks[IMX8ULP_CLK_LPUART4] = imx8ulp_clk_hw_composite("lpuart4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xe4, 1); + clks[IMX8ULP_CLK_LPUART5] = imx8ulp_clk_hw_composite("lpuart5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xe8, 1); + clks[IMX8ULP_CLK_LPSPI4] = imx8ulp_clk_hw_composite("lpspi4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xec, 1); + clks[IMX8ULP_CLK_LPSPI5] = imx8ulp_clk_hw_composite("lpspi5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xf0, 1); + + clks[IMX8ULP_CLK_DMA1_MP] = imx_clk_hw_gate("pcc_dma1_mp", "xbar_ad_divplat", base + 0x4, 30); + clks[IMX8ULP_CLK_DMA1_CH0] = imx_clk_hw_gate("pcc_dma1_ch0", "xbar_ad_divplat", base + 0x8, 30); + clks[IMX8ULP_CLK_DMA1_CH1] = imx_clk_hw_gate("pcc_dma1_ch1", "xbar_ad_divplat", base + 0xc, 30); + clks[IMX8ULP_CLK_DMA1_CH2] = imx_clk_hw_gate("pcc_dma1_ch2", "xbar_ad_divplat", base + 0x10, 30); + clks[IMX8ULP_CLK_DMA1_CH3] = imx_clk_hw_gate("pcc_dma1_ch3", "xbar_ad_divplat", base + 0x14, 30); + clks[IMX8ULP_CLK_DMA1_CH4] = imx_clk_hw_gate("pcc_dma1_ch4", "xbar_ad_divplat", base + 0x18, 30); + clks[IMX8ULP_CLK_DMA1_CH5] = imx_clk_hw_gate("pcc_dma1_ch5", "xbar_ad_divplat", base + 0x1c, 30); + clks[IMX8ULP_CLK_DMA1_CH6] = imx_clk_hw_gate("pcc_dma1_ch6", "xbar_ad_divplat", base + 0x20, 30); + clks[IMX8ULP_CLK_DMA1_CH7] = imx_clk_hw_gate("pcc_dma1_ch7", "xbar_ad_divplat", base + 0x24, 30); + clks[IMX8ULP_CLK_DMA1_CH8] = imx_clk_hw_gate("pcc_dma1_ch8", "xbar_ad_divplat", base + 0x28, 30); + clks[IMX8ULP_CLK_DMA1_CH9] = imx_clk_hw_gate("pcc_dma1_ch9", "xbar_ad_divplat", base + 0x2c, 30); + clks[IMX8ULP_CLK_DMA1_CH10] = imx_clk_hw_gate("pcc_dma1_ch10", "xbar_ad_divplat", base + 0x30, 30); + clks[IMX8ULP_CLK_DMA1_CH11] = imx_clk_hw_gate("pcc_dma1_ch11", "xbar_ad_divplat", base + 0x34, 30); + clks[IMX8ULP_CLK_DMA1_CH12] = imx_clk_hw_gate("pcc_dma1_ch12", "xbar_ad_divplat", base + 0x38, 30); + clks[IMX8ULP_CLK_DMA1_CH13] = imx_clk_hw_gate("pcc_dma1_ch13", "xbar_ad_divplat", base + 0x3c, 30); + clks[IMX8ULP_CLK_DMA1_CH14] = imx_clk_hw_gate("pcc_dma1_ch14", "xbar_ad_divplat", base + 0x40, 30); + clks[IMX8ULP_CLK_DMA1_CH15] = imx_clk_hw_gate("pcc_dma1_ch15", "xbar_ad_divplat", base + 0x44, 30); + clks[IMX8ULP_CLK_DMA1_CH16] = imx_clk_hw_gate("pcc_dma1_ch16", "xbar_ad_divplat", base + 0x48, 30); + clks[IMX8ULP_CLK_DMA1_CH17] = imx_clk_hw_gate("pcc_dma1_ch17", "xbar_ad_divplat", base + 0x4c, 30); + clks[IMX8ULP_CLK_DMA1_CH18] = imx_clk_hw_gate("pcc_dma1_ch18", "xbar_ad_divplat", base + 0x50, 30); + clks[IMX8ULP_CLK_DMA1_CH19] = imx_clk_hw_gate("pcc_dma1_ch19", "xbar_ad_divplat", base + 0x54, 30); + clks[IMX8ULP_CLK_DMA1_CH20] = imx_clk_hw_gate("pcc_dma1_ch20", "xbar_ad_divplat", base + 0x58, 30); + clks[IMX8ULP_CLK_DMA1_CH21] = imx_clk_hw_gate("pcc_dma1_ch21", "xbar_ad_divplat", base + 0x5c, 30); + clks[IMX8ULP_CLK_DMA1_CH22] = imx_clk_hw_gate("pcc_dma1_ch22", "xbar_ad_divplat", base + 0x60, 30); + clks[IMX8ULP_CLK_DMA1_CH23] = imx_clk_hw_gate("pcc_dma1_ch23", "xbar_ad_divplat", base + 0x64, 30); + clks[IMX8ULP_CLK_DMA1_CH24] = imx_clk_hw_gate("pcc_dma1_ch24", "xbar_ad_divplat", base + 0x68, 30); + clks[IMX8ULP_CLK_DMA1_CH25] = imx_clk_hw_gate("pcc_dma1_ch25", "xbar_ad_divplat", base + 0x6c, 30); + clks[IMX8ULP_CLK_DMA1_CH26] = imx_clk_hw_gate("pcc_dma1_ch26", "xbar_ad_divplat", base + 0x70, 30); + clks[IMX8ULP_CLK_DMA1_CH27] = imx_clk_hw_gate("pcc_dma1_ch27", "xbar_ad_divplat", base + 0x74, 30); + clks[IMX8ULP_CLK_DMA1_CH28] = imx_clk_hw_gate("pcc_dma1_ch28", "xbar_ad_divplat", base + 0x78, 30); + clks[IMX8ULP_CLK_DMA1_CH29] = imx_clk_hw_gate("pcc_dma1_ch29", "xbar_ad_divplat", base + 0x7c, 30); + clks[IMX8ULP_CLK_DMA1_CH30] = imx_clk_hw_gate("pcc_dma1_ch30", "xbar_ad_divplat", base + 0x80, 30); + clks[IMX8ULP_CLK_DMA1_CH31] = imx_clk_hw_gate("pcc_dma1_ch31", "xbar_ad_divplat", base + 0x84, 30); + clks[IMX8ULP_CLK_MU0_B] = imx_clk_hw_gate("mu0_b", "xbar_ad_divplat", base + 0x88, 30); + clks[IMX8ULP_CLK_MU3_A] = imx_clk_hw_gate("mu3_a", "xbar_ad_divplat", base + 0x8c, 30); + + imx_check_clk_hws(clks, clk_data->num); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); + + imx_register_uart_clocks(1); + + return ret; +} + +static int imx8ulp_clk_pcc4_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC4_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_PCC4_END; + clks = clk_data->hws; + + /* PCC4 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_FLEXSPI2] = imx8ulp_clk_hw_composite("flexspi2", pcc4_periph_plat_sels, ARRAY_SIZE(pcc4_periph_plat_sels), true, true, true, base + 0x4, 1); + clks[IMX8ULP_CLK_TPM6] = imx8ulp_clk_hw_composite("tpm6", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x8, 1); + clks[IMX8ULP_CLK_TPM7] = imx8ulp_clk_hw_composite("tpm7", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0xc, 1); + clks[IMX8ULP_CLK_LPI2C6] = imx8ulp_clk_hw_composite("lpi2c6", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x10, 1); + clks[IMX8ULP_CLK_LPI2C7] = imx8ulp_clk_hw_composite("lpi2c7", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x14, 1); + clks[IMX8ULP_CLK_LPUART6] = imx8ulp_clk_hw_composite("lpuart6", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x18, 1); + clks[IMX8ULP_CLK_LPUART7] = imx8ulp_clk_hw_composite("lpuart7", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x1c, 1); + clks[IMX8ULP_CLK_SAI4] = imx8ulp_clk_hw_composite("sai4", xbar_divbus, 1, false, false, true, base + 0x20, 1); /* sai ipg, NOT from sai sel */ + clks[IMX8ULP_CLK_SAI5] = imx8ulp_clk_hw_composite("sai5", xbar_divbus, 1, false, false, true, base + 0x24, 1); /* sai ipg */ + clks[IMX8ULP_CLK_PCTLE] = imx_clk_hw_gate("pctle", "xbar_divbus", base + 0x28, 30); + clks[IMX8ULP_CLK_PCTLF] = imx_clk_hw_gate("pctlf", "xbar_divbus", base + 0x2c, 30); + clks[IMX8ULP_CLK_USDHC0] = imx8ulp_clk_hw_composite("usdhc0", pcc4_periph_plat_sels, ARRAY_SIZE(pcc4_periph_plat_sels), true, false, true, base + 0x34, 1); + clks[IMX8ULP_CLK_USDHC1] = imx8ulp_clk_hw_composite("usdhc1", pcc4_periph_plat_sels, ARRAY_SIZE(pcc4_periph_plat_sels), true, false, true, base + 0x38, 1); + clks[IMX8ULP_CLK_USDHC2] = imx8ulp_clk_hw_composite("usdhc2", pcc4_periph_plat_sels, ARRAY_SIZE(pcc4_periph_plat_sels), true, false, true, base + 0x3c, 1); + clks[IMX8ULP_CLK_USB0] = imx8ulp_clk_hw_composite("usb0", nic_per_divplat, 1, false, false, true, base + 0x40, 1); + clks[IMX8ULP_CLK_USB0_PHY] = imx8ulp_clk_hw_composite("usb0_phy", xbar_divbus, 1, false, false, true, base + 0x44, 1); + clks[IMX8ULP_CLK_USB1] = imx8ulp_clk_hw_composite("usb1", nic_per_divplat, 1, false, false, true, base + 0x48, 1); + clks[IMX8ULP_CLK_USB1_PHY] = imx8ulp_clk_hw_composite("usb1_phy", xbar_divbus, 1, false, false, true, base + 0x4c, 1); + clks[IMX8ULP_CLK_USB_XBAR] = imx_clk_hw_gate("usb_xbar", "xbar_divbus", base + 0x50, 30); + clks[IMX8ULP_CLK_ENET] = imx8ulp_clk_hw_composite("enet", nic_per_divplat, 1, false, false, true, base + 0x54, 1); + clks[IMX8ULP_CLK_RGPIOE] = imx_clk_hw_gate("rgpioe", "nic_per_divplat", base + 0x78, 30); + clks[IMX8ULP_CLK_RGPIOF] = imx_clk_hw_gate("rgpiof", "nic_per_divplat", base + 0x7c, 30); + + imx_check_clk_hws(clks, clk_data->num); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} + +static int imx8ulp_clk_pcc5_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC5_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_PCC5_END; + clks = clk_data->hws; + + /* PCC5 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_DMA2_MP] = imx_clk_hw_gate("pcc_dma2_mp", "lpav_axi_div", base + 0x0, 30); + clks[IMX8ULP_CLK_DMA2_CH0] = imx_clk_hw_gate("pcc_dma2_ch0", "lpav_axi_div", base + 0x4, 30); + clks[IMX8ULP_CLK_DMA2_CH1] = imx_clk_hw_gate("pcc_dma2_ch1", "lpav_axi_div", base + 0x8, 30); + clks[IMX8ULP_CLK_DMA2_CH2] = imx_clk_hw_gate("pcc_dma2_ch2", "lpav_axi_div", base + 0xc, 30); + clks[IMX8ULP_CLK_DMA2_CH3] = imx_clk_hw_gate("pcc_dma2_ch3", "lpav_axi_div", base + 0x10, 30); + clks[IMX8ULP_CLK_DMA2_CH4] = imx_clk_hw_gate("pcc_dma2_ch4", "lpav_axi_div", base + 0x14, 30); + clks[IMX8ULP_CLK_DMA2_CH5] = imx_clk_hw_gate("pcc_dma2_ch5", "lpav_axi_div", base + 0x18, 30); + clks[IMX8ULP_CLK_DMA2_CH6] = imx_clk_hw_gate("pcc_dma2_ch6", "lpav_axi_div", base + 0x1c, 30); + clks[IMX8ULP_CLK_DMA2_CH7] = imx_clk_hw_gate("pcc_dma2_ch7", "lpav_axi_div", base + 0x20, 30); + clks[IMX8ULP_CLK_DMA2_CH8] = imx_clk_hw_gate("pcc_dma2_ch8", "lpav_axi_div", base + 0x24, 30); + clks[IMX8ULP_CLK_DMA2_CH9] = imx_clk_hw_gate("pcc_dma2_ch9", "lpav_axi_div", base + 0x28, 30); + clks[IMX8ULP_CLK_DMA2_CH10] = imx_clk_hw_gate("pcc_dma2_ch10", "lpav_axi_div", base + 0x2c, 30); + clks[IMX8ULP_CLK_DMA2_CH11] = imx_clk_hw_gate("pcc_dma2_ch11", "lpav_axi_div", base + 0x30, 30); + clks[IMX8ULP_CLK_DMA2_CH12] = imx_clk_hw_gate("pcc_dma2_ch12", "lpav_axi_div", base + 0x34, 30); + clks[IMX8ULP_CLK_DMA2_CH13] = imx_clk_hw_gate("pcc_dma2_ch13", "lpav_axi_div", base + 0x38, 30); + clks[IMX8ULP_CLK_DMA2_CH14] = imx_clk_hw_gate("pcc_dma2_ch14", "lpav_axi_div", base + 0x3c, 30); + clks[IMX8ULP_CLK_DMA2_CH15] = imx_clk_hw_gate("pcc_dma2_ch15", "lpav_axi_div", base + 0x40, 30); + clks[IMX8ULP_CLK_DMA2_CH16] = imx_clk_hw_gate("pcc_dma2_ch16", "lpav_axi_div", base + 0x44, 30); + clks[IMX8ULP_CLK_DMA2_CH17] = imx_clk_hw_gate("pcc_dma2_ch17", "lpav_axi_div", base + 0x48, 30); + clks[IMX8ULP_CLK_DMA2_CH18] = imx_clk_hw_gate("pcc_dma2_ch18", "lpav_axi_div", base + 0x4c, 30); + clks[IMX8ULP_CLK_DMA2_CH19] = imx_clk_hw_gate("pcc_dma2_ch19", "lpav_axi_div", base + 0x50, 30); + clks[IMX8ULP_CLK_DMA2_CH20] = imx_clk_hw_gate("pcc_dma2_ch20", "lpav_axi_div", base + 0x54, 30); + clks[IMX8ULP_CLK_DMA2_CH21] = imx_clk_hw_gate("pcc_dma2_ch21", "lpav_axi_div", base + 0x58, 30); + clks[IMX8ULP_CLK_DMA2_CH22] = imx_clk_hw_gate("pcc_dma2_ch22", "lpav_axi_div", base + 0x5c, 30); + clks[IMX8ULP_CLK_DMA2_CH23] = imx_clk_hw_gate("pcc_dma2_ch23", "lpav_axi_div", base + 0x60, 30); + clks[IMX8ULP_CLK_DMA2_CH24] = imx_clk_hw_gate("pcc_dma2_ch24", "lpav_axi_div", base + 0x64, 30); + clks[IMX8ULP_CLK_DMA2_CH25] = imx_clk_hw_gate("pcc_dma2_ch25", "lpav_axi_div", base + 0x68, 30); + clks[IMX8ULP_CLK_DMA2_CH26] = imx_clk_hw_gate("pcc_dma2_ch26", "lpav_axi_div", base + 0x6c, 30); + clks[IMX8ULP_CLK_DMA2_CH27] = imx_clk_hw_gate("pcc_dma2_ch27", "lpav_axi_div", base + 0x70, 30); + clks[IMX8ULP_CLK_DMA2_CH28] = imx_clk_hw_gate("pcc_dma2_ch28", "lpav_axi_div", base + 0x74, 30); + clks[IMX8ULP_CLK_DMA2_CH29] = imx_clk_hw_gate("pcc_dma2_ch29", "lpav_axi_div", base + 0x78, 30); + clks[IMX8ULP_CLK_DMA2_CH30] = imx_clk_hw_gate("pcc_dma2_ch30", "lpav_axi_div", base + 0x7c, 30); + clks[IMX8ULP_CLK_DMA2_CH31] = imx_clk_hw_gate("pcc_dma2_ch31", "lpav_axi_div", base + 0x80, 30); + + clks[IMX8ULP_CLK_AVD_SIM] = imx_clk_hw_gate("avd_sim", "lpav_bus_div", base + 0x94, 30); + clks[IMX8ULP_CLK_TPM8] = imx8ulp_clk_hw_composite("tpm8", pcc5_periph_bus_sels, ARRAY_SIZE(pcc5_periph_bus_sels), true, true, true, base + 0xa0, 1); + clks[IMX8ULP_CLK_MU2_B] = imx_clk_hw_gate("mu2_b", "lpav_bus_div", base + 0x84, 30); + clks[IMX8ULP_CLK_MU3_B] = imx_clk_hw_gate("mu3_b", "lpav_bus_div", base + 0x88, 30); + clks[IMX8ULP_CLK_SAI6] = imx8ulp_clk_hw_composite("sai6", lpav_bus_div, 1, false, false, true, base + 0xa4, 1); + clks[IMX8ULP_CLK_SAI7] = imx8ulp_clk_hw_composite("sai7", lpav_bus_div, 1, false, false, true, base + 0xa8, 1); + clks[IMX8ULP_CLK_SPDIF] = imx8ulp_clk_hw_composite("spdif", lpav_bus_div, 1, false, false, true, base + 0xac, 1); + clks[IMX8ULP_CLK_ISI] = imx8ulp_clk_hw_composite("isi", lpav_axi_div, 1, false, false, true, base + 0xb0, 1); + clks[IMX8ULP_CLK_CSI_REGS] = imx8ulp_clk_hw_composite("csi_regs", lpav_bus_div, 1, false, false, true, base + 0xb4, 1); + clks[IMX8ULP_CLK_CSI] = imx8ulp_clk_hw_composite("csi", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xbc, 1); + clks[IMX8ULP_CLK_DSI] = imx8ulp_clk_hw_composite("dsi", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xc0, 1); + clks[IMX8ULP_CLK_WDOG5] = imx8ulp_clk_hw_composite("wdog5", pcc5_periph_bus_sels, ARRAY_SIZE(pcc5_periph_bus_sels), true, true, true, base + 0xc8, 1); + clks[IMX8ULP_CLK_EPDC] = imx8ulp_clk_hw_composite("epdc", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xcc, 1); + clks[IMX8ULP_CLK_PXP] = imx8ulp_clk_hw_composite("pxp", lpav_axi_div, 1, false, false, true, base + 0xd0, 1); + clks[IMX8ULP_CLK_GPU2D] = imx8ulp_clk_hw_composite("gpu2d", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xf0, 1); + clks[IMX8ULP_CLK_GPU3D] = imx8ulp_clk_hw_composite("gpu3d", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xf4, 1); + clks[IMX8ULP_CLK_DC_NANO] = imx8ulp_clk_hw_composite("dc_nano", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xf8, 1); + clks[IMX8ULP_CLK_CSI_CLK_UI] = imx8ulp_clk_hw_composite("csi_clk_ui", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0x10c, 1); + clks[IMX8ULP_CLK_CSI_CLK_ESC] = imx8ulp_clk_hw_composite("csi_clk_esc", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0x110, 1); + clks[IMX8ULP_CLK_RGPIOD] = imx_clk_hw_gate("rgpiod", "lpav_axi_div", base + 0x114, 30); + clks[IMX8ULP_CLK_DSI_TX_ESC] = imx_clk_hw_fixed_factor("mipi_dsi_tx_esc", "dsi", 1, 4); + + imx_check_clk_hws(clks, clk_data->num); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} + +static int imx8ulp_clk_probe(struct platform_device *pdev) +{ + int (*probe)(struct platform_device *pdev); + + probe = of_device_get_match_data(&pdev->dev); + + if (probe) + return probe(pdev); + + return 0; +} + +static const struct of_device_id imx8ulp_clk_dt_ids[] = { + { .compatible = "fsl,imx8ulp-pcc3", .data = imx8ulp_clk_pcc3_init }, + { .compatible = "fsl,imx8ulp-pcc4", .data = imx8ulp_clk_pcc4_init }, + { .compatible = "fsl,imx8ulp-pcc5", .data = imx8ulp_clk_pcc5_init }, + { .compatible = "fsl,imx8ulp-cgc2", .data = imx8ulp_clk_cgc2_init }, + { .compatible = "fsl,imx8ulp-cgc1", .data = imx8ulp_clk_cgc1_init }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx8ulp_clk_dt_ids); + +static struct platform_driver imx8ulp_clk_driver = { + .probe = imx8ulp_clk_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = imx8ulp_clk_dt_ids, + }, +}; +module_platform_driver(imx8ulp_clk_driver); + +MODULE_AUTHOR("Peng Fan "); +MODULE_DESCRIPTION("NXP i.MX8ULP clock driver"); +MODULE_LICENSE("GPL v2"); From 3fa36200a43f508ee49895e74d86b511fcd8ff3f Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Tue, 14 Sep 2021 14:52:08 +0800 Subject: [PATCH 17/24] clk: imx: Add the pcc reset controller support on imx8ulp On i.MX8ULP, for some of the PCCs, it has a peripheral SW RST bit resides in the same registers as the clock controller. So add this SW RST controller support alongs with the pcc clock initialization. the reset and clock shared the same register, to avoid accessing the same register by reset control and clock control concurrently, locking is necessary, so reuse the imx_ccm_lock spinlock to simplify the code. Suggested-by: Liu Ying Signed-off-by: Jacky Bai Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210914065208.3582128-10-ping.bai@nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-composite-7ulp.c | 10 +++ drivers/clk/imx/clk-imx8ulp.c | 116 ++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c index 9ce8c630ee32..89106de16a3f 100644 --- a/drivers/clk/imx/clk-composite-7ulp.c +++ b/drivers/clk/imx/clk-composite-7ulp.c @@ -29,6 +29,7 @@ static int pcc_gate_enable(struct clk_hw *hw) { struct clk_gate *gate = to_clk_gate(hw); + unsigned long flags; u32 val; int ret; @@ -36,6 +37,7 @@ static int pcc_gate_enable(struct clk_hw *hw) if (ret) return ret; + spin_lock_irqsave(gate->lock, flags); /* * release the sw reset for peripherals associated with * with this pcc clock. @@ -44,6 +46,8 @@ static int pcc_gate_enable(struct clk_hw *hw) val |= SW_RST; writel(val, gate->reg); + spin_unlock_irqrestore(gate->lock, flags); + return 0; } @@ -84,6 +88,8 @@ static struct clk_hw *imx_ulp_clk_hw_composite(const char *name, mux->reg = reg; mux->shift = PCG_PCS_SHIFT; mux->mask = PCG_PCS_MASK; + if (has_swrst) + mux->lock = &imx_ccm_lock; } if (rate_present) { @@ -101,6 +107,8 @@ static struct clk_hw *imx_ulp_clk_hw_composite(const char *name, fd->nwidth = PCG_PCD_WIDTH; fd->nmask = PCG_PCD_MASK; fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED; + if (has_swrst) + fd->lock = &imx_ccm_lock; } if (gate_present) { @@ -113,6 +121,8 @@ static struct clk_hw *imx_ulp_clk_hw_composite(const char *name, gate_hw = &gate->hw; gate->reg = reg; gate->bit_idx = PCG_CGC_SHIFT; + if (has_swrst) + gate->lock = &imx_ccm_lock; /* * make sure clock is gated during clock tree initialization, * the HW ONLY allow clock parent/rate changed with clock gated, diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c index 6aad04114658..6699437e17b8 100644 --- a/drivers/clk/imx/clk-imx8ulp.c +++ b/drivers/clk/imx/clk-imx8ulp.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "clk.h" @@ -48,6 +49,99 @@ static const char * const nic_per_divplat[] = { "nic_per_divplat" }; static const char * const lpav_axi_div[] = { "lpav_axi_div" }; static const char * const lpav_bus_div[] = { "lpav_bus_div" }; +struct pcc_reset_dev { + void __iomem *base; + struct reset_controller_dev rcdev; + const u32 *resets; + /* Set to imx_ccm_lock to protect register access shared with clock control */ + spinlock_t *lock; +}; + +#define PCC_SW_RST BIT(28) +#define to_pcc_reset_dev(_rcdev) container_of(_rcdev, struct pcc_reset_dev, rcdev) + +static const u32 pcc3_resets[] = { + 0xa8, 0xac, 0xc8, 0xcc, 0xd0, + 0xd4, 0xd8, 0xdc, 0xe0, 0xe4, + 0xe8, 0xec, 0xf0 +}; + +static const u32 pcc4_resets[] = { + 0x4, 0x8, 0xc, 0x10, 0x14, + 0x18, 0x1c, 0x20, 0x24, 0x34, + 0x38, 0x3c, 0x40, 0x44, 0x48, + 0x4c, 0x54 +}; + +static const u32 pcc5_resets[] = { + 0xa0, 0xa4, 0xa8, 0xac, 0xb0, + 0xb4, 0xbc, 0xc0, 0xc8, 0xcc, + 0xd0, 0xf0, 0xf4, 0xf8 +}; + +static int imx8ulp_pcc_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev); + u32 offset = pcc_reset->resets[id]; + unsigned long flags; + u32 val; + + spin_lock_irqsave(pcc_reset->lock, flags); + + val = readl(pcc_reset->base + offset); + val &= ~PCC_SW_RST; + writel(val, pcc_reset->base + offset); + + spin_unlock_irqrestore(pcc_reset->lock, flags); + + return 0; +} + +static int imx8ulp_pcc_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev); + u32 offset = pcc_reset->resets[id]; + unsigned long flags; + u32 val; + + spin_lock_irqsave(pcc_reset->lock, flags); + + val = readl(pcc_reset->base + offset); + val |= PCC_SW_RST; + writel(val, pcc_reset->base + offset); + + spin_unlock_irqrestore(pcc_reset->lock, flags); + + return 0; +} + +static const struct reset_control_ops imx8ulp_pcc_reset_ops = { + .assert = imx8ulp_pcc_assert, + .deassert = imx8ulp_pcc_deassert, +}; + +static int imx8ulp_pcc_reset_init(struct platform_device *pdev, void __iomem *base, + const u32 *resets, unsigned int nr_resets) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct pcc_reset_dev *pcc_reset; + + pcc_reset = devm_kzalloc(dev, sizeof(*pcc_reset), GFP_KERNEL); + if (!pcc_reset) + return -ENOMEM; + + pcc_reset->base = base; + pcc_reset->lock = &imx_ccm_lock; + pcc_reset->resets = resets; + pcc_reset->rcdev.owner = THIS_MODULE; + pcc_reset->rcdev.nr_resets = nr_resets; + pcc_reset->rcdev.ops = &imx8ulp_pcc_reset_ops; + pcc_reset->rcdev.of_node = np; + + return devm_reset_controller_register(dev, &pcc_reset->rcdev); +} + static int imx8ulp_clk_cgc1_init(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -288,10 +382,13 @@ static int imx8ulp_clk_pcc3_init(struct platform_device *pdev) imx_check_clk_hws(clks, clk_data->num); ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); + if (ret) + return ret; imx_register_uart_clocks(1); - return ret; + /* register the pcc3 reset controller */ + return imx8ulp_pcc_reset_init(pdev, base, pcc3_resets, ARRAY_SIZE(pcc3_resets)); } static int imx8ulp_clk_pcc4_init(struct platform_device *pdev) @@ -300,6 +397,7 @@ static int imx8ulp_clk_pcc4_init(struct platform_device *pdev) struct clk_hw_onecell_data *clk_data; struct clk_hw **clks; void __iomem *base; + int ret; clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC4_END), GFP_KERNEL); @@ -339,7 +437,13 @@ static int imx8ulp_clk_pcc4_init(struct platform_device *pdev) imx_check_clk_hws(clks, clk_data->num); - return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); + if (ret) + return ret; + + /* register the pcc4 reset controller */ + return imx8ulp_pcc_reset_init(pdev, base, pcc4_resets, ARRAY_SIZE(pcc4_resets)); + } static int imx8ulp_clk_pcc5_init(struct platform_device *pdev) @@ -348,6 +452,7 @@ static int imx8ulp_clk_pcc5_init(struct platform_device *pdev) struct clk_hw_onecell_data *clk_data; struct clk_hw **clks; void __iomem *base; + int ret; clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC5_END), GFP_KERNEL); @@ -420,7 +525,12 @@ static int imx8ulp_clk_pcc5_init(struct platform_device *pdev) imx_check_clk_hws(clks, clk_data->num); - return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); + if (ret) + return ret; + + /* register the pcc5 reset controller */ + return imx8ulp_pcc_reset_init(pdev, base, pcc5_resets, ARRAY_SIZE(pcc5_resets)); } static int imx8ulp_clk_probe(struct platform_device *pdev) From d4e6c054fa953d06025b606b26939468df477fbf Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Fri, 17 Sep 2021 14:16:29 +0800 Subject: [PATCH 18/24] clk: imx: Fix the build break when clk-imx8ulp build as module Export the necessary symbols to fix the build break when clk-imx8ulp build as module Fixes: c43a801a5789 ("clk: imx: Add clock driver for imx8ulp") Signed-off-by: Jacky Bai Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210917061629.3798360-1-ping.bai@nxp.com Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-composite-7ulp.c | 1 + drivers/clk/imx/clk-pfdv2.c | 1 + drivers/clk/imx/clk-pllv4.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c index 89106de16a3f..4eedd45dbaa8 100644 --- a/drivers/clk/imx/clk-composite-7ulp.c +++ b/drivers/clk/imx/clk-composite-7ulp.c @@ -166,3 +166,4 @@ struct clk_hw *imx8ulp_clk_hw_composite(const char *name, const char * const *pa return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present, gate_present, reg, has_swrst); } +EXPORT_SYMBOL_GPL(imx8ulp_clk_hw_composite); diff --git a/drivers/clk/imx/clk-pfdv2.c b/drivers/clk/imx/clk-pfdv2.c index 42505669cdfb..6ca53a960eb7 100644 --- a/drivers/clk/imx/clk-pfdv2.c +++ b/drivers/clk/imx/clk-pfdv2.c @@ -239,3 +239,4 @@ struct clk_hw *imx_clk_hw_pfdv2(enum imx_pfdv2_type type, const char *name, return hw; } +EXPORT_SYMBOL_GPL(imx_clk_hw_pfdv2); diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c index 3c750ccbee25..6e7e34571fc8 100644 --- a/drivers/clk/imx/clk-pllv4.c +++ b/drivers/clk/imx/clk-pllv4.c @@ -254,3 +254,4 @@ struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name, return hw; } +EXPORT_SYMBOL_GPL(imx_clk_hw_pllv4); From 2f9d61869640f732599ec36b984c2b5c46067519 Mon Sep 17 00:00:00 2001 From: Stefan Riedmueller Date: Mon, 27 Sep 2021 09:28:56 +0200 Subject: [PATCH 19/24] clk: imx: imx6ul: Move csi_sel mux to correct base register The csi_sel mux register is located in the CCM register base and not the CCM_ANALOG register base. So move it to the correct position in code. Otherwise changing the parent of the csi clock can lead to a complete system failure due to the CCM_ANALOG_PLL_SYS_TOG register being falsely modified. Also remove the SET_RATE_PARENT flag since one possible supply for the csi_sel mux is the system PLL which we don't want to modify. Signed-off-by: Stefan Riedmueller Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210927072857.3940880-1-s.riedmueller@phytec.de Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx6ul.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index 5dbb6a937732..206e4c43f68f 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -161,7 +161,6 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) hws[IMX6UL_PLL5_BYPASS] = imx_clk_hw_mux_flags("pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT); hws[IMX6UL_PLL6_BYPASS] = imx_clk_hw_mux_flags("pll6_bypass", base + 0xe0, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT); hws[IMX6UL_PLL7_BYPASS] = imx_clk_hw_mux_flags("pll7_bypass", base + 0x20, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT); - hws[IMX6UL_CLK_CSI_SEL] = imx_clk_hw_mux_flags("csi_sel", base + 0x3c, 9, 2, csi_sels, ARRAY_SIZE(csi_sels), CLK_SET_RATE_PARENT); /* Do not bypass PLLs initially */ clk_set_parent(hws[IMX6UL_PLL1_BYPASS]->clk, hws[IMX6UL_CLK_PLL1]->clk); @@ -270,6 +269,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) hws[IMX6UL_CLK_ECSPI_SEL] = imx_clk_hw_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); hws[IMX6UL_CLK_LCDIF_PRE_SEL] = imx_clk_hw_mux_flags("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels), CLK_SET_RATE_PARENT); hws[IMX6UL_CLK_LCDIF_SEL] = imx_clk_hw_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels)); + hws[IMX6UL_CLK_CSI_SEL] = imx_clk_hw_mux("csi_sel", base + 0x3c, 9, 2, csi_sels, ARRAY_SIZE(csi_sels)); hws[IMX6UL_CLK_LDB_DI0_DIV_SEL] = imx_clk_hw_mux("ldb_di0", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels)); hws[IMX6UL_CLK_LDB_DI1_DIV_SEL] = imx_clk_hw_mux("ldb_di1", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels)); From d1012253a2d377f389ea09a82e04b2241fb30b10 Mon Sep 17 00:00:00 2001 From: Stefan Riedmueller Date: Mon, 27 Sep 2021 09:28:57 +0200 Subject: [PATCH 20/24] clk: imx: imx6ul: Fix csi clk gate register According to the imx6ul Reference Manual the csi clk gate register is CCM_CCGR3 (offset 0x74) bit 0/1. For the imx6ull on the other hand the Reference Manual lists register CCM_CCGR2 (offset 0x70) bit 2/3 as the csi clk gate which is the current setting. Tests have shown though that the correct csi clk gate register for the imx6ull is actually CCM_CCGR3 bit 0/1 as well. Thus set the correct register for both platforms. Signed-off-by: Stefan Riedmueller Tested-by: Fabio Estevam Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20210927072857.3940880-2-s.riedmueller@phytec.de Signed-off-by: Abel Vesa --- drivers/clk/imx/clk-imx6ul.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index 206e4c43f68f..520b100bff4b 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -380,7 +380,6 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) hws[IMX6ULL_CLK_ESAI_IPG] = imx_clk_hw_gate2_shared("esai_ipg", "ahb", base + 0x70, 0, &share_count_esai); hws[IMX6ULL_CLK_ESAI_MEM] = imx_clk_hw_gate2_shared("esai_mem", "ahb", base + 0x70, 0, &share_count_esai); } - hws[IMX6UL_CLK_CSI] = imx_clk_hw_gate2("csi", "csi_podf", base + 0x70, 2); hws[IMX6UL_CLK_I2C1] = imx_clk_hw_gate2("i2c1", "perclk", base + 0x70, 6); hws[IMX6UL_CLK_I2C2] = imx_clk_hw_gate2("i2c2", "perclk", base + 0x70, 8); hws[IMX6UL_CLK_I2C3] = imx_clk_hw_gate2("i2c3", "perclk", base + 0x70, 10); @@ -391,6 +390,12 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) hws[IMX6UL_CLK_PXP] = imx_clk_hw_gate2("pxp", "axi", base + 0x70, 30); /* CCGR3 */ + /* + * Although the imx6ull reference manual lists CCGR2 as the csi clk + * gate register, tests have shown that it is actually the CCGR3 + * register bit 0/1, same as for the imx6ul. + */ + hws[IMX6UL_CLK_CSI] = imx_clk_hw_gate2("csi", "csi_podf", base + 0x74, 0); hws[IMX6UL_CLK_UART5_IPG] = imx_clk_hw_gate2("uart5_ipg", "ipg", base + 0x74, 2); hws[IMX6UL_CLK_UART5_SERIAL] = imx_clk_hw_gate2("uart5_serial", "uart_podf", base + 0x74, 2); if (clk_on_imx6ul()) { From e8271eff5d8c8499289380edbf3bc47de83ab70b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 6 Oct 2021 16:00:08 -0300 Subject: [PATCH 21/24] clk: imx: Make CLK_IMX8ULP select MXC_CLK Building CLK_IMX8ULP without selecting MXC_CLK causes the following build errors: ld: drivers/clk/imx/clk-imx8ulp.o: in function `imx8ulp_clk_cgc2_init': clk-imx8ulp.c:(.text+0xd0): undefined reference to `imx_ccm_lock' ld: clk-imx8ulp.c:(.text+0x14f): undefined reference to `imx_clk_hw_pllv4' ld: clk-imx8ulp.c:(.text+0x15a): undefined reference to `imx_ccm_lock' Avoid this problem by making CLK_IMX8ULP select MXC_CLK. Fixes: c43a801a5789 ("clk: imx: Add clock driver for imx8ulp") Reported-by: Randy Dunlap Signed-off-by: Fabio Estevam Acked-by: Randy Dunlap Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20211006190008.1935051-1-festevam@gmail.com Signed-off-by: Abel Vesa --- drivers/clk/imx/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index b81d6437ed95..c08edbd04d22 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -102,5 +102,6 @@ config CLK_IMX8QXP config CLK_IMX8ULP tristate "IMX8ULP CCM Clock Driver" depends on ARCH_MXC || COMPILE_TEST + select MXC_CLK help Build the driver for i.MX8ULP CCM Clock Driver From f2b883bbdd08b32f4c52cc95bccb4defae6ebf74 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 21 Sep 2021 20:48:02 +0200 Subject: [PATCH 22/24] dt-bindings: clock: u8500: Rewrite in YAML and extend This rewrites the ux500/u8500 clock bindings in YAML schema and extends them with the PRCC reset controller. The bindings are a bit idiomatic but it just reflects their age, the ux500 platform was used as guinea pig for early device tree conversion of platforms in 2015. The new subnode for the reset controller follows the pattern of the old bindings and adds a node with reset-cells for this. Cc: devicetree@vger.kernel.org Cc: Philipp Zabel Reviewed-by: Rob Herring Acked-by: Ulf Hansson Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20210921184803.1757916-1-linus.walleij@linaro.org Signed-off-by: Stephen Boyd --- .../bindings/clock/stericsson,u8500-clks.yaml | 121 ++++++++++++++++++ .../devicetree/bindings/clock/ux500.txt | 64 --------- .../reset/stericsson,db8500-prcc-reset.h | 51 ++++++++ 3 files changed, 172 insertions(+), 64 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/stericsson,u8500-clks.yaml delete mode 100644 Documentation/devicetree/bindings/clock/ux500.txt create mode 100644 include/dt-bindings/reset/stericsson,db8500-prcc-reset.h diff --git a/Documentation/devicetree/bindings/clock/stericsson,u8500-clks.yaml b/Documentation/devicetree/bindings/clock/stericsson,u8500-clks.yaml new file mode 100644 index 000000000000..9bc95a308477 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/stericsson,u8500-clks.yaml @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/stericsson,u8500-clks.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST-Ericsson DB8500 (U8500) clocks + +maintainers: + - Ulf Hansson + - Linus Walleij + +description: While named "U8500 clocks" these clocks are inside the + DB8500 digital baseband system-on-chip and its siblings such as + DB8520. These bindings consider the clocks present in the SoC + itself, not off-chip clocks. There are four different on-chip + clocks - RTC (32 kHz), CPU clock (SMP TWD), PRCMU (power reset and + control management unit) clocks and PRCC (peripheral reset and + clock controller) clocks. For some reason PRCC 4 does not exist so + the itemization can be a bit unintuitive. + +properties: + compatible: + enum: + - stericsson,u8500-clks + - stericsson,u8540-clks + - stericsson,u9540-clks + + reg: + items: + - description: PRCC 1 register area + - description: PRCC 2 register area + - description: PRCC 3 register area + - description: PRCC 5 register area + - description: PRCC 6 register area + + prcmu-clock: + description: A subnode with one clock cell for PRCMU (power, reset, control + management unit) clocks. The cell indicates which PRCMU clock in the + prcmu-clock node the consumer wants to use. + type: object + + properties: + '#clock-cells': + const: 1 + + additionalProperties: false + + prcc-periph-clock: + description: A subnode with two clock cells for PRCC (peripheral + reset and clock controller) peripheral clocks. The first cell indicates + which PRCC block the consumer wants to use, possible values are 1, 2, 3, + 5, 6. The second cell indicates which clock inside the PRCC block it + wants, possible values are 0 thru 31. + type: object + + properties: + '#clock-cells': + const: 2 + + additionalProperties: false + + prcc-kernel-clock: + description: A subnode with two clock cells for PRCC (peripheral reset + and clock controller) kernel clocks. The first cell indicates which PRCC + block the consumer wants to use, possible values are 1, 2, 3, 5, 6. The + second cell indicates which clock inside the PRCC block it wants, possible + values are 0 thru 31. + type: object + + properties: + '#clock-cells': + const: 2 + + additionalProperties: false + + prcc-reset-controller: + description: A subnode with two reset cells for the reset portions of the + PRCC (peripheral reset and clock controller). The first cell indicates + which PRCC block the consumer wants to use, possible values are 1, 2, 3 + 5 and 6. The second cell indicates which reset line inside the PRCC block + it wants to control, possible values are 0 thru 31. + type: object + + properties: + '#reset-cells': + const: 2 + + additionalProperties: false + + rtc32k-clock: + description: A subnode with zero clock cells for the 32kHz RTC clock. + type: object + + properties: + '#clock-cells': + const: 0 + + additionalProperties: false + + smp-twd-clock: + description: A subnode for the ARM SMP Timer Watchdog cluster with zero + clock cells. + type: object + + properties: + '#clock-cells': + const: 0 + + additionalProperties: false + +required: + - compatible + - reg + - prcmu-clock + - prcc-periph-clock + - prcc-kernel-clock + - rtc32k-clock + - smp-twd-clock + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/clock/ux500.txt b/Documentation/devicetree/bindings/clock/ux500.txt deleted file mode 100644 index e52bd4b72348..000000000000 --- a/Documentation/devicetree/bindings/clock/ux500.txt +++ /dev/null @@ -1,64 +0,0 @@ -Clock bindings for ST-Ericsson Ux500 clocks - -Required properties : -- compatible : shall contain only one of the following: - "stericsson,u8500-clks" - "stericsson,u8540-clks" - "stericsson,u9540-clks" -- reg : shall contain base register location and length for - CLKRST1, 2, 3, 5, and 6 in an array. Note the absence of - CLKRST4, which does not exist. - -Required subnodes: -- prcmu-clock: a subnode with one clock cell for PRCMU (power, - reset, control unit) clocks. The cell indicates which PRCMU - clock in the prcmu-clock node the consumer wants to use. -- prcc-periph-clock: a subnode with two clock cells for - PRCC (programmable reset- and clock controller) peripheral clocks. - The first cell indicates which PRCC block the consumer - wants to use, possible values are 1, 2, 3, 5, 6. The second - cell indicates which clock inside the PRCC block it wants, - possible values are 0 thru 31. -- prcc-kernel-clock: a subnode with two clock cells for - PRCC (programmable reset- and clock controller) kernel clocks - The first cell indicates which PRCC block the consumer - wants to use, possible values are 1, 2, 3, 5, 6. The second - cell indicates which clock inside the PRCC block it wants, - possible values are 0 thru 31. -- rtc32k-clock: a subnode with zero clock cells for the 32kHz - RTC clock. -- smp-twd-clock: a subnode for the ARM SMP Timer Watchdog cluster - with zero clock cells. - -Example: - -clocks { - compatible = "stericsson,u8500-clks"; - /* - * Registers for the CLKRST block on peripheral - * groups 1, 2, 3, 5, 6, - */ - reg = <0x8012f000 0x1000>, <0x8011f000 0x1000>, - <0x8000f000 0x1000>, <0xa03ff000 0x1000>, - <0xa03cf000 0x1000>; - - prcmu_clk: prcmu-clock { - #clock-cells = <1>; - }; - - prcc_pclk: prcc-periph-clock { - #clock-cells = <2>; - }; - - prcc_kclk: prcc-kernel-clock { - #clock-cells = <2>; - }; - - rtc_clk: rtc32k-clock { - #clock-cells = <0>; - }; - - smp_twd_clk: smp-twd-clock { - #clock-cells = <0>; - }; -}; diff --git a/include/dt-bindings/reset/stericsson,db8500-prcc-reset.h b/include/dt-bindings/reset/stericsson,db8500-prcc-reset.h new file mode 100644 index 000000000000..ea906896c70f --- /dev/null +++ b/include/dt-bindings/reset/stericsson,db8500-prcc-reset.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _DT_BINDINGS_STE_PRCC_RESET +#define _DT_BINDINGS_STE_PRCC_RESET + +#define DB8500_PRCC_1 1 +#define DB8500_PRCC_2 2 +#define DB8500_PRCC_3 3 +#define DB8500_PRCC_6 6 + +/* Reset lines on PRCC 1 */ +#define DB8500_PRCC_1_RESET_UART0 0 +#define DB8500_PRCC_1_RESET_UART1 1 +#define DB8500_PRCC_1_RESET_I2C1 2 +#define DB8500_PRCC_1_RESET_MSP0 3 +#define DB8500_PRCC_1_RESET_MSP1 4 +#define DB8500_PRCC_1_RESET_SDI0 5 +#define DB8500_PRCC_1_RESET_I2C2 6 +#define DB8500_PRCC_1_RESET_SPI3 7 +#define DB8500_PRCC_1_RESET_SLIMBUS0 8 +#define DB8500_PRCC_1_RESET_I2C4 9 +#define DB8500_PRCC_1_RESET_MSP3 10 +#define DB8500_PRCC_1_RESET_PER_MSP3 11 +#define DB8500_PRCC_1_RESET_PER_MSP1 12 +#define DB8500_PRCC_1_RESET_PER_MSP0 13 +#define DB8500_PRCC_1_RESET_PER_SLIMBUS 14 + +/* Reset lines on PRCC 2 */ +#define DB8500_PRCC_2_RESET_I2C3 0 +#define DB8500_PRCC_2_RESET_PWL 1 +#define DB8500_PRCC_2_RESET_SDI4 2 +#define DB8500_PRCC_2_RESET_MSP2 3 +#define DB8500_PRCC_2_RESET_SDI1 4 +#define DB8500_PRCC_2_RESET_SDI3 5 +#define DB8500_PRCC_2_RESET_HSIRX 6 +#define DB8500_PRCC_2_RESET_HSITX 7 +#define DB8500_PRCC_1_RESET_PER_MSP2 8 + +/* Reset lines on PRCC 3 */ +#define DB8500_PRCC_3_RESET_SSP0 1 +#define DB8500_PRCC_3_RESET_SSP1 2 +#define DB8500_PRCC_3_RESET_I2C0 3 +#define DB8500_PRCC_3_RESET_SDI2 4 +#define DB8500_PRCC_3_RESET_SKE 5 +#define DB8500_PRCC_3_RESET_UART2 6 +#define DB8500_PRCC_3_RESET_SDI5 7 + +/* Reset lines on PRCC 6 */ +#define DB8500_PRCC_3_RESET_RNG 0 + +#endif From b14cbdfd467d1e505ad8e03f94e18b3cffc37043 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 21 Sep 2021 20:48:03 +0200 Subject: [PATCH 23/24] clk: ux500: Add driver for the reset portions of PRCC The Ux500 PRCC (peripheral reset and clock controller) can also control reset of the IP blocks, not just clocks. As the PRCC is probed as a clock controller and we have other platforms implementing combined clock and reset controllers, follow this pattern and implement the PRCC rest controller as part of the clock driver. The reset controller needs to be selected from the machine as Ux500 has traditionally selected its mandatory subsystem prerequisites from there. Cc: Philipp Zabel Cc: Ulf Hansson Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20210921184803.1757916-2-linus.walleij@linaro.org Acked-by: Ulf Hansson [sboyd@kernel.org: Dropped allocation error message] Signed-off-by: Stephen Boyd --- arch/arm/mach-ux500/Kconfig | 1 + drivers/clk/ux500/Makefile | 3 + drivers/clk/ux500/prcc.h | 19 ++++ drivers/clk/ux500/reset-prcc.c | 181 +++++++++++++++++++++++++++++++ drivers/clk/ux500/reset-prcc.h | 23 ++++ drivers/clk/ux500/u8500_of_clk.c | 30 ++--- 6 files changed, 244 insertions(+), 13 deletions(-) create mode 100644 drivers/clk/ux500/prcc.h create mode 100644 drivers/clk/ux500/reset-prcc.c create mode 100644 drivers/clk/ux500/reset-prcc.h diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig index c1086ebe0050..24ed7f4a87a4 100644 --- a/arch/arm/mach-ux500/Kconfig +++ b/arch/arm/mach-ux500/Kconfig @@ -29,6 +29,7 @@ menuconfig ARCH_U8500 select REGULATOR_DB8500_PRCMU select REGULATOR_FIXED_VOLTAGE select SOC_BUS + select RESET_CONTROLLER help Support for ST-Ericsson's Ux500 architecture diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile index 53fd29002401..c29b83df403e 100644 --- a/drivers/clk/ux500/Makefile +++ b/drivers/clk/ux500/Makefile @@ -8,6 +8,9 @@ obj-y += clk-prcc.o obj-y += clk-prcmu.o obj-y += clk-sysctrl.o +# Reset control +obj-y += reset-prcc.o + # Clock definitions obj-y += u8500_of_clk.o diff --git a/drivers/clk/ux500/prcc.h b/drivers/clk/ux500/prcc.h new file mode 100644 index 000000000000..5b6774d79506 --- /dev/null +++ b/drivers/clk/ux500/prcc.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __PRCC_H +#define __PRCC_H + +#define PRCC_NUM_PERIPH_CLUSTERS 6 +#define PRCC_PERIPHS_PER_CLUSTER 32 + +/* CLKRST4 is missing making it hard to index things */ +enum clkrst_index { + CLKRST1_INDEX = 0, + CLKRST2_INDEX, + CLKRST3_INDEX, + CLKRST5_INDEX, + CLKRST6_INDEX, + CLKRST_MAX, +}; + +#endif diff --git a/drivers/clk/ux500/reset-prcc.c b/drivers/clk/ux500/reset-prcc.c new file mode 100644 index 000000000000..fcd5d042806a --- /dev/null +++ b/drivers/clk/ux500/reset-prcc.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Reset controller portions for the U8500 PRCC + * Copyright (C) 2021 Linus Walleij + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prcc.h" +#include "reset-prcc.h" + +#define to_u8500_prcc_reset(p) container_of((p), struct u8500_prcc_reset, rcdev) + +/* This macro flattens the 2-dimensional PRCC numberspace */ +#define PRCC_RESET_LINE(prcc_num, bit) \ + (((prcc_num) * PRCC_PERIPHS_PER_CLUSTER) + (bit)) + +/* + * Reset registers in each PRCC - the reset lines are active low + * so what you need to do is write a bit for the peripheral you + * want to put into reset into the CLEAR register, this will assert + * the reset by pulling the line low. SET take the device out of + * reset. The status reflects the actual state of the line. + */ +#define PRCC_K_SOFTRST_SET 0x018 +#define PRCC_K_SOFTRST_CLEAR 0x01c +#define PRCC_K_RST_STATUS 0x020 + +static int prcc_num_to_index(unsigned int num) +{ + switch (num) { + case 1: + return CLKRST1_INDEX; + case 2: + return CLKRST2_INDEX; + case 3: + return CLKRST3_INDEX; + case 5: + return CLKRST5_INDEX; + case 6: + return CLKRST6_INDEX; + } + return -EINVAL; +} + +static void __iomem *u8500_prcc_reset_base(struct u8500_prcc_reset *ur, + unsigned long id) +{ + unsigned int prcc_num, index; + + prcc_num = id / PRCC_PERIPHS_PER_CLUSTER; + index = prcc_num_to_index(prcc_num); + + if (index > ARRAY_SIZE(ur->base)) + return NULL; + + return ur->base[index]; +} + +static int u8500_prcc_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct u8500_prcc_reset *ur = to_u8500_prcc_reset(rcdev); + void __iomem *base = u8500_prcc_reset_base(ur, id); + unsigned int bit = id % PRCC_PERIPHS_PER_CLUSTER; + + pr_debug("PRCC cycle reset id %lu, bit %u\n", id, bit); + + /* + * Assert reset and then release it. The one microsecond + * delay is found in the vendor reference code. + */ + writel(BIT(bit), base + PRCC_K_SOFTRST_CLEAR); + udelay(1); + writel(BIT(bit), base + PRCC_K_SOFTRST_SET); + udelay(1); + + return 0; +} + +static int u8500_prcc_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct u8500_prcc_reset *ur = to_u8500_prcc_reset(rcdev); + void __iomem *base = u8500_prcc_reset_base(ur, id); + unsigned int bit = id % PRCC_PERIPHS_PER_CLUSTER; + + pr_debug("PRCC assert reset id %lu, bit %u\n", id, bit); + writel(BIT(bit), base + PRCC_K_SOFTRST_CLEAR); + + return 0; +} + +static int u8500_prcc_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct u8500_prcc_reset *ur = to_u8500_prcc_reset(rcdev); + void __iomem *base = u8500_prcc_reset_base(ur, id); + unsigned int bit = id % PRCC_PERIPHS_PER_CLUSTER; + + pr_debug("PRCC deassert reset id %lu, bit %u\n", id, bit); + writel(BIT(bit), base + PRCC_K_SOFTRST_SET); + + return 0; +} + +static int u8500_prcc_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct u8500_prcc_reset *ur = to_u8500_prcc_reset(rcdev); + void __iomem *base = u8500_prcc_reset_base(ur, id); + unsigned int bit = id % PRCC_PERIPHS_PER_CLUSTER; + u32 val; + + pr_debug("PRCC check status on reset line id %lu, bit %u\n", id, bit); + val = readl(base + PRCC_K_RST_STATUS); + + /* Active low so return the inverse value of the bit */ + return !(val & BIT(bit)); +} + +static const struct reset_control_ops u8500_prcc_reset_ops = { + .reset = u8500_prcc_reset, + .assert = u8500_prcc_reset_assert, + .deassert = u8500_prcc_reset_deassert, + .status = u8500_prcc_reset_status, +}; + +static int u8500_prcc_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + unsigned int prcc_num, bit; + + if (reset_spec->args_count != 2) + return -EINVAL; + + prcc_num = reset_spec->args[0]; + bit = reset_spec->args[1]; + + if (prcc_num != 1 && prcc_num != 2 && prcc_num != 3 && + prcc_num != 5 && prcc_num != 6) { + pr_err("%s: invalid PRCC %d\n", __func__, prcc_num); + return -EINVAL; + } + + pr_debug("located reset line %d at PRCC %d bit %d\n", + PRCC_RESET_LINE(prcc_num, bit), prcc_num, bit); + + return PRCC_RESET_LINE(prcc_num, bit); +} + +void u8500_prcc_reset_init(struct device_node *np, struct u8500_prcc_reset *ur) +{ + struct reset_controller_dev *rcdev = &ur->rcdev; + int ret; + int i; + + for (i = 0; i < CLKRST_MAX; i++) { + ur->base[i] = ioremap(ur->phy_base[i], SZ_4K); + if (!ur->base[i]) + pr_err("PRCC failed to remap for reset base %d (%08x)\n", + i, ur->phy_base[i]); + } + + rcdev->owner = THIS_MODULE; + rcdev->ops = &u8500_prcc_reset_ops; + rcdev->of_node = np; + rcdev->of_reset_n_cells = 2; + rcdev->of_xlate = u8500_prcc_reset_xlate; + + ret = reset_controller_register(rcdev); + if (ret) + pr_err("PRCC failed to register reset controller\n"); +} diff --git a/drivers/clk/ux500/reset-prcc.h b/drivers/clk/ux500/reset-prcc.h new file mode 100644 index 000000000000..353c9719f2e6 --- /dev/null +++ b/drivers/clk/ux500/reset-prcc.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __RESET_PRCC_H +#define __RESET_PRCC_H + +#include +#include + +/** + * struct u8500_prcc_reset - U8500 PRCC reset controller state + * @rcdev: reset controller device + * @phy_base: the physical base address for each PRCC block + * @base: the remapped PRCC bases + */ +struct u8500_prcc_reset { + struct reset_controller_dev rcdev; + u32 phy_base[CLKRST_MAX]; + void __iomem *base[CLKRST_MAX]; +}; + +void u8500_prcc_reset_init(struct device_node *np, struct u8500_prcc_reset *ur); + +#endif diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index 528c5bb397cc..e86ed2eec3fd 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -10,10 +10,10 @@ #include #include #include -#include "clk.h" -#define PRCC_NUM_PERIPH_CLUSTERS 6 -#define PRCC_PERIPHS_PER_CLUSTER 32 +#include "clk.h" +#include "prcc.h" +#include "reset-prcc.h" static struct clk *prcmu_clk[PRCMU_NUM_CLKS]; static struct clk *prcc_pclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_CLUSTER]; @@ -46,16 +46,6 @@ static struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, return PRCC_SHOW(clk_data, base, bit); } -/* CLKRST4 is missing making it hard to index things */ -enum clkrst_index { - CLKRST1_INDEX = 0, - CLKRST2_INDEX, - CLKRST3_INDEX, - CLKRST5_INDEX, - CLKRST6_INDEX, - CLKRST_MAX, -}; - static void u8500_clk_init(struct device_node *np) { struct prcmu_fw_version *fw_version; @@ -63,8 +53,18 @@ static void u8500_clk_init(struct device_node *np) const char *sgaclk_parent = NULL; struct clk *clk, *rtc_clk, *twd_clk; u32 bases[CLKRST_MAX]; + struct u8500_prcc_reset *rstc; int i; + /* + * We allocate the reset controller here so that we can fill in the + * base addresses properly and pass to the reset controller init + * function later on. + */ + rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return; + for (i = 0; i < ARRAY_SIZE(bases); i++) { struct resource r; @@ -73,6 +73,7 @@ static void u8500_clk_init(struct device_node *np) pr_err("failed to get CLKRST %d base address\n", i + 1); bases[i] = r.start; + rstc->phy_base[i] = r.start; } /* Clock sources */ @@ -563,6 +564,9 @@ static void u8500_clk_init(struct device_node *np) if (of_node_name_eq(child, "smp-twd-clock")) of_clk_add_provider(child, of_clk_src_simple_get, twd_clk); + + if (of_node_name_eq(child, "prcc-reset-controller")) + u8500_prcc_reset_init(child, rstc); } } CLK_OF_DECLARE(u8500_clks, "stericsson,u8500-clks", u8500_clk_init); From dd742cac340f3b4711e0c0a355d63133c0ee4944 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 11 Oct 2021 14:27:19 +0300 Subject: [PATCH 24/24] clk: use clk_core_get_rate_recalc() in clk_rate_get() In case clock flags contains CLK_GET_RATE_NOCACHE the clk_rate_get() will return the cached rate. Thus, use clk_core_get_rate_recalc() which takes proper action when clock flags contains CLK_GET_RATE_NOCACHE. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20211011112719.3951784-16-claudiu.beznea@microchip.com Acked-by: Nicolas Ferre [sboyd@kernel.org: Grab prepare lock around operation] Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 65508eb89ec9..f467d63bbf1e 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3108,7 +3108,10 @@ static int clk_rate_get(void *data, u64 *val) { struct clk_core *core = data; - *val = core->rate; + clk_prepare_lock(); + *val = clk_core_get_rate_recalc(core); + clk_prepare_unlock(); + return 0; }