diff mbox series

[v2,03/22] clk: rockchip: rk3399: Improve support for SCLK_PCIEPHY_REF clock

Message ID 20240501162308.875193-4-jonas@kwiboo.se
State Accepted
Commit e801d05bea6d99b2731f3c96edc754b36f75bcdc
Delegated to: Kever Yang
Headers show
Series rockchip: rk3399: Sync DT with v6.8 and update defconfigs | expand

Commit Message

Jonas Karlman May 1, 2024, 4:22 p.m. UTC
rk3399-nanopi-4.dtsi try to set parent of and set rate to 100 MHz of the
SCLK_PCIEPHY_REF clock.

The existing enable/disable ops for SCLK_PCIEPHY_REF currently force
use of 24 MHz parent and rate.

Add improved support for setting parent and rate of the pciephy refclk
to driver to better support assign-clock props for pciephy refclk in DT.

This limited implementation only support setting 24 or 100 MHz rate,
and expect npll and clk_pciephy_ref100m divider to use default values.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
v2: Implement partial instead of dummy support for SCLK_PCIEPHY_REF
---
 drivers/clk/rockchip/clk_rk3399.c | 59 +++++++++++++++++++++++++++++--
 1 file changed, 57 insertions(+), 2 deletions(-)

Comments

Kever Yang May 6, 2024, 10:32 a.m. UTC | #1
On 2024/5/2 00:22, Jonas Karlman wrote:
> rk3399-nanopi-4.dtsi try to set parent of and set rate to 100 MHz of the
> SCLK_PCIEPHY_REF clock.
>
> The existing enable/disable ops for SCLK_PCIEPHY_REF currently force
> use of 24 MHz parent and rate.
>
> Add improved support for setting parent and rate of the pciephy refclk
> to driver to better support assign-clock props for pciephy refclk in DT.
>
> This limited implementation only support setting 24 or 100 MHz rate,
> and expect npll and clk_pciephy_ref100m divider to use default values.
>
> Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Kever Yang <kever.yang@rock-chips.com>

