[PATCH v4 3/6] phy: qcom-qmp-pcie: support separate tables for EP mode

Johan Hovold johan at kernel.org
Mon Sep 26 00:48:00 PDT 2022


On Sat, Sep 24, 2022 at 07:02:59PM +0300, Dmitry Baryshkov wrote:
> The PCIe QMP PHY requires different programming sequences when being
> used for the RC (Root Complex) or for the EP (End Point) modes. Allow
> selecting the submode and thus selecting a set of PHY programming
> tables.
> 
> Since the RC and EP modes share common some common init sequence, the
> common sequence is kept in the main table and the sequence differences
> are pushed to the extra tables.
> 
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov at linaro.org>
> ---
>  drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 67 ++++++++++++++++++++----
>  1 file changed, 58 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> index 6e8c74585670..1fc23df59454 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> @@ -1320,10 +1320,14 @@ struct qmp_phy_cfg {
>  	/* Main init sequence for PHY blocks - serdes, tx, rx, pcs */
>  	struct qmp_phy_cfg_tables common;
>  	/*
> -	 * Additional init sequence for PHY blocks, providing additional
> -	 * register programming. Unless required it can be left omitted.
> +	 * Additional init sequences for PHY blocks, providing additional
> +	 * register programming. They are used for providing separate sequences
> +	 * for the Root Complex and for the End Point usecases.
> +	 *
> +	 * If EP mode is not supported, both tables can be left empty.
>  	 */
> -	struct qmp_phy_cfg_tables *extra;
> +	struct qmp_phy_cfg_tables *extra_rc; /* for the RC only */
> +	struct qmp_phy_cfg_tables *extra_ep; /* for the EP only */

So with the additional "_rc" and "_ep" suffixes this would be more
obvious as for example:

	struct qmp_phy_cfg_tables tbls_common;
	struct qmp_phy_cfg_tables tbls_rc;
	struct qmp_phy_cfg_tables tbls_ep;

The "for the RC only" comments doesn't really add anything.

>  
>  	/* clock ids to be requested */
>  	const char * const *clk_list;
> @@ -1367,6 +1371,7 @@ struct qmp_phy_cfg {
>   * @pcs_misc: iomapped memory space for lane's pcs_misc
>   * @pipe_clk: pipe clock
>   * @qmp: QMP phy to which this lane belongs
> + * @extra: currently selected PHY extra init table set

"extra" is not a descriptive name here either.

>   */
>  struct qmp_phy {
>  	struct phy *phy;
> @@ -1379,6 +1384,7 @@ struct qmp_phy {
>  	void __iomem *rx2;
>  	void __iomem *pcs_misc;
>  	struct clk *pipe_clk;
> +	const struct qmp_phy_cfg_tables *extra;
>  	struct qcom_qmp *qmp;
>  };
>  
> @@ -1624,7 +1630,15 @@ static const struct qmp_phy_cfg sm8250_qmp_gen3x1_pciephy_cfg = {
>  	.pcs_misc_tbl		= sm8250_qmp_pcie_pcs_misc_tbl,
>  	.pcs_misc_tbl_num	= ARRAY_SIZE(sm8250_qmp_pcie_pcs_misc_tbl),
>  	},
> -	.extra = &(struct qmp_phy_cfg_tables) {
> +	/*
> +	 * For sm8250 the split between the primary and extra_rc tables is
> +	 * historical, it reflects the programming sequence common to all PCIe
> +	 * PHYs on this platform and a sequence required for this particular
> +	 * PHY type. If EP support for sm8250 is required, the
> +	 * primary/extra_rc split is to be reconsidered and adjusted
> +	 * according to EP programming sequence.
> +	 */

Not sure this is needed either. I'm currently using the "_sec" tables
(future "extra") for sc8280xp as well to handle minor variations in init
sequences between the different PHY types.

> +	.extra_rc = &(struct qmp_phy_cfg_tables) {
>  	.serdes_tbl		= sm8250_qmp_gen3x1_pcie_serdes_tbl,
>  	.serdes_tbl_num		= ARRAY_SIZE(sm8250_qmp_gen3x1_pcie_serdes_tbl),
>  	.rx_tbl			= sm8250_qmp_gen3x1_pcie_rx_tbl,

> @@ -2000,8 +2022,12 @@ static int qmp_pcie_power_on(struct phy *phy)
>  	unsigned int mask, val, ready;
>  	int ret;
>  
> +	/* Default to RC mode if the mode was not selected using phy_set_mode_ext() */
> +	if (!qphy->extra)
> +		qphy->extra = cfg->extra_rc;

It would probably be better to just store the submode in
phy_set_mode_ext() and pick the right table here.

That way you don't need to describe what's really going on in a comment
as it would be apparent from the code.

> +
>  	qmp_pcie_serdes_init(qphy, &cfg->common);
> -	qmp_pcie_serdes_init(qphy, cfg->extra);
> +	qmp_pcie_serdes_init(qphy, qphy->extra);
>  
>  	ret = clk_prepare_enable(qphy->pipe_clk);
>  	if (ret) {
 
> +static int qmp_pcie_set_mode(struct phy *phy,

Do you really need a line break?

> +				 enum phy_mode mode, int submode)
> +{
> +	struct qmp_phy *qphy = phy_get_drvdata(phy);
> +
> +	switch (submode) {
> +	case PHY_MODE_PCIE_RC:
> +		qphy->extra = qphy->cfg->extra_rc;
> +		break;
> +	case PHY_MODE_PCIE_EP:
> +		qphy->extra = qphy->cfg->extra_ep;
> +		break;
> +	default:
> +		dev_err(&phy->dev, "Unuspported submode %d\n", submode);

Typo: unsupported

> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int qmp_pcie_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
>  {
>  	struct qcom_qmp *qmp = dev_get_drvdata(dev);
> @@ -2224,6 +2270,7 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
>  static const struct phy_ops qmp_pcie_ops = {
>  	.power_on	= qmp_pcie_enable,
>  	.power_off	= qmp_pcie_disable,
> +	.set_mode	= qmp_pcie_set_mode,
>  	.owner		= THIS_MODULE,
>  };
>  
> @@ -2278,7 +2325,9 @@ static int qmp_pcie_create(struct device *dev, struct device_node *np, int id,
>  		qphy->pcs_misc = qphy->pcs + 0x400;
>  
>  	if (IS_ERR(qphy->pcs_misc)) {
> -		if (cfg->common.pcs_misc_tbl || cfg->extra->pcs_misc_tbl)
> +		if (cfg->common.pcs_misc_tbl ||
> +		    cfg->extra_rc->pcs_misc_tbl ||
> +		    cfg->extra_ep->pcs_misc_tbl)

I already pointed out the NULL deref here.

>  			return PTR_ERR(qphy->pcs_misc);
>  	}

Johan



More information about the linux-phy mailing list