[RFC PATCH 3/6] phy: rockcip-pcie: reconstruct driver to support per-lane PHYs

Shawn Lin shawn.lin at rock-chips.com
Thu Jul 13 23:33:26 PDT 2017


Hi Brian,

On 2017/7/14 13:10, Brian Norris wrote:
> On Fri, Jul 14, 2017 at 11:52:43AM +0800, Shawn Lin wrote:
>> This patch reconstructs the whole driver to support per-lane
>> PHYs. Note that we could also support the legacy PHY if you
>> don't provide argument to our of_xlate.
>>
>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>> ---
>>
>>   drivers/phy/rockchip/phy-rockchip-pcie.c | 116 +++++++++++++++++++++++++++----
>>   1 file changed, 101 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/phy/rockchip/phy-rockchip-pcie.c b/drivers/phy/rockchip/phy-rockchip-pcie.c
>> index 6904633..da74b47 100644
>> --- a/drivers/phy/rockchip/phy-rockchip-pcie.c
>> +++ b/drivers/phy/rockchip/phy-rockchip-pcie.c
>> @@ -73,10 +73,35 @@ struct rockchip_pcie_data {
>>   struct rockchip_pcie_phy {
>>   	struct rockchip_pcie_data *phy_data;
>>   	struct regmap *reg_base;
>> +	struct phy **phys;
>>   	struct reset_control *phy_rst;
>>   	struct clk *clk_pciephy_ref;
>> +	u32 pwr_cnt;
>> +	bool initialized;
>>   };
>>   
>> +static struct phy *rockchip_pcie_phy_of_xlate(struct device *dev,
>> +					      struct of_phandle_args *args)
>> +{
>> +	struct rockchip_pcie_phy *rk_phy = dev_get_drvdata(dev);
>> +
>> +	if (!rk_phy)
>> +		return ERR_PTR(-ENODEV);
> 
> Shouldn't you just check args->args_count to determine legacy vs. new
> style?
> 

args_count is 1 for legacy mode but could also means you just add one
phy with the new per-lane mode?

>> +
>> +	switch (args->args[0]) {
>> +	case 1:
>> +		return rk_phy->phys[1];
>> +	case 2:
>> +		return rk_phy->phys[2];
>> +	case 3:
>> +		return rk_phy->phys[3];
>> +	case 0:
>> +		/* keep backward compatibility to legacy phy */
>> +	default:
> 
> This also ends up accepting invalid indeces. You should probably
> bounds-check args->args[0].
> 
> Then this can just be:
> 
> 	if (legacy)
> 		return rk_phy->phys[0];
> 	else
> 		return rk_phy->phys[index];


However, checking args_count to see if it's legacy seems to simply
the code a lot. So I would fix that above.

> 
>> +		return rk_phy->phys[0];
>> +	}
>> +}
>> +
>>   static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy,
>>   			      u32 addr, u32 data)
>>   {
>> @@ -114,20 +139,55 @@ static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy,
>>   	return val;
>>   }
>>   
>> -static int rockchip_pcie_phy_power_off(struct phy *phy)
>> +static int rockchip_pcie_phy_common_power_off(struct phy *phy)
>>   {
>>   	struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
>>   	int err = 0;
>>   
>> +	if (WARN_ON(!rk_phy->pwr_cnt))
>> +		return -EINVAL;
>> +
>> +	if (rk_phy->pwr_cnt > 0)
> 
> This should be:
> 
> 	if (--rk_phy->pwr_cnt)
> 
> Also, you technically might need locking, now that multiple phys (which
> each only have their own independent mutex) are accessing the same
> refcount. Or maybe just make this an atomic variable.

Good catch!

> 
>> +		return 0;
>> +
>>   	err = reset_control_assert(rk_phy->phy_rst);
>>   	if (err) {
>>   		dev_err(&phy->dev, "assert phy_rst err %d\n", err);
>>   		return err;
>>   	}
>>   
>> +	rk_phy->pwr_cnt--;
> 
> You've got things backwards... how do you expect to ever decrement this,
> if you return earlier in the function? The effect is that you never
> power off after you've powered on. (You should try instrumenting and
> testing this better.)

Right, I should notice this if I checked the power for S3 but
unfortunately I didn't..

> 
>> +
>>   	return 0;
>>   }
>>   
>> +#define DECLARE_PHY_POWER_OFF_PER_LANE(id) \
>> +static int rockchip_pcie_lane##id##_phy_power_off(struct phy *phy) \
> 
> What? All this macro magic (and duplicate generated functions) should
> not be necessary. You just need some per-phy data that keeps the index.

I can't quite follow yours here. The only argument passing on to
the PHY APIs is 'struct phy *phy', and how could you trace the index
from it? The caller should save phy instead of 'rockchip_pcie_phy', in 
which the per-phy data should be.

Or could you kindly show me some example here:)


