diff mbox

[v3,4/7] clk: rockchip: Add support for multiple clock providers

Message ID 1457491027-30936-5-git-send-email-zhengxing@rock-chips.com (mailing list archive)
State New, archived
Headers show

Commit Message

zhengxing March 9, 2016, 2:37 a.m. UTC
There are need to support Multi-CRUs probability in future, but
it is not supported on the current Rockchip Clock Framework.

Therefore, this patch add support a provider as the parameter
handler when we call the clock register functions for per CRU.

Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>
---

Changes in v3: None
Changes in v2: None

 drivers/clk/rockchip/clk-pll.c    |   30 ++++----
 drivers/clk/rockchip/clk-rk3036.c |   17 +++--
 drivers/clk/rockchip/clk-rk3188.c |   48 ++++++++----
 drivers/clk/rockchip/clk-rk3228.c |   17 +++--
 drivers/clk/rockchip/clk-rk3288.c |   19 +++--
 drivers/clk/rockchip/clk-rk3368.c |   21 ++++--
 drivers/clk/rockchip/clk.c        |  148 +++++++++++++++++++++++--------------
 drivers/clk/rockchip/clk.h        |   49 ++++++++----
 8 files changed, 231 insertions(+), 118 deletions(-)

Comments

Heiko Stübner March 9, 2016, 10:25 p.m. UTC | #1
Hi Xing,

Am Mittwoch, 9. März 2016, 10:37:04 schrieb Xing Zheng:
> There are need to support Multi-CRUs probability in future, but
> it is not supported on the current Rockchip Clock Framework.
> 
> Therefore, this patch add support a provider as the parameter
> handler when we call the clock register functions for per CRU.
> 
> Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>

I've applied that in a clk branch for 4.7 [0] with some changes detailed
below. If you can, please check that I didn't mess anything up :-)

I've sucessfully booted that on both a rk3036 and rk3288 as well.


Heiko

[0] https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?h=v4.7-clk/next&id=d509ddf2e57c99ae760d1a289b85f1e0d729f864


> ---
> 
> Changes in v3: None
> Changes in v2: None
> 
>  drivers/clk/rockchip/clk-pll.c    |   30 ++++----
>  drivers/clk/rockchip/clk-rk3036.c |   17 +++--
>  drivers/clk/rockchip/clk-rk3188.c |   48 ++++++++----
>  drivers/clk/rockchip/clk-rk3228.c |   17 +++--
>  drivers/clk/rockchip/clk-rk3288.c |   19 +++--
>  drivers/clk/rockchip/clk-rk3368.c |   21 ++++--
>  drivers/clk/rockchip/clk.c        |  148
> +++++++++++++++++++++++-------------- drivers/clk/rockchip/clk.h        |  
> 49 ++++++++----
>  8 files changed, 231 insertions(+), 118 deletions(-)

[...]

