From 168fbf79db2aa2b8f123fc3a060f38789e113aa2 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Fri, 28 Jan 2022 13:41:48 +0530 Subject: [PATCH] phy: cadence: Sierra: Add support for PHY multilink configurations Add support for multilink configuration of Sierra PHY. Currently, maximum two links are supported. Signed-off-by: Swapnil Jakhade Signed-off-by: Aswath Govindraju --- 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 54d316d4ec..5641c56728 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -29,7 +29,7 @@ #include #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 >> (3 - (offset))))) #define SIERRA_PHY_ISO_LINK_CTRL 0xB +/* PHY PMA lane registers */ +#define SIERRA_PHY_PMA_LANE_CDB_OFFSET(ln, offset) \ + (0xF000 + ((ln) * (0x800 >> (3 - (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.39.5