From: Swapnil Jakhade <sjakh...@cadence.com>

Add support for multilink configuration of Sierra PHY. Currently,
maximum two links are supported.

Signed-off-by: Swapnil Jakhade <sjakh...@cadence.com>
Signed-off-by: Aswath Govindraju <a-govindr...@ti.com>
---
 drivers/phy/cadence/phy-cadence-sierra.c | 153 +++++++++++++++++++++--
 1 file changed, 145 insertions(+), 8 deletions(-)

diff --git a/drivers/phy/cadence/phy-cadence-sierra.c 
b/drivers/phy/cadence/phy-cadence-sierra.c
index 323cfe470bd5..43a0f6537ab6 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -29,7 +29,7 @@
 #include <regmap.h>
 
 #define NUM_SSC_MODE           3
-#define NUM_PHY_TYPE           3
+#define NUM_PHY_TYPE           4
 
 /* PHY register offsets */
 #define SIERRA_COMMON_CDB_OFFSET                       0x0
@@ -183,6 +183,11 @@
                                        (0xD000 + ((ln) * (0x800 >> (2 - 
(offset)))))
 #define SIERRA_PHY_ISO_LINK_CTRL                       0xB
 
+/* PHY PMA lane registers */
+#define SIERRA_PHY_PMA_LANE_CDB_OFFSET(ln, offset)     \
+                                     (0xF000 + ((ln) * (0x800 >> (2 - 
(offset)))))
+#define SIERRA_PHY_PMA_XCVR_CTRL                       0x000
+
 #define SIERRA_MACRO_ID                                        0x00007364
 #define SIERRA_MAX_LANES                               16
 #define PLL_LOCK_TIME                                  100
@@ -288,6 +293,8 @@ struct cdns_sierra_data {
                u8 reg_offset_shift;
                struct cdns_sierra_vals 
*pcs_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
                                                     [NUM_SSC_MODE];
+               struct cdns_sierra_vals 
*phy_pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
+                                                       [NUM_SSC_MODE];
                struct cdns_sierra_vals 
*pma_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
                                                     [NUM_SSC_MODE];
                struct cdns_sierra_vals *pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
@@ -306,6 +313,7 @@ struct cdns_sierra_phy {
        struct regmap *regmap_phy_pcs_common_cdb;
        struct regmap *regmap_phy_pcs_lane_cdb[SIERRA_MAX_LANES];
        struct regmap *regmap_phy_pma_common_cdb;
+       struct regmap *regmap_phy_pma_lane_cdb[SIERRA_MAX_LANES];
        struct regmap *regmap_common_cdb;
        struct regmap_field *macro_id_type;
        struct regmap_field *phy_pll_cfg_1;
@@ -361,6 +369,7 @@ static int cdns_sierra_phy_init(struct phy *gphy)
        struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals;
        enum cdns_sierra_phy_type phy_type = ins->phy_type;
        enum cdns_sierra_ssc_mode ssc = ins->ssc_mode;
+       struct cdns_sierra_vals *phy_pma_ln_vals;
        const struct cdns_reg_pairs *reg_pairs;
        struct cdns_sierra_vals *pcs_cmn_vals;
        struct regmap *regmap = phy->regmap;
@@ -368,7 +377,7 @@ static int cdns_sierra_phy_init(struct phy *gphy)
        int i, j;
 
        /* Initialise the PHY registers, unless auto configured */
-       if (phy->autoconf)
+       if (phy->autoconf || phy->nsubnodes > 1)
                return 0;
 
        clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000);
@@ -384,6 +393,18 @@ static int cdns_sierra_phy_init(struct phy *gphy)
                        regmap_write(regmap, reg_pairs[i].off, 
reg_pairs[i].val);
        }
 
