diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index ae0d7b49dfa3..ba01338d93ac 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1320,10 +1321,14 @@ struct qmp_phy_cfg { /* Main init sequence for PHY blocks - serdes, tx, rx, pcs */ const struct qmp_phy_cfg_tables tables; /* - * 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 End Point use cases. + * + * If EP mode is not supported, both tables can be left unset. */ const struct qmp_phy_cfg_tables *tables_rc; + const struct qmp_phy_cfg_tables *tables_ep; /* clock ids to be requested */ const char * const *clk_list; @@ -1367,6 +1372,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 + * @mode: currently selected PHY mode */ struct qmp_phy { struct phy *phy; @@ -1380,6 +1386,7 @@ struct qmp_phy { void __iomem *pcs_misc; struct clk *pipe_clk; struct qcom_qmp *qmp; + int mode; }; /** @@ -1991,13 +1998,19 @@ static int qmp_pcie_power_on(struct phy *phy) struct qmp_phy *qphy = phy_get_drvdata(phy); struct qcom_qmp *qmp = qphy->qmp; const struct qmp_phy_cfg *cfg = qphy->cfg; + const struct qmp_phy_cfg_tables *mode_tables; void __iomem *pcs = qphy->pcs; void __iomem *status; unsigned int mask, val, ready; int ret; + if (qphy->mode == PHY_MODE_PCIE_RC) + mode_tables = cfg->tables_rc; + else + mode_tables = cfg->tables_ep; + qmp_pcie_serdes_init(qphy, &cfg->tables); - qmp_pcie_serdes_init(qphy, cfg->tables_rc); + qmp_pcie_serdes_init(qphy, mode_tables); ret = clk_prepare_enable(qphy->pipe_clk); if (ret) { @@ -2007,10 +2020,10 @@ static int qmp_pcie_power_on(struct phy *phy) /* Tx, Rx, and PCS configurations */ qmp_pcie_lanes_init(qphy, &cfg->tables); - qmp_pcie_lanes_init(qphy, cfg->tables_rc); + qmp_pcie_lanes_init(qphy, mode_tables); qmp_pcie_pcs_init(qphy, &cfg->tables); - qmp_pcie_pcs_init(qphy, cfg->tables_rc); + qmp_pcie_pcs_init(qphy, mode_tables); /* * Pull out PHY from POWER DOWN state. @@ -2097,6 +2110,23 @@ static int qmp_pcie_disable(struct phy *phy) return qmp_pcie_exit(phy); } +static int qmp_pcie_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + + switch (submode) { + case PHY_MODE_PCIE_RC: + case PHY_MODE_PCIE_EP: + qphy->mode = submode; + break; + default: + dev_err(&phy->dev, "Unsupported submode %d\n", submode); + 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); @@ -2220,6 +2250,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, }; @@ -2235,6 +2266,8 @@ static int qmp_pcie_create(struct device *dev, struct device_node *np, int id, if (!qphy) return -ENOMEM; + qphy->mode = PHY_MODE_PCIE_RC; + qphy->cfg = cfg; qphy->serdes = serdes; /* @@ -2278,7 +2311,8 @@ static int qmp_pcie_create(struct device *dev, struct device_node *np, int id, if (IS_ERR(qphy->pcs_misc)) { if (cfg->tables.pcs_misc || - (cfg->tables_rc && cfg->tables_rc->pcs_misc)) + (cfg->tables_rc && cfg->tables_rc->pcs_misc) || + (cfg->tables_ep && cfg->tables_ep->pcs_misc)) return PTR_ERR(qphy->pcs_misc); }