> 
>> +{ \
>> +	struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); \
>> +\
>> +	regmap_write(rk_phy->reg_base, \
>> +		     rk_phy->phy_data->pcie_laneoff, \
>> +		     HIWORD_UPDATE(PHY_LANE_IDLE_OFF, \
>> +				   PHY_LANE_IDLE_MASK, \
>> +				   PHY_LANE_IDLE_A_SHIFT + id)); \
>> +	return rockchip_pcie_phy_common_power_off(phy); \
>> +}
>> +
>> +DECLARE_PHY_POWER_OFF_PER_LANE(0);
>> +DECLARE_PHY_POWER_OFF_PER_LANE(1);
>> +DECLARE_PHY_POWER_OFF_PER_LANE(2);
>> +DECLARE_PHY_POWER_OFF_PER_LANE(3);
>> +
>> +#define PROVIDE_PHY_OPS(id) \
>> +	{ \
>> +		.init = rockchip_pcie_phy_init, \
>> +		.exit = rockchip_pcie_phy_exit, \
>> +		.power_on = rockchip_pcie_phy_power_on, \
>> +		.power_off = rockchip_pcie_lane##id##_phy_power_off, \
>> +		.owner  = THIS_MODULE, \
>> +}
>> +
>>   static int rockchip_pcie_phy_power_on(struct phy *phy)
>>   {
>>   	struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
>> @@ -135,6 +195,12 @@ static int rockchip_pcie_phy_power_on(struct phy *phy)
>>   	u32 status;
>>   	unsigned long timeout;
>>   
>> +	if (WARN_ON(rk_phy->pwr_cnt > PHY_MAX_LANE_NUM))
>> +		return -EINVAL;
>> +
>> +	if (rk_phy->pwr_cnt)
> 
> This could just be:
> 
> 	if (rk_phy->pwr_cnt++)
> 
>> +		return 0;
>> +
>>   	err = reset_control_deassert(rk_phy->phy_rst);
>>   	if (err) {
>>   		dev_err(&phy->dev, "deassert phy_rst err %d\n", err);
>> @@ -214,6 +280,7 @@ static int rockchip_pcie_phy_power_on(struct phy *phy)
>>   		goto err_pll_lock;
>>   	}
>>   
>> +	rk_phy->pwr_cnt++;
> 
> Similar problem to what you're doing in power_off(); you're not doing
> the refcount right.

Will fix as well.

> 
> Brian
> 
>>   	return 0;
>>   
>>   err_pll_lock:
>> @@ -226,6 +293,9 @@ static int rockchip_pcie_phy_init(struct phy *phy)
>>   	struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
>>   	int err = 0;
>>   
>> +	if (rk_phy->initialized)
>> +		return 0;
>> +
>>   	err = clk_prepare_enable(rk_phy->clk_pciephy_ref);
>>   	if (err) {
>>   		dev_err(&phy->dev, "Fail to enable pcie ref clock.\n");
>> @@ -238,7 +308,9 @@ static int rockchip_pcie_phy_init(struct phy *phy)
>>   		goto err_reset;
>>   	}
>>   
>> -	return err;
>> +	rk_phy->initialized = true;
>> +
>> +	return 0;
>>   
>>   err_reset:
>>   	clk_disable_unprepare(rk_phy->clk_pciephy_ref);
>> @@ -250,17 +322,21 @@ static int rockchip_pcie_phy_exit(struct phy *phy)
>>   {
>>   	struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
>>   
>> +	if (!rk_phy->initialized)
>> +		return 0;
>> +
>>   	clk_disable_unprepare(rk_phy->clk_pciephy_ref);
>>   
>> +	rk_phy->initialized = false;
>> +
>>   	return 0;
>>   }
>>   
>> -static const struct phy_ops ops = {
>> -	.init		= rockchip_pcie_phy_init,
>> -	.exit		= rockchip_pcie_phy_exit,
>> -	.power_on	= rockchip_pcie_phy_power_on,
>> -	.power_off	= rockchip_pcie_phy_power_off,
>> -	.owner		= THIS_MODULE,
>> +static const struct phy_ops ops[PHY_MAX_LANE_NUM] = {
>> +	PROVIDE_PHY_OPS(0),
>> +	PROVIDE_PHY_OPS(1),
>> +	PROVIDE_PHY_OPS(2),
>> +	PROVIDE_PHY_OPS(3),
>>   };
>>   
>>   static const struct rockchip_pcie_data rk3399_pcie_data = {
>> @@ -283,10 +359,10 @@ static int rockchip_pcie_phy_probe(struct platform_device *pdev)
>>   {
>>   	struct device *dev = &pdev->dev;
>>   	struct rockchip_pcie_phy *rk_phy;
>> -	struct phy *generic_phy;
>>   	struct phy_provider *phy_provider;
>>   	struct regmap *grf;
>>   	const struct of_device_id *of_id;
>> +	int i;
>>   
>>   	grf = syscon_node_to_regmap(dev->parent->of_node);
>>   	if (IS_ERR(grf)) {
>> @@ -319,14 +395,24 @@ static int rockchip_pcie_phy_probe(struct platform_device *pdev)
>>   		return PTR_ERR(rk_phy->clk_pciephy_ref);
>>   	}
>>   
>> -	generic_phy = devm_phy_create(dev, dev->of_node, &ops);
>> -	if (IS_ERR(generic_phy)) {
>> -		dev_err(dev, "failed to create PHY\n");
>> -		return PTR_ERR(generic_phy);
>> +	rk_phy->phys = devm_kcalloc(dev, sizeof(struct phy),
>> +				    PHY_MAX_LANE_NUM, GFP_KERNEL);
>> +	if (!rk_phy->phys)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < PHY_MAX_LANE_NUM; i++) {
>> +		rk_phy->phys[i] = devm_phy_create(dev, dev->of_node, &ops[i]);
>> +		if (IS_ERR(rk_phy->phys[i])) {
>> +			dev_err(dev, "failed to create PHY%d\n", i);
>> +			return PTR_ERR(rk_phy->phys[i]);
>> +		}
>> +
>> +		phy_set_drvdata(rk_phy->phys[i], rk_phy);
>>   	}
>>   
>> -	phy_set_drvdata(generic_phy, rk_phy);
>> -	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
>> +	platform_set_drvdata(pdev, rk_phy);
>> +	phy_provider = devm_of_phy_provider_register(dev,
>> +					rockchip_pcie_phy_of_xlate);
>>   
>>   	return PTR_ERR_OR_ZERO(phy_provider);
>>   }
>> -- 
>> 1.9.1
>>
>>
> 
> 
> 




More information about the Linux-rockchip mailing list