mbox series

[v3,0/9] Add imx8ulp clock & reset driver support

Message ID 20210914065208.3582128-1-ping.bai@nxp.com
Headers show
Series Add imx8ulp clock & reset driver support | expand

Message

Jacky Bai Sept. 14, 2021, 6:51 a.m. UTC
This patchset adds the clock & reset driver support for i.MX8ULP.
For some of the PCC slot, As there is a SWRST control bit share
the same pcc register for peripheral reset ccontrol. To simplify
the case, register the pcc reset controller driver when pcc
clock driver is registered.

Patch 1/9 for the dt-bindings part is send out for review previously
with the dts patchset:
https://patchwork.kernel.org/project/linux-arm-kernel/cover/20210607083921.2668568-1-ping.bai@nxp.com/

Shawn suggests to send out the clock driver part firstly, so this
patch is included in this patchset for now.

v3 changes:
  - split the clock dt schema file into two file, one for pcc, one for cgc

v2 changes:
  - remove the useless clocks & clock-names from the dt-binding doc
  - remove the redundant fixed clock register.

Anson Huang (1):
  clk: imx: disable i.mx7ulp composite clock during initialization

Jacky Bai (8):
  dt-bindings: clock: Add imx8ulp clock support
  clk: imx: Update the pllv4 to support imx8ulp
  clk: imx: Update the compsite driver to support imx8ulp
  clk: imx: Add 'CLK_SET_RATE_NO_REPARENT' for composite-7ulp
  clk: imx: disable the pfd when set pfdv2 clock rate
  clk: imx: Update the pfdv2 for 8ulp specific support
  clk: imx: Add clock driver for imx8ulp
  clk: imx: Add the pcc reset controller support on imx8ulp

 .../bindings/clock/imx8ulp-cgc-clock.yaml     |  43 ++
 .../bindings/clock/imx8ulp-pcc-clock.yaml     |  50 ++
 drivers/clk/imx/Kconfig                       |   6 +
 drivers/clk/imx/Makefile                      |   2 +
 drivers/clk/imx/clk-composite-7ulp.c          |  87 ++-
 drivers/clk/imx/clk-imx7ulp.c                 |  20 +-
 drivers/clk/imx/clk-imx8ulp.c                 | 569 ++++++++++++++++++
 drivers/clk/imx/clk-pfdv2.c                   |  22 +-
 drivers/clk/imx/clk-pllv4.c                   |  34 +-
 drivers/clk/imx/clk.h                         |  24 +-
 include/dt-bindings/clock/imx8ulp-clock.h     | 258 ++++++++
 include/dt-bindings/reset/imx8ulp-pcc-reset.h |  59 ++
 12 files changed, 1142 insertions(+), 32 deletions(-)
 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 drivers/clk/imx/clk-imx8ulp.c
 create mode 100644 include/dt-bindings/clock/imx8ulp-clock.h
 create mode 100644 include/dt-bindings/reset/imx8ulp-pcc-reset.h

Comments

Abel Vesa Sept. 14, 2021, 11:17 a.m. UTC | #1
On 21-09-14 14:51:59, Jacky Bai wrote:
> This patchset adds the clock & reset driver support for i.MX8ULP.
> For some of the PCC slot, As there is a SWRST control bit share
> the same pcc register for peripheral reset ccontrol. To simplify
> the case, register the pcc reset controller driver when pcc
> clock driver is registered.
> 
> Patch 1/9 for the dt-bindings part is send out for review previously
> with the dts patchset:
> https://patchwork.kernel.org/project/linux-arm-kernel/cover/20210607083921.2668568-1-ping.bai@nxp.com/
> 
> Shawn suggests to send out the clock driver part firstly, so this
> patch is included in this patchset for now.

Usually, the clock patches need to be sent to linux-clk mailing list
too.