> diff --git a/drivers/clk/rockchip/clk-rk3188.c
> b/drivers/clk/rockchip/clk-rk3188.c index e832403..7c73c51 100644
> --- a/drivers/clk/rockchip/clk-rk3188.c
> @@ -759,57 +759,78 @@ static const char *const rk3188_critical_clocks[]
> __initconst = { "hclk_cpubus"
>  };
> 
> -static void __init rk3188_common_clk_init(struct device_node *np)
> +static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct
> device_node *np) {
> +	struct rockchip_clk_provider *ctx;
>  	void __iomem *reg_base;
> 
>  	reg_base = of_iomap(np, 0);
>  	if (!reg_base) {
>  		pr_err("%s: could not map cru region\n", __func__);
> -		return;
> +		return ERR_PTR(-ENOMEM);
>  	}
> 
> -	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
> +	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
> +	if (IS_ERR(ctx)) {
> +		pr_err("%s: rockchip clk init failed\n", __func__);
> +		return ERR_PTR(-ENOMEM);
> +	}
> 
> -	rockchip_clk_register_branches(common_clk_branches,
> +	rockchip_clk_register_branches(ctx, common_clk_branches,
>  				  ARRAY_SIZE(common_clk_branches));
> 
>  	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
>  				  ROCKCHIP_SOFTRST_HIWORD_MASK);
> 
> -	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
> +	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
> +
> +	return ctx;
>  }
> 
>  static void __init rk3066a_clk_init(struct device_node *np)
>  {
> -	rk3188_common_clk_init(np);
> -	rockchip_clk_register_plls(rk3066_pll_clks,
> +	struct rockchip_clk_provider *ctx;
> +
> +	ctx = rk3188_common_clk_init(np);
> +	if (IS_ERR(ctx)) {
> +		pr_err("%s: common clk init failed\n", __func__);
> +		return;
> +	}

I've dropped the pr_err + parentheses, as rk3188_common_clk_init
will already output a suitable error.


> +
> +	rockchip_clk_register_plls(ctx, rk3066_pll_clks,
>  				   ARRAY_SIZE(rk3066_pll_clks),
>  				   RK3066_GRF_SOC_STATUS);
> -	rockchip_clk_register_branches(rk3066a_clk_branches,
> +	rockchip_clk_register_branches(ctx, rk3066a_clk_branches,
>  				  ARRAY_SIZE(rk3066a_clk_branches));
> -	rockchip_clk_register_armclk(ARMCLK, "armclk",
> +	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
>  			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
>  			&rk3066_cpuclk_data, rk3066_cpuclk_rates,
>  			ARRAY_SIZE(rk3066_cpuclk_rates));
>  	rockchip_clk_protect_critical(rk3188_critical_clocks,
>  				      ARRAY_SIZE(rk3188_critical_clocks));
> +	rockchip_clk_of_add_provider(np, ctx);
>  }
>  CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
> 
>  static void __init rk3188a_clk_init(struct device_node *np)
>  {
> +	struct rockchip_clk_provider *ctx;
>  	struct clk *clk1, *clk2;
>  	unsigned long rate;
>  	int ret;
> 
> -	rk3188_common_clk_init(np);
> -	rockchip_clk_register_plls(rk3188_pll_clks,
> +	ctx = rk3188_common_clk_init(np);
> +	if (IS_ERR(ctx)) {
> +		pr_err("%s: common clk init failed\n", __func__);
> +		return;
> +	}

same as above


> +
> +	rockchip_clk_register_plls(ctx, rk3188_pll_clks,
>  				   ARRAY_SIZE(rk3188_pll_clks),
>  				   RK3188_GRF_SOC_STATUS);
> -	rockchip_clk_register_branches(rk3188_clk_branches,
> +	rockchip_clk_register_branches(ctx, rk3188_clk_branches,
>  				  ARRAY_SIZE(rk3188_clk_branches));
> -	rockchip_clk_register_armclk(ARMCLK, "armclk",
> +	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
>  				  mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
>  				  &rk3188_cpuclk_data, rk3188_cpuclk_rates,
>  				  ARRAY_SIZE(rk3188_cpuclk_rates));
> @@ -833,6 +854,7 @@ static void __init rk3188a_clk_init(struct device_node
> *np)
> 
>  	rockchip_clk_protect_critical(rk3188_critical_clocks,
>  				      ARRAY_SIZE(rk3188_critical_clocks));
> +	rockchip_clk_of_add_provider(np, ctx);
>  }
>  CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);

[...]

> diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
> index ab50524..54e6b74 100644
> --- a/drivers/clk/rockchip/clk.c
> +++ b/drivers/clk/rockchip/clk.c
> @@ -312,66 +316,94 @@ static struct clk
> *rockchip_clk_register_factor_branch(const char *name, return clk;
>  }
> 
> -static DEFINE_SPINLOCK(clk_lock);
> -static struct clk **clk_table;
> -static void __iomem *reg_base;
> -static struct clk_onecell_data clk_data;
> -static struct device_node *cru_node;
> -static struct regmap *grf;
> -
> -void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
> -			      unsigned long nr_clks)
> +struct rockchip_clk_provider *__init rockchip_clk_init(struct device_node

I've added a space between the asterisk and __init flag


> *np, +			void __iomem *base, unsigned long nr_clks)
>  {
> -	reg_base = base;
> -	cru_node = np;
> -	grf = ERR_PTR(-EPROBE_DEFER);
> +	struct rockchip_clk_provider *ctx;
> +	struct clk **clk_table;
> +	int i;
> +
> +	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
> +	if (!ctx) {
> +		pr_err("%s: Could not allocate clock provider context\n",
> +			__func__);
> +		return ERR_PTR(-ENOMEM);
> +	}
> 
>  	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
> -	if (!clk_table)
> -		pr_err("%s: could not allocate clock lookup table\n", __func__);
> +	if (!clk_table) {
> +		pr_err("%s: Could not allocate clock lookup table\n",
> +			__func__);
> +		goto err_free;
> +	}
> +
> +	for (i = 0; i < nr_clks; ++i)
> +		clk_table[i] = ERR_PTR(-ENOENT);
> 
> -	clk_data.clks = clk_table;
> -	clk_data.clk_num = nr_clks;
> -	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
> +	ctx->reg_base = base;
> +	ctx->clk_data.clks = clk_table;
> +	ctx->clk_data.clk_num = nr_clks;
> +	ctx->cru_node = np;
> +	ctx->grf = ERR_PTR(-EPROBE_DEFER);
> +	spin_lock_init(&ctx->lock);
> +
> +	return ctx;
> +
> +err_free:
> +	kfree(ctx);
> +	return ERR_PTR(-ENOMEM);
> +}
> +
> +void __init rockchip_clk_of_add_provider(struct device_node *np,
> +				struct rockchip_clk_provider *ctx)
> +{
> +	if (np) {
> +		if (of_clk_add_provider(np, of_clk_src_onecell_get,
> +					&ctx->clk_data))
> +			panic("could not register clk provider\n");

I've changed that to a pr_err, again no need to panic on this, as letting
the kernel run may give the affected developer more hints what may be wrong.


> +	}
>  }
> 

> diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
> index 7aafe18..b7affb6 100644
> --- a/drivers/clk/rockchip/clk.h
> +++ b/drivers/clk/rockchip/clk.h
> @@ -127,6 +128,20 @@ enum rockchip_pll_type {
>  	.nb = _nb,						\
>  }
> 
> +/**
> + * struct rockchip_clk_provider: information about clock provider
> + * @reg_base: virtual address for the register base.
> + * @clk_data: holds clock related data like clk* and number of clocks.
> + * @lock: maintains exclusion between callbacks for a given clock-provider.

I've added the missing kerneldoc entries here


> + */
> +struct rockchip_clk_provider {
> +	void __iomem *reg_base;
> +	struct clk_onecell_data clk_data;
> +	struct device_node *cru_node;
> +	struct regmap *grf;
> +	spinlock_t lock;
> +};
> +
>  struct rockchip_pll_rate_table {
>  	unsigned long rate;
>  	unsigned int nr;
zhengxing March 10, 2016, 2:29 a.m. UTC | #2
Hi Heiko,
     Thank you for helping me to optimize these details. :-)

On 2016?03?10? 06:25, Heiko Stübner wrote:
> Hi Xing,
>
> Am Mittwoch, 9. März 2016, 10:37:04 schrieb Xing Zheng:
>> There are need to support Multi-CRUs probability in future, but
>> it is not supported on the current Rockchip Clock Framework.
>>
>> Therefore, this patch add support a provider as the parameter
>> handler when we call the clock register functions for per CRU.
>>
>> Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>
> I've applied that in a clk branch for 4.7 [0] with some changes detailed
> below. If you can, please check that I didn't mess anything up :-)
>
> I've sucessfully booted that on both a rk3036 and rk3288 as well.
>
>
> Heiko
>
> [0] https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?h=v4.7-clk/next&id=d509ddf2e57c99ae760d1a289b85f1e0d729f864
>
>
>> ---
>>
>> Changes in v3: None
>> Changes in v2: None
>>
>>   drivers/clk/rockchip/clk-pll.c    |   30 ++++----
>>   drivers/clk/rockchip/clk-rk3036.c |   17 +++--
>>   drivers/clk/rockchip/clk-rk3188.c |   48 ++++++++----
>>   drivers/clk/rockchip/clk-rk3228.c |   17 +++--
>>   drivers/clk/rockchip/clk-rk3288.c |   19 +++--
>>   drivers/clk/rockchip/clk-rk3368.c |   21 ++++--
>>   drivers/clk/rockchip/clk.c        |  148
>> +++++++++++++++++++++++-------------- drivers/clk/rockchip/clk.h        |
>> 49 ++++++++----
>>   8 files changed, 231 insertions(+), 118 deletions(-)
> [...]
>
>> diff --git a/drivers/clk/rockchip/clk-rk3188.c
>> b/drivers/clk/rockchip/clk-rk3188.c index e832403..7c73c51 100644
>> --- a/drivers/clk/rockchip/clk-rk3188.c
>> @@ -759,57 +759,78 @@ static const char *const rk3188_critical_clocks[]
>> __initconst = { "hclk_cpubus"
>>   };
>>
>> -static void __init rk3188_common_clk_init(struct device_node *np)
>> +static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct
>> device_node *np) {
>> +	struct rockchip_clk_provider *ctx;
>>   	void __iomem *reg_base;
>>
>>   	reg_base = of_iomap(np, 0);
>>   	if (!reg_base) {
>>   		pr_err("%s: could not map cru region\n", __func__);
>> -		return;
>> +		return ERR_PTR(-ENOMEM);
>>   	}
>>
>> -	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
>> +	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
>> +	if (IS_ERR(ctx)) {
>> +		pr_err("%s: rockchip clk init failed\n", __func__);
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>>
>> -	rockchip_clk_register_branches(common_clk_branches,
>> +	rockchip_clk_register_branches(ctx, common_clk_branches,
>>   				  ARRAY_SIZE(common_clk_branches));
>>
>>   	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
>>   				  ROCKCHIP_SOFTRST_HIWORD_MASK);
>>
>> -	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
>> +	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
>> +
>> +	return ctx;
>>   }
>>
>>   static void __init rk3066a_clk_init(struct device_node *np)
>>   {
>> -	rk3188_common_clk_init(np);
>> -	rockchip_clk_register_plls(rk3066_pll_clks,
>> +	struct rockchip_clk_provider *ctx;
>> +
>> +	ctx = rk3188_common_clk_init(np);
>> +	if (IS_ERR(ctx)) {
>> +		pr_err("%s: common clk init failed\n", __func__);
>> +		return;
>> +	}
> I've dropped the pr_err + parentheses, as rk3188_common_clk_init
> will already output a suitable error.
>
>
>> +
>> +	rockchip_clk_register_plls(ctx, rk3066_pll_clks,
>>   				   ARRAY_SIZE(rk3066_pll_clks),
>>   				   RK3066_GRF_SOC_STATUS);
>> -	rockchip_clk_register_branches(rk3066a_clk_branches,
>> +	rockchip_clk_register_branches(ctx, rk3066a_clk_branches,
>>   				  ARRAY_SIZE(rk3066a_clk_branches));
>> -	rockchip_clk_register_armclk(ARMCLK, "armclk",
>> +	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
>>   			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
>>   			&rk3066_cpuclk_data, rk3066_cpuclk_rates,
>>   			ARRAY_SIZE(rk3066_cpuclk_rates));
>>   	rockchip_clk_protect_critical(rk3188_critical_clocks,
>>   				      ARRAY_SIZE(rk3188_critical_clocks));
>> +	rockchip_clk_of_add_provider(np, ctx);
>>   }
>>   CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
>>
>>   static void __init rk3188a_clk_init(struct device_node *np)
>>   {
>> +	struct rockchip_clk_provider *ctx;
>>   	struct clk *clk1, *clk2;
>>   	unsigned long rate;
>>   	int ret;
>>
>> -	rk3188_common_clk_init(np);
>> -	rockchip_clk_register_plls(rk3188_pll_clks,
>> +	ctx = rk3188_common_clk_init(np);
>> +	if (IS_ERR(ctx)) {
>> +		pr_err("%s: common clk init failed\n", __func__);
>> +		return;
>> +	}
> same as above
>
>
>> +
>> +	rockchip_clk_register_plls(ctx, rk3188_pll_clks,
>>   				   ARRAY_SIZE(rk3188_pll_clks),
>>   				   RK3188_GRF_SOC_STATUS);
>> -	rockchip_clk_register_branches(rk3188_clk_branches,
>> +	rockchip_clk_register_branches(ctx, rk3188_clk_branches,
>>   				  ARRAY_SIZE(rk3188_clk_branches));
>> -	rockchip_clk_register_armclk(ARMCLK, "armclk",
>> +	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
>>   				  mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
>>   				  &rk3188_cpuclk_data, rk3188_cpuclk_rates,
>>   				  ARRAY_SIZE(rk3188_cpuclk_rates));
>> @@ -833,6 +854,7 @@ static void __init rk3188a_clk_init(struct device_node
>> *np)
>>
>>   	rockchip_clk_protect_critical(rk3188_critical_clocks,
>>   				      ARRAY_SIZE(rk3188_critical_clocks));
>> +	rockchip_clk_of_add_provider(np, ctx);
>>   }
>>   CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);
> [...]
>
>> diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
>> index ab50524..54e6b74 100644
>> --- a/drivers/clk/rockchip/clk.c
>> +++ b/drivers/clk/rockchip/clk.c
>> @@ -312,66 +316,94 @@ static struct clk
>> *rockchip_clk_register_factor_branch(const char *name, return clk;
>>   }
>>
>> -static DEFINE_SPINLOCK(clk_lock);
>> -static struct clk **clk_table;
>> -static void __iomem *reg_base;
>> -static struct clk_onecell_data clk_data;
>> -static struct device_node *cru_node;
>> -static struct regmap *grf;
>> -
>> -void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
>> -			      unsigned long nr_clks)
>> +struct rockchip_clk_provider *__init rockchip_clk_init(struct device_node
> I've added a space between the asterisk and __init flag
>
>
>> *np, +			void __iomem *base, unsigned long nr_clks)
>>   {
>> -	reg_base = base;
>> -	cru_node = np;
>> -	grf = ERR_PTR(-EPROBE_DEFER);
>> +	struct rockchip_clk_provider *ctx;
>> +	struct clk **clk_table;
>> +	int i;
>> +
>> +	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
>> +	if (!ctx) {
>> +		pr_err("%s: Could not allocate clock provider context\n",
>> +			__func__);
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>>
>>   	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
>> -	if (!clk_table)
>> -		pr_err("%s: could not allocate clock lookup table\n", __func__);
>> +	if (!clk_table) {
>> +		pr_err("%s: Could not allocate clock lookup table\n",
>> +			__func__);
>> +		goto err_free;
>> +	}
>> +
>> +	for (i = 0; i < nr_clks; ++i)
>> +		clk_table[i] = ERR_PTR(-ENOENT);
>>
>> -	clk_data.clks = clk_table;
>> -	clk_data.clk_num = nr_clks;
>> -	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
>> +	ctx->reg_base = base;
>> +	ctx->clk_data.clks = clk_table;
>> +	ctx->clk_data.clk_num = nr_clks;
>> +	ctx->cru_node = np;
>> +	ctx->grf = ERR_PTR(-EPROBE_DEFER);
>> +	spin_lock_init(&ctx->lock);
>> +
>> +	return ctx;
>> +
>> +err_free:
>> +	kfree(ctx);
>> +	return ERR_PTR(-ENOMEM);
>> +}
>> +
>> +void __init rockchip_clk_of_add_provider(struct device_node *np,
>> +				struct rockchip_clk_provider *ctx)
>> +{
>> +	if (np) {
>> +		if (of_clk_add_provider(np, of_clk_src_onecell_get,
>> +					&ctx->clk_data))
>> +			panic("could not register clk provider\n");
> I've changed that to a pr_err, again no need to panic on this, as letting
> the kernel run may give the affected developer more hints what may be wrong.
>
>
>> +	}
>>   }
>>
>> diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
>> index 7aafe18..b7affb6 100644
>> --- a/drivers/clk/rockchip/clk.h
>> +++ b/drivers/clk/rockchip/clk.h
>> @@ -127,6 +128,20 @@ enum rockchip_pll_type {
>>   	.nb = _nb,						\
>>   }
>>
>> +/**
>> + * struct rockchip_clk_provider: information about clock provider
>> + * @reg_base: virtual address for the register base.
>> + * @clk_data: holds clock related data like clk* and number of clocks.
>> + * @lock: maintains exclusion between callbacks for a given clock-provider.
> I've added the missing kerneldoc entries here
>
>
>> + */
>> +struct rockchip_clk_provider {
>> +	void __iomem *reg_base;
>> +	struct clk_onecell_data clk_data;
>> +	struct device_node *cru_node;
>> +	struct regmap *grf;
>> +	spinlock_t lock;
>> +};
>> +
>>   struct rockchip_pll_rate_table {
>>   	unsigned long rate;
>>   	unsigned int nr;
>
>
>
diff mbox

Patch

diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index 5de797e..27be66a 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -46,6 +46,8 @@  struct rockchip_clk_pll {
 	const struct rockchip_pll_rate_table *rate_table;
 	unsigned int		rate_count;
 	spinlock_t		*lock;
+
+	struct rockchip_clk_provider *ctx;
 };
 
 #define to_rockchip_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw)
@@ -90,7 +92,7 @@  static long rockchip_pll_round_rate(struct clk_hw *hw,
  */
 static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll)
 {
-	struct regmap *grf = rockchip_clk_get_grf();
+	struct regmap *grf = rockchip_clk_get_grf(pll->ctx);
 	unsigned int val;
 	int delay = 24000000, ret;
 
@@ -251,7 +253,7 @@  static int rockchip_rk3036_pll_set_rate(struct clk_hw *hw, unsigned long drate,
 	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
 	const struct rockchip_pll_rate_table *rate;
 	unsigned long old_rate = rockchip_rk3036_pll_recalc_rate(hw, prate);
-	struct regmap *grf = rockchip_clk_get_grf();
+	struct regmap *grf = rockchip_clk_get_grf(pll->ctx);
 
 	if (IS_ERR(grf)) {
 		pr_debug("%s: grf regmap not available, aborting rate change\n",
@@ -490,7 +492,7 @@  static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
 	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
 	const struct rockchip_pll_rate_table *rate;
 	unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
-	struct regmap *grf = rockchip_clk_get_grf();
+	struct regmap *grf = rockchip_clk_get_grf(pll->ctx);
 
 	if (IS_ERR(grf)) {
 		pr_debug("%s: grf regmap not available, aborting rate change\n",
@@ -563,7 +565,7 @@  static void rockchip_rk3066_pll_init(struct clk_hw *hw)
 		 rate->no, cur.no, rate->nf, cur.nf, rate->nb, cur.nb);
 	if (rate->nr != cur.nr || rate->no != cur.no || rate->nf != cur.nf
 						     || rate->nb != cur.nb) {
-		struct regmap *grf = rockchip_clk_get_grf();
+		struct regmap *grf = rockchip_clk_get_grf(pll->ctx);
 
 		if (IS_ERR(grf))
 			return;
@@ -595,12 +597,13 @@  static const struct clk_ops rockchip_rk3066_pll_clk_ops = {
  * Common registering of pll clocks
  */
 
-struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
+struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
+		enum rockchip_pll_type pll_type,
 		const char *name, const char *const *parent_names,
-		u8 num_parents, void __iomem *base, int con_offset,
-		int grf_lock_offset, int lock_shift, int mode_offset,
-		int mode_shift, struct rockchip_pll_rate_table *rate_table,
-		u8 clk_pll_flags, spinlock_t *lock)
+		u8 num_parents, int con_offset, int grf_lock_offset,
+		int lock_shift, int mode_offset, int mode_shift,
+		struct rockchip_pll_rate_table *rate_table,
+		u8 clk_pll_flags)
 {
 	const char *pll_parents[3];
 	struct clk_init_data init;
@@ -624,11 +627,11 @@  struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 	/* create the mux on top of the real pll */
 	pll->pll_mux_ops = &clk_mux_ops;
 	pll_mux = &pll->pll_mux;
-	pll_mux->reg = base + mode_offset;
+	pll_mux->reg = ctx->reg_base + mode_offset;
 	pll_mux->shift = mode_shift;
 	pll_mux->mask = PLL_MODE_MASK;
 	pll_mux->flags = 0;
-	pll_mux->lock = lock;
+	pll_mux->lock = &ctx->lock;
 	pll_mux->hw.init = &init;
 
 	if (pll_type == pll_rk3036 || pll_type == pll_rk3066)
@@ -695,11 +698,12 @@  struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 
 	pll->hw.init = &init;
 	pll->type = pll_type;
-	pll->reg_base = base + con_offset;
+	pll->reg_base = ctx->reg_base + con_offset;
 	pll->lock_offset = grf_lock_offset;
 	pll->lock_shift = lock_shift;
 	pll->flags = clk_pll_flags;
-	pll->lock = lock;
+	pll->lock = &ctx->lock;
+	pll->ctx = ctx;
 
 	pll_clk = clk_register(NULL, &pll->hw);
 	if (IS_ERR(pll_clk)) {
diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 1dae248..8c683cc 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -440,6 +440,7 @@  static const char *const rk3036_critical_clocks[] __initconst = {
 
 static void __init rk3036_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -449,22 +450,26 @@  static void __init rk3036_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return;
+	}
 
 	clk = clk_register_fixed_factor(NULL, "usb480m", "xin24m", 0, 20, 1);
 	if (IS_ERR(clk))
 		pr_warn("%s: could not register clock usb480m: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_plls(rk3036_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3036_pll_clks,
 				   ARRAY_SIZE(rk3036_pll_clks),
 				   RK3036_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3036_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3036_clk_branches,
 				  ARRAY_SIZE(rk3036_clk_branches));
 	rockchip_clk_protect_critical(rk3036_critical_clocks,
 				      ARRAY_SIZE(rk3036_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3036_cpuclk_data, rk3036_cpuclk_rates,
 			ARRAY_SIZE(rk3036_cpuclk_rates));
@@ -472,6 +477,8 @@  static void __init rk3036_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3036_cru, "rockchip,rk3036-cru", rk3036_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index e832403..7c73c51 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -759,57 +759,78 @@  static const char *const rk3188_critical_clocks[] __initconst = {
 	"hclk_cpubus"
 };
 
-static void __init rk3188_common_clk_init(struct device_node *np)
+static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 
 	reg_base = of_iomap(np, 0);
 	if (!reg_base) {
 		pr_err("%s: could not map cru region\n", __func__);
-		return;
+		return ERR_PTR(-ENOMEM);
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
 
-	rockchip_clk_register_branches(common_clk_branches,
+	rockchip_clk_register_branches(ctx, common_clk_branches,
 				  ARRAY_SIZE(common_clk_branches));
 
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
+
+	return ctx;
 }
 
 static void __init rk3066a_clk_init(struct device_node *np)
 {
-	rk3188_common_clk_init(np);
-	rockchip_clk_register_plls(rk3066_pll_clks,
+	struct rockchip_clk_provider *ctx;
+
+	ctx = rk3188_common_clk_init(np);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: common clk init failed\n", __func__);
+		return;
+	}
+
+	rockchip_clk_register_plls(ctx, rk3066_pll_clks,
 				   ARRAY_SIZE(rk3066_pll_clks),
 				   RK3066_GRF_SOC_STATUS);
-	rockchip_clk_register_branches(rk3066a_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3066a_clk_branches,
 				  ARRAY_SIZE(rk3066a_clk_branches));
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3066_cpuclk_data, rk3066_cpuclk_rates,
 			ARRAY_SIZE(rk3066_cpuclk_rates));
 	rockchip_clk_protect_critical(rk3188_critical_clocks,
 				      ARRAY_SIZE(rk3188_critical_clocks));
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
 
 static void __init rk3188a_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	struct clk *clk1, *clk2;
 	unsigned long rate;
 	int ret;
 
-	rk3188_common_clk_init(np);
-	rockchip_clk_register_plls(rk3188_pll_clks,
+	ctx = rk3188_common_clk_init(np);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: common clk init failed\n", __func__);
+		return;
+	}
+
+	rockchip_clk_register_plls(ctx, rk3188_pll_clks,
 				   ARRAY_SIZE(rk3188_pll_clks),
 				   RK3188_GRF_SOC_STATUS);
-	rockchip_clk_register_branches(rk3188_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3188_clk_branches,
 				  ARRAY_SIZE(rk3188_clk_branches));
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 				  mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 				  &rk3188_cpuclk_data, rk3188_cpuclk_rates,
 				  ARRAY_SIZE(rk3188_cpuclk_rates));
@@ -833,6 +854,7 @@  static void __init rk3188a_clk_init(struct device_node *np)
 
 	rockchip_clk_protect_critical(rk3188_critical_clocks,
 				      ARRAY_SIZE(rk3188_critical_clocks));
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);
 
diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
index 4b4137e..c112b2f 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -628,6 +628,7 @@  static const char *const rk3228_critical_clocks[] __initconst = {
 
 static void __init rk3228_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 
 	reg_base = of_iomap(np, 0);
@@ -636,17 +637,21 @@  static void __init rk3228_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return;
+	}
 
-	rockchip_clk_register_plls(rk3228_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3228_pll_clks,
 				   ARRAY_SIZE(rk3228_pll_clks),
 				   RK3228_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3228_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3228_clk_branches,
 				  ARRAY_SIZE(rk3228_clk_branches));
 	rockchip_clk_protect_critical(rk3228_critical_clocks,
 				      ARRAY_SIZE(rk3228_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3228_cpuclk_data, rk3228_cpuclk_rates,
 			ARRAY_SIZE(rk3228_cpuclk_rates));
@@ -654,6 +659,8 @@  static void __init rk3228_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3228_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK3228_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3228_cru, "rockchip,rk3228-cru", rk3228_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 00faf3f..d1031d1 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -881,6 +881,7 @@  static struct syscore_ops rk3288_clk_syscore_ops = {
 
 static void __init rk3288_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	struct clk *clk;
 
 	rk3288_cru_base = of_iomap(np, 0);
@@ -889,7 +890,11 @@  static void __init rk3288_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return;
+	}
 
 	/* Watchdog pclk is controlled by RK3288_SGRF_SOC_CON0[1]. */
 	clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1);
@@ -897,17 +902,17 @@  static void __init rk3288_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock pclk_wdt: %ld\n",
 			__func__, PTR_ERR(clk));
 	else
-		rockchip_clk_add_lookup(clk, PCLK_WDT);
+		rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
 
-	rockchip_clk_register_plls(rk3288_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3288_pll_clks,
 				   ARRAY_SIZE(rk3288_pll_clks),
 				   RK3288_GRF_SOC_STATUS1);
-	rockchip_clk_register_branches(rk3288_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3288_clk_branches,
 				  ARRAY_SIZE(rk3288_clk_branches));
 	rockchip_clk_protect_critical(rk3288_critical_clocks,
 				      ARRAY_SIZE(rk3288_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3288_cpuclk_data, rk3288_cpuclk_rates,
 			ARRAY_SIZE(rk3288_cpuclk_rates));
@@ -916,8 +921,10 @@  static void __init rk3288_clk_init(struct device_node *np)
 				  rk3288_cru_base + RK3288_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3288_GLB_SRST_FST,
+	rockchip_register_restart_notifier(ctx, RK3288_GLB_SRST_FST,
 					   rk3288_clk_shutdown);
 	register_syscore_ops(&rk3288_clk_syscore_ops);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index c71f070..58690f2 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -862,6 +862,7 @@  static const char *const rk3368_critical_clocks[] __initconst = {
 
 static void __init rk3368_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -871,7 +872,11 @@  static void __init rk3368_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return;
+	}
 
 	/* Watchdog pclk is controlled by sgrf_soc_con3[7]. */
 	clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1);
@@ -879,22 +884,22 @@  static void __init rk3368_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock pclk_wdt: %ld\n",
 			__func__, PTR_ERR(clk));
 	else
-		rockchip_clk_add_lookup(clk, PCLK_WDT);
+		rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
 
-	rockchip_clk_register_plls(rk3368_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3368_pll_clks,
 				   ARRAY_SIZE(rk3368_pll_clks),
 				   RK3368_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3368_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3368_clk_branches,
 				  ARRAY_SIZE(rk3368_clk_branches));
 	rockchip_clk_protect_critical(rk3368_critical_clocks,
 				      ARRAY_SIZE(rk3368_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLKB, "armclkb",
+	rockchip_clk_register_armclk(ctx, ARMCLKB, "armclkb",
 			mux_armclkb_p, ARRAY_SIZE(mux_armclkb_p),
 			&rk3368_cpuclkb_data, rk3368_cpuclkb_rates,
 			ARRAY_SIZE(rk3368_cpuclkb_rates));
 
-	rockchip_clk_register_armclk(ARMCLKL, "armclkl",
+	rockchip_clk_register_armclk(ctx, ARMCLKL, "armclkl",
 			mux_armclkl_p, ARRAY_SIZE(mux_armclkl_p),
 			&rk3368_cpuclkl_data, rk3368_cpuclkl_rates,
 			ARRAY_SIZE(rk3368_cpuclkl_rates));
@@ -902,6 +907,8 @@  static void __init rk3368_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 15, reg_base + RK3368_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3368_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK3368_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3368_cru, "rockchip,rk3368-cru", rk3368_clk_init);
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index ab50524..54e6b74 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -2,6 +2,9 @@ 
  * Copyright (c) 2014 MundoReader S.L.
  * Author: Heiko Stuebner <heiko@sntech.de>
  *
+ * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
+ * Author: Xing Zheng <zhengxing@rock-chips.com>
+ *
  * based on
  *
  * samsung/clk.c
@@ -155,7 +158,8 @@  static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
 	return notifier_from_errno(ret);
 }
 
-static struct clk *rockchip_clk_register_frac_branch(const char *name,
+static struct clk *rockchip_clk_register_frac_branch(
+		struct rockchip_clk_provider *ctx, const char *name,
 		const char *const *parent_names, u8 num_parents,
 		void __iomem *base, int muxdiv_offset, u8 div_flags,
 		int gate_offset, u8 gate_shift, u8 gate_flags,
@@ -248,7 +252,7 @@  static struct clk *rockchip_clk_register_frac_branch(const char *name,
 		if (IS_ERR(mux_clk))
 			return clk;
 
-		rockchip_clk_add_lookup(mux_clk, child->id);
+		rockchip_clk_add_lookup(ctx, mux_clk, child->id);
 
 		/* notifier on the fraction divider to catch rate changes */
 		if (frac->mux_frac_idx >= 0) {
@@ -312,66 +316,94 @@  static struct clk *rockchip_clk_register_factor_branch(const char *name,
 	return clk;
 }
 
-static DEFINE_SPINLOCK(clk_lock);
-static struct clk **clk_table;
-static void __iomem *reg_base;
-static struct clk_onecell_data clk_data;
-static struct device_node *cru_node;
-static struct regmap *grf;
-
-void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
-			      unsigned long nr_clks)
+struct rockchip_clk_provider *__init rockchip_clk_init(struct device_node *np,
+			void __iomem *base, unsigned long nr_clks)
 {
-	reg_base = base;
-	cru_node = np;
-	grf = ERR_PTR(-EPROBE_DEFER);
+	struct rockchip_clk_provider *ctx;
+	struct clk **clk_table;
+	int i;
+
+	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
+	if (!ctx) {
+		pr_err("%s: Could not allocate clock provider context\n",
+			__func__);
+		return ERR_PTR(-ENOMEM);
+	}
 
 	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
-	if (!clk_table)
-		pr_err("%s: could not allocate clock lookup table\n", __func__);
+	if (!clk_table) {
+		pr_err("%s: Could not allocate clock lookup table\n",
+			__func__);
+		goto err_free;
+	}
+
+	for (i = 0; i < nr_clks; ++i)
+		clk_table[i] = ERR_PTR(-ENOENT);
 
-	clk_data.clks = clk_table;
-	clk_data.clk_num = nr_clks;
-	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+	ctx->reg_base = base;
+	ctx->clk_data.clks = clk_table;
+	ctx->clk_data.clk_num = nr_clks;
+	ctx->cru_node = np;
+	ctx->grf = ERR_PTR(-EPROBE_DEFER);
+	spin_lock_init(&ctx->lock);
+
+	return ctx;
+
+err_free:
+	kfree(ctx);
+	return ERR_PTR(-ENOMEM);
+}
+
+void __init rockchip_clk_of_add_provider(struct device_node *np,
+				struct rockchip_clk_provider *ctx)
+{
+	if (np) {
+		if (of_clk_add_provider(np, of_clk_src_onecell_get,
+					&ctx->clk_data))
+			panic("could not register clk provider\n");
+	}
 }
 
-struct regmap *rockchip_clk_get_grf(void)
+struct regmap *rockchip_clk_get_grf(struct rockchip_clk_provider *ctx)
 {
-	if (IS_ERR(grf))
-		grf = syscon_regmap_lookup_by_phandle(cru_node, "rockchip,grf");
-	return grf;
+	if (IS_ERR(ctx->grf))
+		ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node, "rockchip,grf");
+	return ctx->grf;
 }
 
-void rockchip_clk_add_lookup(struct clk *clk, unsigned int id)
+void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
+			     struct clk *clk, unsigned int id)
 {
-	if (clk_table && id)
-		clk_table[id] = clk;
+	if (ctx->clk_data.clks && id)
+		ctx->clk_data.clks[id] = clk;
 }
 
-void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list,
+void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
+				struct rockchip_pll_clock *list,
 				unsigned int nr_pll, int grf_lock_offset)
 {
 	struct clk *clk;
 	int idx;
 
 	for (idx = 0; idx < nr_pll; idx++, list++) {
-		clk = rockchip_clk_register_pll(list->type, list->name,
+		clk = rockchip_clk_register_pll(ctx, list->type, list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->con_offset, grf_lock_offset,
+				list->con_offset, grf_lock_offset,
 				list->lock_shift, list->mode_offset,
 				list->mode_shift, list->rate_table,
-				list->pll_flags, &clk_lock);
+				list->pll_flags);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
 				list->name);
 			continue;
 		}
 
-		rockchip_clk_add_lookup(clk, list->id);
+		rockchip_clk_add_lookup(ctx, clk, list->id);
 	}
 }
 
 void __init rockchip_clk_register_branches(
+				      struct rockchip_clk_provider *ctx,
 				      struct rockchip_clk_branch *list,
 				      unsigned int nr_clk)
 {
@@ -387,56 +419,56 @@  void __init rockchip_clk_register_branches(
 		case branch_mux:
 			clk = clk_register_mux(NULL, list->name,
 				list->parent_names, list->num_parents,
-				flags, reg_base + list->muxdiv_offset,
+				flags, ctx->reg_base + list->muxdiv_offset,
 				list->mux_shift, list->mux_width,
-				list->mux_flags, &clk_lock);
+				list->mux_flags, &ctx->lock);
 			break;
 		case branch_divider:
 			if (list->div_table)
 				clk = clk_register_divider_table(NULL,
 					list->name, list->parent_names[0],
-					flags, reg_base + list->muxdiv_offset,
+					flags, ctx->reg_base + list->muxdiv_offset,
 					list->div_shift, list->div_width,
 					list->div_flags, list->div_table,
-					&clk_lock);
+					&ctx->lock);
 			else
 				clk = clk_register_divider(NULL, list->name,
 					list->parent_names[0], flags,
-					reg_base + list->muxdiv_offset,
+					ctx->reg_base + list->muxdiv_offset,
 					list->div_shift, list->div_width,
-					list->div_flags, &clk_lock);
+					list->div_flags, &ctx->lock);
 			break;
 		case branch_fraction_divider:
-			clk = rockchip_clk_register_frac_branch(list->name,
+			clk = rockchip_clk_register_frac_branch(ctx, list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->muxdiv_offset, list->div_flags,
+				ctx->reg_base, list->muxdiv_offset, list->div_flags,
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, list->child,
-				&clk_lock);
+				&ctx->lock);
 			break;
 		case branch_gate:
 			flags |= CLK_SET_RATE_PARENT;
 
 			clk = clk_register_gate(NULL, list->name,
 				list->parent_names[0], flags,
-				reg_base + list->gate_offset,
-				list->gate_shift, list->gate_flags, &clk_lock);
+				ctx->reg_base + list->gate_offset,
+				list->gate_shift, list->gate_flags, &ctx->lock);
 			break;
 		case branch_composite:
 			clk = rockchip_clk_register_branch(list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->muxdiv_offset, list->mux_shift,
+				ctx->reg_base, list->muxdiv_offset, list->mux_shift,
 				list->mux_width, list->mux_flags,
 				list->div_shift, list->div_width,
 				list->div_flags, list->div_table,
 				list->gate_offset, list->gate_shift,
-				list->gate_flags, flags, &clk_lock);
+				list->gate_flags, flags, &ctx->lock);
 			break;
 		case branch_mmc:
 			clk = rockchip_clk_register_mmc(
 				list->name,
 				list->parent_names, list->num_parents,
-				reg_base + list->muxdiv_offset,
+				ctx->reg_base + list->muxdiv_offset,
 				list->div_shift
 			);
 			break;
@@ -444,16 +476,16 @@  void __init rockchip_clk_register_branches(
 			clk = rockchip_clk_register_inverter(
 				list->name, list->parent_names,
 				list->num_parents,
-				reg_base + list->muxdiv_offset,
-				list->div_shift, list->div_flags, &clk_lock);
+				ctx->reg_base + list->muxdiv_offset,
+				list->div_shift, list->div_flags, &ctx->lock);
 			break;
 		case branch_factor:
 			clk = rockchip_clk_register_factor_branch(
 				list->name, list->parent_names,
-				list->num_parents, reg_base,
+				list->num_parents, ctx->reg_base,
 				list->div_shift, list->div_width,
 				list->gate_offset, list->gate_shift,
-				list->gate_flags, flags, &clk_lock);
+				list->gate_flags, flags, &ctx->lock);
 			break;
 		}
 
@@ -470,11 +502,12 @@  void __init rockchip_clk_register_branches(
 			continue;
 		}
 
-		rockchip_clk_add_lookup(clk, list->id);
+		rockchip_clk_add_lookup(ctx, clk, list->id);
 	}
 }
 
-void __init rockchip_clk_register_armclk(unsigned int lookup_id,
+void __init rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
+			unsigned int lookup_id,
 			const char *name, const char *const *parent_names,
 			u8 num_parents,
 			const struct rockchip_cpuclk_reg_data *reg_data,
@@ -484,15 +517,15 @@  void __init rockchip_clk_register_armclk(unsigned int lookup_id,
 	struct clk *clk;
 
 	clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
-					   reg_data, rates, nrates, reg_base,
-					   &clk_lock);
+					   reg_data, rates, nrates, ctx->reg_base,
+					   &ctx->lock);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register clock %s: %ld\n",
 		       __func__, name, PTR_ERR(clk));
 		return;
 	}
 
-	rockchip_clk_add_lookup(clk, lookup_id);
+	rockchip_clk_add_lookup(ctx, clk, lookup_id);
 }
 
 void __init rockchip_clk_protect_critical(const char *const clocks[],
@@ -509,6 +542,7 @@  void __init rockchip_clk_protect_critical(const char *const clocks[],
 	}
 }
 