+       /* PHY PMA lane registers configurations */
+       phy_pma_ln_vals = init_data->phy_pma_ln_vals[phy_type][TYPE_NONE][ssc];
+       if (phy_pma_ln_vals) {
+               reg_pairs = phy_pma_ln_vals->reg_pairs;
+               num_regs = phy_pma_ln_vals->num_regs;
+               for (i = 0; i < ins->num_lanes; i++) {
+                       regmap = phy->regmap_phy_pma_lane_cdb[i + ins->mlane];
+                       for (j = 0; j < num_regs; j++)
+                               regmap_write(regmap, reg_pairs[j].off, 
reg_pairs[j].val);
+               }
+       }
+
        /* PMA common registers configurations */
        pma_cmn_vals = init_data->pma_cmn_vals[phy_type][TYPE_NONE][ssc];
        if (pma_cmn_vals) {
@@ -417,10 +438,13 @@ static int cdns_sierra_phy_on(struct phy *gphy)
        u32 val;
        int ret;
 
-       ret = reset_control_deassert(sp->phy_rst);
-       if (ret) {
-               dev_err(dev, "Failed to take the PHY out of reset\n");
-               return ret;
+       if (sp->nsubnodes == 1) {
+               /* Take the PHY out of reset */
+               ret = reset_control_deassert(sp->phy_rst);
+               if (ret) {
+                       dev_err(dev, "Failed to take the PHY out of reset\n");
+                       return ret;
+               }
        }
 
        /* Take the PHY lane group out of reset */
@@ -776,6 +800,116 @@ static int cdns_regmap_init_blocks(struct cdns_sierra_phy 
*sp,
        }
        sp->regmap_phy_pma_common_cdb = regmap;
 
+       for (i = 0; i < SIERRA_MAX_LANES; i++) {
+               block_offset = SIERRA_PHY_PMA_LANE_CDB_OFFSET(i, 
reg_offset_shift);
+               regmap = cdns_regmap_init(dev, base, block_offset,
+                                         block_offset_shift, reg_offset_shift);
+               if (IS_ERR(regmap)) {
+                       dev_err(dev, "Failed to init PHY PMA lane CDB 
regmap\n");
+                       return PTR_ERR(regmap);
+               }
+               sp->regmap_phy_pma_lane_cdb[i] = regmap;
+       }
+
+       return 0;
+}
+
+static int cdns_sierra_phy_configure_multilink(struct cdns_sierra_phy *sp)
+{
+       const struct cdns_sierra_data *init_data = sp->init_data;
+       enum cdns_sierra_phy_type phy_t1, phy_t2, tmp_phy_type;
+       struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals;
+       struct cdns_sierra_vals *phy_pma_ln_vals;
+       const struct cdns_reg_pairs *reg_pairs;
+       struct cdns_sierra_vals *pcs_cmn_vals;
+       int i, j, node, mlane, num_lanes, ret;
+       enum cdns_sierra_ssc_mode ssc;
+       struct regmap *regmap;
+       u32 num_regs;
+
+       /* Maximum 2 links (subnodes) are supported */
+       if (sp->nsubnodes != 2)
+               return -EINVAL;
+
+       clk_set_rate(sp->input_clks[CMN_REFCLK_DIG_DIV], 25000000);
+       clk_set_rate(sp->input_clks[CMN_REFCLK1_DIG_DIV], 25000000);
+
+       /* PHY configured to use both PLL LC and LC1 */
+       regmap_field_write(sp->phy_pll_cfg_1, 0x1);
+
+       phy_t1 = sp->phys[0]->phy_type;
+       phy_t2 = sp->phys[1]->phy_type;
+
+       /*
+        * First configure the PHY for first link with phy_t1. Get the array
+        * values as [phy_t1][phy_t2][ssc].
+        */
+       for (node = 0; node < sp->nsubnodes; node++) {
+               if (node == 1) {
+                       /*
+                        * If first link with phy_t1 is configured, then
+                        * configure the PHY for second link with phy_t2.
+                        * Get the array values as [phy_t2][phy_t1][ssc].
+                        */
+                       tmp_phy_type = phy_t1;
+                       phy_t1 = phy_t2;
+                       phy_t2 = tmp_phy_type;
+               }
+
+               mlane = sp->phys[node]->mlane;
+               ssc = sp->phys[node]->ssc_mode;
+               num_lanes = sp->phys[node]->num_lanes;
+
+               /* PHY PCS common registers configurations */
+               pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc];
+               if (pcs_cmn_vals) {
+                       reg_pairs = pcs_cmn_vals->reg_pairs;
+                       num_regs = pcs_cmn_vals->num_regs;
+                       regmap = sp->regmap_phy_pcs_common_cdb;
+                       for (i = 0; i < num_regs; i++)
+                               regmap_write(regmap, reg_pairs[i].off, 
reg_pairs[i].val);
+               }
+
+               /* PHY PMA lane registers configurations */
+               phy_pma_ln_vals = 
init_data->phy_pma_ln_vals[phy_t1][phy_t2][ssc];
+               if (phy_pma_ln_vals) {
+                       reg_pairs = phy_pma_ln_vals->reg_pairs;
+                       num_regs = phy_pma_ln_vals->num_regs;
+                       for (i = 0; i < num_lanes; i++) {
+                               regmap = sp->regmap_phy_pma_lane_cdb[i + mlane];
+                               for (j = 0; j < num_regs; j++)
+                                       regmap_write(regmap, reg_pairs[j].off, 
reg_pairs[j].val);
+                       }
+               }
+
+               /* PMA common registers configurations */
+               pma_cmn_vals = init_data->pma_cmn_vals[phy_t1][phy_t2][ssc];
+               if (pma_cmn_vals) {
+                       reg_pairs = pma_cmn_vals->reg_pairs;
+                       num_regs = pma_cmn_vals->num_regs;
+                       regmap = sp->regmap_common_cdb;
+                       for (i = 0; i < num_regs; i++)
+                               regmap_write(regmap, reg_pairs[i].off, 
reg_pairs[i].val);
+               }
+
+               /* PMA TX lane registers configurations */
+               pma_ln_vals = init_data->pma_ln_vals[phy_t1][phy_t2][ssc];
+               if (pma_ln_vals) {
+                       reg_pairs = pma_ln_vals->reg_pairs;
+                       num_regs = pma_ln_vals->num_regs;
+                       for (i = 0; i < num_lanes; i++) {
+                               regmap = sp->regmap_lane_cdb[i + mlane];
+                               for (j = 0; j < num_regs; j++)
+                                       regmap_write(regmap, reg_pairs[j].off, 
reg_pairs[j].val);
+                       }
+               }
+       }
+
+       /* Take the PHY out of reset */
+       ret = reset_control_deassert(sp->phy_rst);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
@@ -896,8 +1030,11 @@ static int cdns_sierra_link_probe(struct udevice *dev)
        sp->num_lanes += inst->num_lanes;
 
        /* If more than one subnode, configure the PHY as multilink */
-       if (!sp->autoconf && sp->nsubnodes > 1)
-               regmap_field_write(sp->phy_pll_cfg_1, 0x1);
+       if (!sp->autoconf && sp->nsubnodes > 1) {
+               ret = cdns_sierra_phy_configure_multilink(sp);
+               if (ret)
+                       return ret;
+       }
 
        return 0;
 }
-- 
2.17.1

Reply via email to