> 
> v3 changes:
>   - split the clock dt schema file into two file, one for pcc, one for cgc
> 
> v2 changes:
>   - remove the useless clocks & clock-names from the dt-binding doc
>   - remove the redundant fixed clock register.
> 
> Anson Huang (1):
>   clk: imx: disable i.mx7ulp composite clock during initialization
> 
> Jacky Bai (8):
>   dt-bindings: clock: Add imx8ulp clock support
>   clk: imx: Update the pllv4 to support imx8ulp
>   clk: imx: Update the compsite driver to support imx8ulp
>   clk: imx: Add 'CLK_SET_RATE_NO_REPARENT' for composite-7ulp
>   clk: imx: disable the pfd when set pfdv2 clock rate
>   clk: imx: Update the pfdv2 for 8ulp specific support
>   clk: imx: Add clock driver for imx8ulp
>   clk: imx: Add the pcc reset controller support on imx8ulp
> 
>  .../bindings/clock/imx8ulp-cgc-clock.yaml     |  43 ++
>  .../bindings/clock/imx8ulp-pcc-clock.yaml     |  50 ++
>  drivers/clk/imx/Kconfig                       |   6 +
>  drivers/clk/imx/Makefile                      |   2 +
>  drivers/clk/imx/clk-composite-7ulp.c          |  87 ++-
>  drivers/clk/imx/clk-imx7ulp.c                 |  20 +-
>  drivers/clk/imx/clk-imx8ulp.c                 | 569 ++++++++++++++++++
>  drivers/clk/imx/clk-pfdv2.c                   |  22 +-
>  drivers/clk/imx/clk-pllv4.c                   |  34 +-
>  drivers/clk/imx/clk.h                         |  24 +-
>  include/dt-bindings/clock/imx8ulp-clock.h     | 258 ++++++++
>  include/dt-bindings/reset/imx8ulp-pcc-reset.h |  59 ++
>  12 files changed, 1142 insertions(+), 32 deletions(-)
>  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 drivers/clk/imx/clk-imx8ulp.c
>  create mode 100644 include/dt-bindings/clock/imx8ulp-clock.h
>  create mode 100644 include/dt-bindings/reset/imx8ulp-pcc-reset.h
> 
> -- 
> 2.26.2
>
Abel Vesa Sept. 14, 2021, 11:29 a.m. UTC | #2
On 21-09-14 14:52:01, Jacky Bai wrote:
> 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 <ping.bai@nxp.com>

I'm OK with this one. But maybe later on we will have two separate
wrappers in clk.h called imx_clk_hw_pllv4_7ulp and
imx_clk_hw_pllv4_8ulp. We'll see. 

Reviewed-by: Abel Vesa <abel.vesa@nxp.com>

> ---
>   v3 changes: no
> ---
>  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 e144f983fd8c..3f518559b8f9 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;
> @@ -191,8 +196,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,
> -- 
> 2.26.2
>
Abel Vesa Sept. 14, 2021, 11:30 a.m. UTC | #3
On 21-09-14 14:52:02, Jacky Bai wrote:
> 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 <peng.fan@nxp.com>
> Signed-off-by: Jacky Bai <ping.bai@nxp.com>

Reviewed-by: Abel Vesa <abel.vesa@nxp.com>

> ---
>   v3 changs: no
> ---
>  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 3f518559b8f9..a9bcfee7a75b 100644
> --- a/drivers/clk/imx/clk.h
> +++ b/drivers/clk/imx/clk.h
> @@ -237,6 +237,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));
> -- 
> 2.26.2
>
Abel Vesa Sept. 14, 2021, 11:32 a.m. UTC | #4
On 21-09-14 14:52:03, Jacky Bai wrote:
> From: Anson Huang <Anson.Huang@nxp.com>
> 
> 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 <Anson.Huang@nxp.com>

Reviewed-by: Abel Vesa <abel.vesa@nxp.com>

> ---
>   v3 changes: no
> ---
>  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 <linux/bits.h>
>  #include <linux/clk-provider.h>
>  #include <linux/err.h>
> +#include <linux/io.h>
>  #include <linux/slab.h>
>  
>  #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,
> -- 
> 2.26.2
>
Abel Vesa Sept. 14, 2021, 11:33 a.m. UTC | #5
On 21-09-14 14:52:04, Jacky Bai wrote:
> 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 <ping.bai@nxp.com>

Reviewed-by: Abel Vesa <abel.vesa@nxp.com>

> ---
>   v3 changs: no
> ---
>  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);
> -- 
> 2.26.2
>
Abel Vesa Sept. 14, 2021, 11:34 a.m. UTC | #6
On 21-09-14 14:52:05, Jacky Bai wrote:
> 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 <ping.bai@nxp.com>