+static void __iomem *rst_base;
 static unsigned int reg_restart;
 static void (*cb_restart)(void);
 static int rockchip_restart_notify(struct notifier_block *this,
@@ -517,7 +551,7 @@  static int rockchip_restart_notify(struct notifier_block *this,
 	if (cb_restart)
 		cb_restart();
 
-	writel(0xfdb9, reg_base + reg_restart);
+	writel(0xfdb9, rst_base + reg_restart);
 	return NOTIFY_DONE;
 }
 
@@ -526,10 +560,12 @@  static struct notifier_block rockchip_restart_handler = {
 	.priority = 128,
 };
 
-void __init rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void))
+void __init rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
+					       unsigned int reg, void (*cb)(void))
 {
 	int ret;
 
+	rst_base = ctx->reg_base;
 	reg_restart = reg;
 	cb_restart = cb;
 	ret = register_restart_handler(&rockchip_restart_handler);
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 7aafe18..b7affb6 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -27,6 +27,7 @@ 
 #define CLK_ROCKCHIP_CLK_H
 
 #include <linux/io.h>
+#include <linux/clk-provider.h>
 
 struct clk;
 
@@ -127,6 +128,20 @@  enum rockchip_pll_type {
 	.nb = _nb,						\
 }
 
+/**
+ * struct rockchip_clk_provider: information about clock provider
+ * @reg_base: virtual address for the register base.
+ * @clk_data: holds clock related data like clk* and number of clocks.
+ * @lock: maintains exclusion between callbacks for a given clock-provider.
+ */
+struct rockchip_clk_provider {
+	void __iomem *reg_base;
+	struct clk_onecell_data clk_data;
+	struct device_node *cru_node;
+	struct regmap *grf;
+	spinlock_t lock;
+};
+
 struct rockchip_pll_rate_table {
 	unsigned long rate;
 	unsigned int nr;
@@ -194,12 +209,13 @@  struct rockchip_pll_clock {
 		.rate_table	= _rtable,				\
 	}
 
-struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
+struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
+		enum rockchip_pll_type pll_type,
 		const char *name, const char *const *parent_names,
-		u8 num_parents, void __iomem *base, int con_offset,
-		int grf_lock_offset, int lock_shift, int reg_mode,
-		int mode_shift, struct rockchip_pll_rate_table *rate_table,
-		u8 clk_pll_flags, spinlock_t *lock);
+		u8 num_parents, int con_offset, int grf_lock_offset,
+		int lock_shift, int mode_offset, int mode_shift,
+		struct rockchip_pll_rate_table *rate_table,
+		u8 clk_pll_flags);
 
 struct rockchip_cpuclk_clksel {
 	int reg;
@@ -542,21 +558,28 @@  struct rockchip_clk_branch {
 		.gate_flags	= gf,				\
 	}
 
-void rockchip_clk_init(struct device_node *np, void __iomem *base,
-		       unsigned long nr_clks);
-struct regmap *rockchip_clk_get_grf(void);
-void rockchip_clk_add_lookup(struct clk *clk, unsigned int id);
-void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
+struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
+			void __iomem *base, unsigned long nr_clks);
+void rockchip_clk_of_add_provider(struct device_node *np,
+				struct rockchip_clk_provider *ctx);
+struct regmap *rockchip_clk_get_grf(struct rockchip_clk_provider *ctx);
+void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
+			     struct clk *clk, unsigned int id);
+void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
+				    struct rockchip_clk_branch *list,
 				    unsigned int nr_clk);
-void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
+void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
+				struct rockchip_pll_clock *pll_list,
 				unsigned int nr_pll, int grf_lock_offset);
-void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name,
+void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
+			unsigned int lookup_id, const char *name,
 			const char *const *parent_names, u8 num_parents,
 			const struct rockchip_cpuclk_reg_data *reg_data,
 			const struct rockchip_cpuclk_rate_table *rates,
 			int nrates);
 void rockchip_clk_protect_critical(const char *const clocks[], int nclocks);
-void rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void));
+void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
+					unsigned int reg, void (*cb)(void));
 
 #define ROCKCHIP_SOFTRST_HIWORD_MASK	BIT(0)