Thanks,
- Kever
> ---
> v2: Implement partial instead of dummy support for SCLK_PCIEPHY_REF
> ---
>   drivers/clk/rockchip/clk_rk3399.c | 59 +++++++++++++++++++++++++++++--
>   1 file changed, 57 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
> index 5934771b4096..0b3223611a32 100644
> --- a/drivers/clk/rockchip/clk_rk3399.c
> +++ b/drivers/clk/rockchip/clk_rk3399.c
> @@ -926,6 +926,26 @@ static ulong rk3399_saradc_set_clk(struct rockchip_cru *cru, uint hz)
>   	return rk3399_saradc_get_clk(cru);
>   }
>   
> +static ulong rk3399_pciephy_get_clk(struct rockchip_cru *cru)
> +{
> +	if (readl(&cru->clksel_con[18]) & BIT(10))
> +		return 100 * MHz;
> +	else
> +		return OSC_HZ;
> +}
> +
> +static ulong rk3399_pciephy_set_clk(struct rockchip_cru *cru, uint hz)
> +{
> +	if (hz == 100 * MHz)
> +		rk_setreg(&cru->clksel_con[18], BIT(10));
> +	else if (hz == OSC_HZ)
> +		rk_clrreg(&cru->clksel_con[18], BIT(10));
> +	else
> +		return -EINVAL;
> +
> +	return rk3399_pciephy_get_clk(cru);
> +}
> +
>   static ulong rk3399_clk_get_rate(struct clk *clk)
>   {
>   	struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
> @@ -967,6 +987,9 @@ static ulong rk3399_clk_get_rate(struct clk *clk)
>   	case SCLK_SARADC:
>   		rate = rk3399_saradc_get_clk(priv->cru);
>   		break;
> +	case SCLK_PCIEPHY_REF:
> +		rate = rk3399_pciephy_get_clk(priv->cru);
> +		break;
>   	case ACLK_VIO:
>   	case ACLK_HDCP:
>   	case ACLK_GIC_PRE:
> @@ -1058,6 +1081,9 @@ static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate)
>   	case SCLK_SARADC:
>   		ret = rk3399_saradc_set_clk(priv->cru, rate);
>   		break;
> +	case SCLK_PCIEPHY_REF:
> +		ret = rk3399_pciephy_set_clk(priv->cru, rate);
> +		break;
>   	case ACLK_VIO:
>   	case ACLK_HDCP:
>   	case ACLK_GIC_PRE:
> @@ -1108,12 +1134,39 @@ static int __maybe_unused rk3399_gmac_set_parent(struct clk *clk,
>   	return -EINVAL;
>   }
>   
> +static int __maybe_unused rk3399_pciephy_set_parent(struct clk *clk,
> +						    struct clk *parent)
> +{
> +	struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
> +	const char *clock_output_name;
> +	int ret;
> +
> +	if (parent->dev == clk->dev && parent->id == SCLK_PCIEPHY_REF100M) {
> +		rk_setreg(&priv->cru->clksel_con[18], BIT(10));
> +		return 0;
> +	}
> +
> +	ret = dev_read_string_index(parent->dev, "clock-output-names",
> +				    parent->id, &clock_output_name);
> +	if (ret < 0)
> +		return -ENODATA;
> +
> +	if (!strcmp(clock_output_name, "xin24m")) {
> +		rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
>   static int __maybe_unused rk3399_clk_set_parent(struct clk *clk,
>   						struct clk *parent)
>   {
>   	switch (clk->id) {
>   	case SCLK_RMII_SRC:
>   		return rk3399_gmac_set_parent(clk, parent);
> +	case SCLK_PCIEPHY_REF:
> +		return rk3399_pciephy_set_parent(clk, parent);
>   	}
>   
>   	debug("%s: unsupported clk %ld\n", __func__, clk->id);
> @@ -1204,7 +1257,8 @@ static int rk3399_clk_enable(struct clk *clk)
>   		rk_clrreg(&priv->cru->clkgate_con[13], BIT(7));
>   		break;
>   	case SCLK_PCIEPHY_REF:
> -		rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
> +		if (readl(&priv->cru->clksel_con[18]) & BIT(10))
> +			rk_clrreg(&priv->cru->clkgate_con[12], BIT(6));
>   		break;
>   	default:
>   		debug("%s: unsupported clk %ld\n", __func__, clk->id);
> @@ -1298,7 +1352,8 @@ static int rk3399_clk_disable(struct clk *clk)
>   		rk_setreg(&priv->cru->clkgate_con[13], BIT(7));
>   		break;
>   	case SCLK_PCIEPHY_REF:
> -		rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
> +		if (readl(&priv->cru->clksel_con[18]) & BIT(10))
> +			rk_setreg(&priv->cru->clkgate_con[12], BIT(6));
>   		break;
>   	default:
>   		debug("%s: unsupported clk %ld\n", __func__, clk->id);
Quentin Schulz May 6, 2024, 11:07 a.m. UTC | #2
Hi Jonas,

On 5/1/24 6:22 PM, Jonas Karlman wrote:
> rk3399-nanopi-4.dtsi try to set parent of and set rate to 100 MHz of the
> SCLK_PCIEPHY_REF clock.
> 
> The existing enable/disable ops for SCLK_PCIEPHY_REF currently force
> use of 24 MHz parent and rate.
> 
> Add improved support for setting parent and rate of the pciephy refclk
> to driver to better support assign-clock props for pciephy refclk in DT.
> 
> This limited implementation only support setting 24 or 100 MHz rate,
> and expect npll and clk_pciephy_ref100m divider to use default values.
> 
> Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
> ---
> v2: Implement partial instead of dummy support for SCLK_PCIEPHY_REF
> ---
>   drivers/clk/rockchip/clk_rk3399.c | 59 +++++++++++++++++++++++++++++--
>   1 file changed, 57 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
> index 5934771b4096..0b3223611a32 100644
> --- a/drivers/clk/rockchip/clk_rk3399.c
> +++ b/drivers/clk/rockchip/clk_rk3399.c
> @@ -926,6 +926,26 @@ static ulong rk3399_saradc_set_clk(struct rockchip_cru *cru, uint hz)
>   	return rk3399_saradc_get_clk(cru);
>   }
>   
> +static ulong rk3399_pciephy_get_clk(struct rockchip_cru *cru)
> +{
> +	if (readl(&cru->clksel_con[18]) & BIT(10))
> +		return 100 * MHz;
> +	else
> +		return OSC_HZ;

Could avoid the else since all if blocks return, no other logic than the 
one matching the else can reach that part of the code.

Therefore:

"""
if (readl(&cru->clksel_con[18]) & BIT(10))
     return 100 * MHz;

return OSC_HZ;
"""

works just as well.

Could also be

"""
return (readl(&cru->clksel_con[18]) & BIT(10)) ? 100 * MHz : OSC_HZ;
"""

[...]
> +static int __maybe_unused rk3399_pciephy_set_parent(struct clk *clk,
> +						    struct clk *parent)
> +{
> +	struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
> +	const char *clock_output_name;
> +	int ret;
> +
> +	if (parent->dev == clk->dev && parent->id == SCLK_PCIEPHY_REF100M) {
> +		rk_setreg(&priv->cru->clksel_con[18], BIT(10));
> +		return 0;
> +	}
> +
> +	ret = dev_read_string_index(parent->dev, "clock-output-names",
> +				    parent->id, &clock_output_name);

Are you sure this works?

Considering that parent->id seems to store unique ids, like 167 for 
SCLK_PCIEPHY_REF100M, I doubt we should be using it for 
dev_read_string_index????

As per documentation:

"""
/**
  * dev_read_string_index() - obtain an indexed string from a string list
  *
  * @dev: device to examine
  * @propname: name of the property containing the string list
  * @index: index of the string to return
  * @outp: return location for the string
  *
  * Return:
  *   length of string, if found or -ve error value if not found
  */
int dev_read_string_index(const struct udevice *dev, const char *propname,
			  int index, const char **outp);
"""

So index here means the (index+1)'th string in the list of strings under 
the "propname" DT property, I doubt we have properties with 167+ strings 
in them?

I realize that rk3399_gmac_set_parent also uses this, so I'm a bit 
puzzled right now...

Don't you want to use

dev_read_stringlist_search(parent->dev, "clock-output-names", "xin24m")

instead?

The rest looks good to me.

Cheers,
Quentin
Jonas Karlman May 6, 2024, 3:17 p.m. UTC | #3
Hi Quentin,

On 2024-05-06 13:07, Quentin Schulz wrote:
> Hi Jonas,
> 
> On 5/1/24 6:22 PM, Jonas Karlman wrote:
>> rk3399-nanopi-4.dtsi try to set parent of and set rate to 100 MHz of the
>> SCLK_PCIEPHY_REF clock.
>>
>> The existing enable/disable ops for SCLK_PCIEPHY_REF currently force
>> use of 24 MHz parent and rate.
>>
>> Add improved support for setting parent and rate of the pciephy refclk
>> to driver to better support assign-clock props for pciephy refclk in DT.
>>
>> This limited implementation only support setting 24 or 100 MHz rate,
>> and expect npll and clk_pciephy_ref100m divider to use default values.
>>
>> Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
>> ---
>> v2: Implement partial instead of dummy support for SCLK_PCIEPHY_REF
>> ---
>>   drivers/clk/rockchip/clk_rk3399.c | 59 +++++++++++++++++++++++++++++--
>>   1 file changed, 57 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
>> index 5934771b4096..0b3223611a32 100644
>> --- a/drivers/clk/rockchip/clk_rk3399.c
>> +++ b/drivers/clk/rockchip/clk_rk3399.c
>> @@ -926,6 +926,26 @@ static ulong rk3399_saradc_set_clk(struct rockchip_cru *cru, uint hz)
>>   	return rk3399_saradc_get_clk(cru);
>>   }
>>   
>> +static ulong rk3399_pciephy_get_clk(struct rockchip_cru *cru)
>> +{
>> +	if (readl(&cru->clksel_con[18]) & BIT(10))
>> +		return 100 * MHz;
>> +	else
>> +		return OSC_HZ;
> 
> Could avoid the else since all if blocks return, no other logic than the 
> one matching the else can reach that part of the code.
> 
> Therefore:
> 
> """
> if (readl(&cru->clksel_con[18]) & BIT(10))
>      return 100 * MHz;
> 
> return OSC_HZ;
> """
> 
> works just as well.

I was considering above format at first, but chose to align return
statements for some reason.

I can change to this format in a v3, if needed :-)

> 
> Could also be
> 
> """
> return (readl(&cru->clksel_con[18]) & BIT(10)) ? 100 * MHz : OSC_HZ;
> """
> 
> [...]
>> +static int __maybe_unused rk3399_pciephy_set_parent(struct clk *clk,
>> +						    struct clk *parent)
>> +{
>> +	struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
>> +	const char *clock_output_name;
>> +	int ret;
>> +
>> +	if (parent->dev == clk->dev && parent->id == SCLK_PCIEPHY_REF100M) {
>> +		rk_setreg(&priv->cru->clksel_con[18], BIT(10));
>> +		return 0;
>> +	}
>> +
>> +	ret = dev_read_string_index(parent->dev, "clock-output-names",
>> +				    parent->id, &clock_output_name);
> 
> Are you sure this works?

It should work for the clk reference I tested, <&xin24m>, where the id
will be 0, it should also work for e.g. <&rk809 1>, id will be 1.

And if a &cru/&pmucru clk is referenced those nodes do not have the
clock-output-names prop, so ret should be -EINVAL (or -ENODATA).

> 
> Considering that parent->id seems to store unique ids, like 167 for 
> SCLK_PCIEPHY_REF100M, I doubt we should be using it for 
> dev_read_string_index????
> 
> As per documentation:
> 
> """
> /**
>   * dev_read_string_index() - obtain an indexed string from a string list
>   *
>   * @dev: device to examine
>   * @propname: name of the property containing the string list
>   * @index: index of the string to return
>   * @outp: return location for the string
>   *
>   * Return:
>   *   length of string, if found or -ve error value if not found
>   */
> int dev_read_string_index(const struct udevice *dev, const char *propname,
> 			  int index, const char **outp);
> """
> 
> So index here means the (index+1)'th string in the list of strings under 
> the "propname" DT property, I doubt we have properties with 167+ strings 
> in them?

I would expect the function to return -EINVAL or -ENODATA if it is called
with an out-of-bounds index value.

> 
> I realize that rk3399_gmac_set_parent also uses this, so I'm a bit 
> puzzled right now...
> 
> Don't you want to use
> 
> dev_read_stringlist_search(parent->dev, "clock-output-names", "xin24m")
> 
> instead?

I think the implementation is correct and is what other rk clk drivers
do for gmac set parent ops.

Using dev_read_stringlist_search() could possible match a name for
"wrong" clock, e.g:

clk_ref: clock {
	#clock-cells = <1>;
	clock-output-names = "xin32k", "xin24m";
};

here only <&clk_ref 1> should match and not <&clk_ref 0>.

Regards,
Jonas

> 
> The rest looks good to me.
> 
> Cheers,
> Quentin
Quentin Schulz May 6, 2024, 3:52 p.m. UTC | #4
Hi Jonas,

On 5/6/24 5:17 PM, Jonas Karlman wrote:
> Hi Quentin,
> 
> On 2024-05-06 13:07, Quentin Schulz wrote:
>> Hi Jonas,
>>
>> On 5/1/24 6:22 PM, Jonas Karlman wrote:
>>> rk3399-nanopi-4.dtsi try to set parent of and set rate to 100 MHz of the
>>> SCLK_PCIEPHY_REF clock.
>>>
>>> The existing enable/disable ops for SCLK_PCIEPHY_REF currently force
>>> use of 24 MHz parent and rate.
>>>
>>> Add improved support for setting parent and rate of the pciephy refclk
>>> to driver to better support assign-clock props for pciephy refclk in DT.
>>>
>>> This limited implementation only support setting 24 or 100 MHz rate,
>>> and expect npll and clk_pciephy_ref100m divider to use default values.
>>>
>>> Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
>>> ---
>>> v2: Implement partial instead of dummy support for SCLK_PCIEPHY_REF
>>> ---
>>>    drivers/clk/rockchip/clk_rk3399.c | 59 +++++++++++++++++++++++++++++--
>>>    1 file changed, 57 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
>>> index 5934771b4096..0b3223611a32 100644
>>> --- a/drivers/clk/rockchip/clk_rk3399.c
>>> +++ b/drivers/clk/rockchip/clk_rk3399.c
>>> @@ -926,6 +926,26 @@ static ulong rk3399_saradc_set_clk(struct rockchip_cru *cru, uint hz)
>>>    	return rk3399_saradc_get_clk(cru);
>>>    }
>>>    
>>> +static ulong rk3399_pciephy_get_clk(struct rockchip_cru *cru)
>>> +{
>>> +	if (readl(&cru->clksel_con[18]) & BIT(10))
>>> +		return 100 * MHz;
>>> +	else
>>> +		return OSC_HZ;
>>
>> Could avoid the else since all if blocks return, no other logic than the
>> one matching the else can reach that part of the code.
>>
>> Therefore:
>>
>> """
>> if (readl(&cru->clksel_con[18]) & BIT(10))
>>       return 100 * MHz;
>>
>> return OSC_HZ;
>> """
>>
>> works just as well.
> 
> I was considering above format at first, but chose to align return
> statements for some reason.
> 
> I can change to this format in a v3, if needed :-)
> 

Just a matter of taste :)

>>
>> Could also be
>>
>> """
>> return (readl(&cru->clksel_con[18]) & BIT(10)) ? 100 * MHz : OSC_HZ;
>> """
>>
>> [...]
>>> +static int __maybe_unused rk3399_pciephy_set_parent(struct clk *clk,
>>> +						    struct clk *parent)
>>> +{
>>> +	struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
>>> +	const char *clock_output_name;
>>> +	int ret;
>>> +
>>> +	if (parent->dev == clk->dev && parent->id == SCLK_PCIEPHY_REF100M) {
>>> +		rk_setreg(&priv->cru->clksel_con[18], BIT(10));
>>> +		return 0;
>>> +	}
>>> +
>>> +	ret = dev_read_string_index(parent->dev, "clock-output-names",
>>> +				    parent->id, &clock_output_name);
>>
>> Are you sure this works?
> 
> It should work for the clk reference I tested, <&xin24m>, where the id
> will be 0, it should also work for e.g. <&rk809 1>, id will be 1.
> 
> And if a &cru/&pmucru clk is referenced those nodes do not have the
> clock-output-names prop, so ret should be -EINVAL (or -ENODATA).
> 
>>
>> Considering that parent->id seems to store unique ids, like 167 for
>> SCLK_PCIEPHY_REF100M, I doubt we should be using it for
>> dev_read_string_index????
>>
>> As per documentation:
>>
>> """
>> /**
>>    * dev_read_string_index() - obtain an indexed string from a string list
>>    *
>>    * @dev: device to examine
>>    * @propname: name of the property containing the string list
>>    * @index: index of the string to return
>>    * @outp: return location for the string
>>    *
>>    * Return:
>>    *   length of string, if found or -ve error value if not found
>>    */
>> int dev_read_string_index(const struct udevice *dev, const char *propname,
>> 			  int index, const char **outp);
>> """
>>
>> So index here means the (index+1)'th string in the list of strings under
>> the "propname" DT property, I doubt we have properties with 167+ strings
>> in them?
> 
> I would expect the function to return -EINVAL or -ENODATA if it is called
> with an out-of-bounds index value.
> 

-ENODATA it seems. -EINVAL if the property doesn't exist.

>>
>> I realize that rk3399_gmac_set_parent also uses this, so I'm a bit
>> puzzled right now...
>>
>> Don't you want to use
>>
>> dev_read_stringlist_search(parent->dev, "clock-output-names", "xin24m")
>>
>> instead?
> 
> I think the implementation is correct and is what other rk clk drivers
> do for gmac set parent ops.
> 
> Using dev_read_stringlist_search() could possible match a name for
> "wrong" clock, e.g:
> 
> clk_ref: clock {
> 	#clock-cells = <1>;
> 	clock-output-names = "xin32k", "xin24m";
> };
> 
> here only <&clk_ref 1> should match and not <&clk_ref 0>.
> 

Ah well, one more brain fart :) I somehow mixed things up and was 
thinking about the values in clocks/assigned-clocks in one node needing 
to match the offset in clock-names of that same node, which made no 
sense. But we're talking about different things here :)

I guess it's just a matter of taste between the implementation in your 
patch and the inverted logic which tries to find xin24m in 
clock-output-names and checks its index matches parent->id. Consistency 
is preferred, and the former is already used in other places, so let's 
keep this.

Thanks for taking the time to explain.

This does seem reasonable to me, so:

Reviewed-by: Quentin Schulz <quentin.schulz@cherry.de>

Thanks!
Quentin
diff mbox series

Patch

diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index 5934771b4096..0b3223611a32 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -926,6 +926,26 @@  static ulong rk3399_saradc_set_clk(struct rockchip_cru *cru, uint hz)
 	return rk3399_saradc_get_clk(cru);
 }
 
+static ulong rk3399_pciephy_get_clk(struct rockchip_cru *cru)
+{
+	if (readl(&cru->clksel_con[18]) & BIT(10))
+		return 100 * MHz;
+	else
+		return OSC_HZ;
+}
+
+static ulong rk3399_pciephy_set_clk(struct rockchip_cru *cru, uint hz)
+{
+	if (hz == 100 * MHz)
+		rk_setreg(&cru->clksel_con[18], BIT(10));
+	else if (hz == OSC_HZ)
+		rk_clrreg(&cru->clksel_con[18], BIT(10));
+	else
+		return -EINVAL;
+
+	return rk3399_pciephy_get_clk(cru);
+}
+
 static ulong rk3399_clk_get_rate(struct clk *clk)
 {
 	struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
@@ -967,6 +987,9 @@  static ulong rk3399_clk_get_rate(struct clk *clk)
 	case SCLK_SARADC:
 		rate = rk3399_saradc_get_clk(priv->cru);
 		break;
+	case SCLK_PCIEPHY_REF:
+		rate = rk3399_pciephy_get_clk(priv->cru);
+		break;
 	case ACLK_VIO:
 	case ACLK_HDCP:
 	case ACLK_GIC_PRE:
@@ -1058,6 +1081,9 @@  static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate)
 	case SCLK_SARADC:
 		ret = rk3399_saradc_set_clk(priv->cru, rate);
 		break;
+	case SCLK_PCIEPHY_REF:
+		ret = rk3399_pciephy_set_clk(priv->cru, rate);
+		break;
 	case ACLK_VIO:
 	case ACLK_HDCP:
 	case ACLK_GIC_PRE:
@@ -1108,12 +1134,39 @@  static int __maybe_unused rk3399_gmac_set_parent(struct clk *clk,
 	return -EINVAL;
 }
 
+static int __maybe_unused rk3399_pciephy_set_parent(struct clk *clk,
+						    struct clk *parent)
+{
+	struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
+	const char *clock_output_name;
+	int ret;
+
+	if (parent->dev == clk->dev && parent->id == SCLK_PCIEPHY_REF100M) {
+		rk_setreg(&priv->cru->clksel_con[18], BIT(10));
+		return 0;
+	}
+
+	ret = dev_read_string_index(parent->dev, "clock-output-names",
+				    parent->id, &clock_output_name);
+	if (ret < 0)
+		return -ENODATA;
+
+	if (!strcmp(clock_output_name, "xin24m")) {
+		rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
 static int __maybe_unused rk3399_clk_set_parent(struct clk *clk,
 						struct clk *parent)
 {
 	switch (clk->id) {
 	case SCLK_RMII_SRC:
 		return rk3399_gmac_set_parent(clk, parent);
+	case SCLK_PCIEPHY_REF:
+		return rk3399_pciephy_set_parent(clk, parent);
 	}
 
 	debug("%s: unsupported clk %ld\n", __func__, clk->id);
@@ -1204,7 +1257,8 @@  static int rk3399_clk_enable(struct clk *clk)
 		rk_clrreg(&priv->cru->clkgate_con[13], BIT(7));
 		break;
 	case SCLK_PCIEPHY_REF:
-		rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
+		if (readl(&priv->cru->clksel_con[18]) & BIT(10))
+			rk_clrreg(&priv->cru->clkgate_con[12], BIT(6));
 		break;
 	default:
 		debug("%s: unsupported clk %ld\n", __func__, clk->id);
@@ -1298,7 +1352,8 @@  static int rk3399_clk_disable(struct clk *clk)
 		rk_setreg(&priv->cru->clkgate_con[13], BIT(7));
 		break;
 	case SCLK_PCIEPHY_REF:
-		rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
+		if (readl(&priv->cru->clksel_con[18]) & BIT(10))
+			rk_setreg(&priv->cru->clkgate_con[12], BIT(6));
 		break;
 	default:
 		debug("%s: unsupported clk %ld\n", __func__, clk->id);