Reviewed-by: Abel Vesa <abel.vesa@nxp.com>

> ---
>   v3 changs: no
> ---
>  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);
> -- 
> 2.26.2
>
Abel Vesa Sept. 14, 2021, 11:35 a.m. UTC | #7
On 21-09-14 14:52:06, Jacky Bai wrote:
> 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 <ping.bai@nxp.com>

Reviewed-by: Abel Vesa <abel.vesa@nxp.com>

> ---
>   v3 changs: no
> ---
>  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 a9bcfee7a75b..45be7ba23fae 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;
> @@ -220,8 +225,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,
> -- 
> 2.26.2
>
Abel Vesa Sept. 14, 2021, 11:55 a.m. UTC | #8
On 21-09-14 14:52:07, Jacky Bai wrote:
> Add clock driver for i.MX8ULP.
> 
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> Signed-off-by: Jacky Bai <ping.bai@nxp.com>

Looks good to me.

Reviewed-by: Abel Vesa <abel.vesa@nxp.com>

> ---
>   v3 changes:
>      - no
> 
>   v2 changes:
>      - remove the redundent fixed sosc, fosc etc clock register
> ---
>  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 <dt-bindings/clock/imx8ulp-clock.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#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 <peng.fan@nxp.com>");
> +MODULE_DESCRIPTION("NXP i.MX8ULP clock driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.26.2
>
Abel Vesa Sept. 14, 2021, 12:09 p.m. UTC | #9
On 21-09-14 14:52:08, Jacky Bai wrote:
> 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 <linux/module.h>
>  #include <linux/of_device.h>
>  #include <linux/platform_device.h>
> +#include <linux/reset-controller.h>
>  #include <linux/slab.h>
>  
>  #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
> +};
> +

I believe this could be avoided entirely by having something like:

PCCx_RESETS_OFFSET + (index * 4)

and then:

#define PCC3_RESETS_OFFSET	0xa8
#define PCC4_RESETS_OFFSET	0x4
#define PCC5_RESETS_OFFSET	0xa0

#define PCC3_RESETS_NUM		13
#define PCC4_RESETS_NUM		17
#define PCC5_RESETS_NUM		14

Then we could pass on the PCCx_RESETS_OFFSET and the PCCx_RESETS_NUM and
that's it.

> +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);
> +}
> +

...

> -- 
> 2.26.2
>
Jacky Bai Sept. 14, 2021, 1:07 p.m. UTC | #10
> Subject: Re: [PATCH v3 9/9] clk: imx: Add the pcc reset controller support on
> imx8ulp
> 
> On 21-09-14 14:52:08, Jacky Bai wrote:
> > 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 <linux/module.h>
> >  #include <linux/of_device.h>
> >  #include <linux/platform_device.h>
> > +#include <linux/reset-controller.h>
> >  #include <linux/slab.h>
> >
> >  #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
> > +};
> > +
> 
> I believe this could be avoided entirely by having something like:
> 
> PCCx_RESETS_OFFSET + (index * 4)
> 
> and then:
> 
> #define PCC3_RESETS_OFFSET	0xa8
> #define PCC4_RESETS_OFFSET	0x4
> #define PCC5_RESETS_OFFSET	0xa0
> 
> #define PCC3_RESETS_NUM		13
> #define PCC4_RESETS_NUM		17
> #define PCC5_RESETS_NUM		14
> 
> Then we could pass on the PCCx_RESETS_OFFSET and the
> PCCx_RESETS_NUM and that's it.
> 

As you can see that the PCC offset that has reset is not consecutive in the register region.
So it will introduce hole in the index. And we can NOT use linear formula to calculate the offset directly.

BR
Jacky Bai

> > +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); }
> > +
> 
> ...
> 
> > --
> > 2.26.2
> >
Abel Vesa Sept. 14, 2021, 3:19 p.m. UTC | #11
On 21-09-14 16:07:50, Jacky Bai wrote:
> > Subject: Re: [PATCH v3 9/9] clk: imx: Add the pcc reset controller support on
> > imx8ulp
> > 
> > On 21-09-14 14:52:08, Jacky Bai wrote:
> > > 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 <linux/module.h>
> > >  #include <linux/of_device.h>
> > >  #include <linux/platform_device.h>
> > > +#include <linux/reset-controller.h>
> > >  #include <linux/slab.h>
> > >
> > >  #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
> > > +};
> > > +
> > 
> > I believe this could be avoided entirely by having something like:
> > 
> > PCCx_RESETS_OFFSET + (index * 4)
> > 
> > and then:
> > 
> > #define PCC3_RESETS_OFFSET	0xa8
> > #define PCC4_RESETS_OFFSET	0x4
> > #define PCC5_RESETS_OFFSET	0xa0
> > 
> > #define PCC3_RESETS_NUM		13
> > #define PCC4_RESETS_NUM		17
> > #define PCC5_RESETS_NUM		14
> > 
> > Then we could pass on the PCCx_RESETS_OFFSET and the
> > PCCx_RESETS_NUM and that's it.
> > 
> 
> As you can see that the PCC offset that has reset is not consecutive in the register region.
> So it will introduce hole in the index. And we can NOT use linear formula to calculate the offset directly.
> 

You are right, they don't seem to be consecutive, on a closer look.

I don't have any other comments, so:

Reviewed-by: Abel Vesa <abel.vesa@nxp.com>

> BR
> Jacky Bai
> 
> > > +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); }
> > > +
> > 
> > ...
> > 
> > > --
> > > 2.26.2
> > >
Abel Vesa Sept. 16, 2021, 6:46 a.m. UTC | #12
On 21-09-14 14:51:59, Jacky Bai wrote:
> This patchset adds the clock & reset driver support for i.MX8ULP.
> For some of the PCC slot, As there is a SWRST control bit share
> the same pcc register for peripheral reset ccontrol. To simplify
> the case, register the pcc reset controller driver when pcc
> clock driver is registered.
> 
> Patch 1/9 for the dt-bindings part is send out for review previously
> with the dts patchset:
> https://patchwork.kernel.org/project/linux-arm-kernel/cover/20210607083921.2668568-1-ping.bai@nxp.com/
> 
> Shawn suggests to send out the clock driver part firstly, so this
> patch is included in this patchset for now.
> 

Applied all. 

Thanks.

> v3 changes:
>   - split the clock dt schema file into two file, one for pcc, one for cgc
> 
> v2 changes:
>   - remove the useless clocks & clock-names from the dt-binding doc
>   - remove the redundant fixed clock register.
> 
> Anson Huang (1):
>   clk: imx: disable i.mx7ulp composite clock during initialization
> 
> Jacky Bai (8):
>   dt-bindings: clock: Add imx8ulp clock support
>   clk: imx: Update the pllv4 to support imx8ulp
>   clk: imx: Update the compsite driver to support imx8ulp
>   clk: imx: Add 'CLK_SET_RATE_NO_REPARENT' for composite-7ulp
>   clk: imx: disable the pfd when set pfdv2 clock rate
>   clk: imx: Update the pfdv2 for 8ulp specific support
>   clk: imx: Add clock driver for imx8ulp
>   clk: imx: Add the pcc reset controller support on imx8ulp
> 
>  .../bindings/clock/imx8ulp-cgc-clock.yaml     |  43 ++
>  .../bindings/clock/imx8ulp-pcc-clock.yaml     |  50 ++
>  drivers/clk/imx/Kconfig                       |   6 +
>  drivers/clk/imx/Makefile                      |   2 +
>  drivers/clk/imx/clk-composite-7ulp.c          |  87 ++-
>  drivers/clk/imx/clk-imx7ulp.c                 |  20 +-
>  drivers/clk/imx/clk-imx8ulp.c                 | 569 ++++++++++++++++++
>  drivers/clk/imx/clk-pfdv2.c                   |  22 +-
>  drivers/clk/imx/clk-pllv4.c                   |  34 +-
>  drivers/clk/imx/clk.h                         |  24 +-
>  include/dt-bindings/clock/imx8ulp-clock.h     | 258 ++++++++
>  include/dt-bindings/reset/imx8ulp-pcc-reset.h |  59 ++
>  12 files changed, 1142 insertions(+), 32 deletions(-)
>  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 drivers/clk/imx/clk-imx8ulp.c
>  create mode 100644 include/dt-bindings/clock/imx8ulp-clock.h
>  create mode 100644 include/dt-bindings/reset/imx8ulp-pcc-reset.h
> 
> -- 
> 2.26.2
>