phy: cadence-torrent: add support for three or more links using 2 protocols
authorSiddharth Vadapalli <s-vadapalli@ti.com>
Mon, 5 Aug 2024 09:26:07 +0000 (14:56 +0530)
committerVinod Koul <vkoul@kernel.org>
Mon, 5 Aug 2024 15:58:50 +0000 (21:28 +0530)
The Torrent SERDES can support at most two different protocols (PHY types).
This only mandates that the device-tree sub-nodes used to represent the
configuration should describe links with at-most two different protocols.

The existing implementation however imposes an artificial constraint that
allows only two links (device-tree sub-nodes). As long as at-most two
protocols are chosen, using more than two links to describe them in an
alternating configuration is still a valid configuration of the Torrent
SERDES.

A 3-Link 2-Protocol configuration of the 4-Lane SERDES can be:
Lane 0 => Protocol 1 => Link 1
Lane 1 => Protocol 1 => Link 1
Lane 2 => Protocol 2 => Link 2
Lane 3 => Protocol 1 => Link 3

A 4-Link 2-Protocol configuration of the 4-Lane SERDES can be:
Lane 0 => Protocol 1 => Link 1
Lane 1 => Protocol 2 => Link 2
Lane 2 => Protocol 1 => Link 3
Lane 3 => Protocol 2 => Link 4

Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Reviewed-by: Roger Quadros <rogerq@kernel.org>
Link: https://lore.kernel.org/r/20240805092607.143869-1-s-vadapalli@ti.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/phy/cadence/phy-cadence-torrent.c

index a2d32d5f6b079c24a1863b098373d4cc1b00caa1..8bbbbb87bb22e5ed3e10bbc0963794c750d280de 100644 (file)
@@ -351,6 +351,7 @@ struct cdns_torrent_phy {
        void __iomem *sd_base; /* SD0801 registers base */
        u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
        u32 dp_pll;
+       u32 protocol_bitmask;
        struct reset_control *phy_rst;
        struct reset_control *apb_rst;
        struct device *dev;
@@ -2473,156 +2474,206 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy)
        enum cdns_torrent_phy_type phy_t1, phy_t2;
        const struct cdns_reg_pairs *reg_pairs;
        int i, j, node, mlane, num_lanes, ret;
+       struct device *dev = cdns_phy->dev;
        enum cdns_torrent_ssc_mode ssc;
        struct regmap *regmap;
-       u32 num_regs;
+       u32 num_regs, num_protocols, protocol;
 
-       /* Maximum 2 links (subnodes) are supported */
-       if (cdns_phy->nsubnodes != 2)
+       num_protocols = hweight32(cdns_phy->protocol_bitmask);
+       /* Maximum 2 protocols are supported */
+       if (num_protocols > 2) {
+               dev_err(dev, "at most 2 protocols are supported\n");
                return -EINVAL;
+       }
+
+
+       /**
+        * Get PHY types directly from subnodes if only 2 subnodes exist.
+        * It is possible for phy_t1 to be the same as phy_t2 for special
+        * configurations such as PCIe Multilink.
+        */
+       if (cdns_phy->nsubnodes == 2) {
+               phy_t1 = cdns_phy->phys[0].phy_type;
+               phy_t2 = cdns_phy->phys[1].phy_type;
+       } else {
+               /**
+                * Both PHY types / protocols should be unique.
+                * If they are the same, it should be expressed with either
+                * a) Single-Link (1 Sub-node) - handled via PHY APIs
+                * OR
+                * b) Double-Link (2 Sub-nodes) - handled above
+                */
+               if (num_protocols != 2) {
+                       dev_err(dev, "incorrect representation of link\n");
+                       return -EINVAL;
+               }
 
-       phy_t1 = cdns_phy->phys[0].phy_type;
-       phy_t2 = cdns_phy->phys[1].phy_type;
+               phy_t1 = fns(cdns_phy->protocol_bitmask, 0);
+               phy_t2 = fns(cdns_phy->protocol_bitmask, 1);
+       }
 
        /**
-        * First configure the PHY for first link with phy_t1. Get the array
-        * values as [phy_t1][phy_t2][ssc].
+        * Configure all links with the protocol phy_t1 first followed by
+        * configuring all links with the protocol phy_t2.
+        *
+        * When phy_t1 = phy_t2, it is a single protocol and configuration
+        * is performed with a single iteration of the protocol and multiple
+        * iterations over the sub-nodes (links).
+        *
+        * When phy_t1 != phy_t2, there are two protocols and configuration
+        * is performed by iterating over all sub-nodes matching the first
+        * protocol and configuring them first, followed by iterating over
+        * all sub-nodes matching the second protocol and configuring them
+        * next.
         */
-       for (node = 0; node < cdns_phy->nsubnodes; node++) {
-               if (node == 1) {
+       for (protocol = 0; protocol < num_protocols; protocol++) {
+               /**
+                * For the case where num_protocols is 1,
+                * phy_t1 = phy_t2 and the swap is unnecessary.
+                *
+                * Swapping phy_t1 and phy_t2 is only required when the
+                * number of protocols is 2 and there are 2 or more links.
+                */
+               if (protocol == 1) {
                        /**
-                        * If first link with phy_t1 is configured, then
-                        * configure the PHY for second link with phy_t2.
+                        * If first protocol with phy_t1 is configured, then
+                        * configure the PHY for second protocol with phy_t2.
                         * Get the array values as [phy_t2][phy_t1][ssc].
                         */
                        swap(phy_t1, phy_t2);
                        swap(ref_clk, ref_clk1);
                }
 
-               mlane = cdns_phy->phys[node].mlane;
-               ssc = cdns_phy->phys[node].ssc_mode;
-               num_lanes = cdns_phy->phys[node].num_lanes;
+               for (node = 0; node < cdns_phy->nsubnodes; node++) {
+                       if (cdns_phy->phys[node].phy_type != phy_t1)
+                               continue;
 
-               /**
-                * PHY configuration specific registers:
-                * link_cmn_vals depend on combination of PHY types being
-                * configured and are common for both PHY types, so array
-                * values should be same for [phy_t1][phy_t2][ssc] and
-                * [phy_t2][phy_t1][ssc].
-                * xcvr_diag_vals also depend on combination of PHY types
-                * being configured, but these can be different for particular
-                * PHY type and are per lane.
-                */
-               link_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->link_cmn_vals_tbl,
-                                                         CLK_ANY, CLK_ANY,
-                                                         phy_t1, phy_t2, ANY_SSC);
-               if (link_cmn_vals) {
-                       reg_pairs = link_cmn_vals->reg_pairs;
-                       num_regs = link_cmn_vals->num_regs;
-                       regmap = cdns_phy->regmap_common_cdb;
+                       mlane = cdns_phy->phys[node].mlane;
+                       ssc = cdns_phy->phys[node].ssc_mode;
+                       num_lanes = cdns_phy->phys[node].num_lanes;
 
                        /**
-                        * First array value in link_cmn_vals must be of
-                        * PHY_PLL_CFG register
+                        * PHY configuration specific registers:
+                        * link_cmn_vals depend on combination of PHY types being
+                        * configured and are common for both PHY types, so array
+                        * values should be same for [phy_t1][phy_t2][ssc] and
+                        * [phy_t2][phy_t1][ssc].
+                        * xcvr_diag_vals also depend on combination of PHY types
+                        * being configured, but these can be different for particular
+                        * PHY type and are per lane.
                         */
-                       regmap_field_write(cdns_phy->phy_pll_cfg,
-                                          reg_pairs[0].val);
+                       link_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->link_cmn_vals_tbl,
+                                                                 CLK_ANY, CLK_ANY,
+                                                                 phy_t1, phy_t2, ANY_SSC);
+                       if (link_cmn_vals) {
+                               reg_pairs = link_cmn_vals->reg_pairs;
+                               num_regs = link_cmn_vals->num_regs;
+                               regmap = cdns_phy->regmap_common_cdb;
+
+                               /**
+                                * First array value in link_cmn_vals must be of
+                                * PHY_PLL_CFG register
+                                */
+                               regmap_field_write(cdns_phy->phy_pll_cfg,
+                                                  reg_pairs[0].val);
+
+                               for (i = 1; i < num_regs; i++)
+                                       regmap_write(regmap, reg_pairs[i].off,
+                                                    reg_pairs[i].val);
+                       }
 
-                       for (i = 1; i < num_regs; i++)
-                               regmap_write(regmap, reg_pairs[i].off,
-                                            reg_pairs[i].val);
-               }
+                       xcvr_diag_vals = cdns_torrent_get_tbl_vals(&init_data->xcvr_diag_vals_tbl,
+                                                                  CLK_ANY, CLK_ANY,
+                                                                  phy_t1, phy_t2, ANY_SSC);
+                       if (xcvr_diag_vals) {
+                               reg_pairs = xcvr_diag_vals->reg_pairs;
+                               num_regs = xcvr_diag_vals->num_regs;
+                               for (i = 0; i < num_lanes; i++) {
+                                       regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane];
+                                       for (j = 0; j < num_regs; j++)
+                                               regmap_write(regmap, reg_pairs[j].off,
+                                                            reg_pairs[j].val);
+                               }
+                       }
 
-               xcvr_diag_vals = cdns_torrent_get_tbl_vals(&init_data->xcvr_diag_vals_tbl,
-                                                          CLK_ANY, CLK_ANY,
-                                                          phy_t1, phy_t2, ANY_SSC);
-               if (xcvr_diag_vals) {
-                       reg_pairs = xcvr_diag_vals->reg_pairs;
-                       num_regs = xcvr_diag_vals->num_regs;
-                       for (i = 0; i < num_lanes; i++) {
-                               regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane];
-                               for (j = 0; j < num_regs; j++)
-                                       regmap_write(regmap, reg_pairs[j].off,
-                                                    reg_pairs[j].val);
+                       /* PHY PCS common registers configurations */
+                       pcs_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->pcs_cmn_vals_tbl,
+                                                                CLK_ANY, CLK_ANY,
+                                                                phy_t1, phy_t2, ANY_SSC);
+                       if (pcs_cmn_vals) {
+                               reg_pairs = pcs_cmn_vals->reg_pairs;
+                               num_regs = pcs_cmn_vals->num_regs;
+                               regmap = cdns_phy->regmap_phy_pcs_common_cdb;
+                               for (i = 0; i < num_regs; i++)
+                                       regmap_write(regmap, reg_pairs[i].off,
+                                                    reg_pairs[i].val);
                        }
-               }
 
-               /* PHY PCS common registers configurations */
-               pcs_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->pcs_cmn_vals_tbl,
-                                                        CLK_ANY, CLK_ANY,
-                                                        phy_t1, phy_t2, ANY_SSC);
-               if (pcs_cmn_vals) {
-                       reg_pairs = pcs_cmn_vals->reg_pairs;
-                       num_regs = pcs_cmn_vals->num_regs;
-                       regmap = cdns_phy->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 common registers configurations */
+                       phy_pma_cmn_vals =
+                               cdns_torrent_get_tbl_vals(&init_data->phy_pma_cmn_vals_tbl,
+                                                         CLK_ANY, CLK_ANY, phy_t1, phy_t2,
+                                                         ANY_SSC);
+                       if (phy_pma_cmn_vals) {
+                               reg_pairs = phy_pma_cmn_vals->reg_pairs;
+                               num_regs = phy_pma_cmn_vals->num_regs;
+                               regmap = cdns_phy->regmap_phy_pma_common_cdb;
+                               for (i = 0; i < num_regs; i++)
+                                       regmap_write(regmap, reg_pairs[i].off,
+                                                    reg_pairs[i].val);
+                       }
 
-               /* PHY PMA common registers configurations */
-               phy_pma_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->phy_pma_cmn_vals_tbl,
-                                                            CLK_ANY, CLK_ANY,
-                                                            phy_t1, phy_t2, ANY_SSC);
-               if (phy_pma_cmn_vals) {
-                       reg_pairs = phy_pma_cmn_vals->reg_pairs;
-                       num_regs = phy_pma_cmn_vals->num_regs;
-                       regmap = cdns_phy->regmap_phy_pma_common_cdb;
-                       for (i = 0; i < num_regs; i++)
-                               regmap_write(regmap, reg_pairs[i].off,
-                                            reg_pairs[i].val);
-               }
+                       /* PMA common registers configurations */
+                       cmn_vals = cdns_torrent_get_tbl_vals(&init_data->cmn_vals_tbl,
+                                                            ref_clk, ref_clk1,
+                                                            phy_t1, phy_t2, ssc);
+                       if (cmn_vals) {
+                               reg_pairs = cmn_vals->reg_pairs;
+                               num_regs = cmn_vals->num_regs;
+                               regmap = cdns_phy->regmap_common_cdb;
+                               for (i = 0; i < num_regs; i++)
+                                       regmap_write(regmap, reg_pairs[i].off,
+                                                    reg_pairs[i].val);
+                       }
 
-               /* PMA common registers configurations */
-               cmn_vals = cdns_torrent_get_tbl_vals(&init_data->cmn_vals_tbl,
-                                                    ref_clk, ref_clk1,
-                                                    phy_t1, phy_t2, ssc);
-               if (cmn_vals) {
-                       reg_pairs = cmn_vals->reg_pairs;
-                       num_regs = cmn_vals->num_regs;
-                       regmap = cdns_phy->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 */
+                       tx_ln_vals = cdns_torrent_get_tbl_vals(&init_data->tx_ln_vals_tbl,
+                                                              ref_clk, ref_clk1,
+                                                              phy_t1, phy_t2, ssc);
+                       if (tx_ln_vals) {
+                               reg_pairs = tx_ln_vals->reg_pairs;
+                               num_regs = tx_ln_vals->num_regs;
+                               for (i = 0; i < num_lanes; i++) {
+                                       regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane];
+                                       for (j = 0; j < num_regs; j++)
+                                               regmap_write(regmap, reg_pairs[j].off,
+                                                            reg_pairs[j].val);
+                               }
+                       }
 
-               /* PMA TX lane registers configurations */
-               tx_ln_vals = cdns_torrent_get_tbl_vals(&init_data->tx_ln_vals_tbl,
-                                                      ref_clk, ref_clk1,
-                                                      phy_t1, phy_t2, ssc);
-               if (tx_ln_vals) {
-                       reg_pairs = tx_ln_vals->reg_pairs;
-                       num_regs = tx_ln_vals->num_regs;
-                       for (i = 0; i < num_lanes; i++) {
-                               regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane];
-                               for (j = 0; j < num_regs; j++)
-                                       regmap_write(regmap, reg_pairs[j].off,
-                                                    reg_pairs[j].val);
+                       /* PMA RX lane registers configurations */
+                       rx_ln_vals = cdns_torrent_get_tbl_vals(&init_data->rx_ln_vals_tbl,
+                                                              ref_clk, ref_clk1,
+                                                              phy_t1, phy_t2, ssc);
+                       if (rx_ln_vals) {
+                               reg_pairs = rx_ln_vals->reg_pairs;
+                               num_regs = rx_ln_vals->num_regs;
+                               for (i = 0; i < num_lanes; i++) {
+                                       regmap = cdns_phy->regmap_rx_lane_cdb[i + mlane];
+                                       for (j = 0; j < num_regs; j++)
+                                               regmap_write(regmap, reg_pairs[j].off,
+                                                            reg_pairs[j].val);
+                               }
                        }
-               }
 
-               /* PMA RX lane registers configurations */
-               rx_ln_vals = cdns_torrent_get_tbl_vals(&init_data->rx_ln_vals_tbl,
-                                                      ref_clk, ref_clk1,
-                                                      phy_t1, phy_t2, ssc);
-               if (rx_ln_vals) {
-                       reg_pairs = rx_ln_vals->reg_pairs;
-                       num_regs = rx_ln_vals->num_regs;
-                       for (i = 0; i < num_lanes; i++) {
-                               regmap = cdns_phy->regmap_rx_lane_cdb[i + mlane];
-                               for (j = 0; j < num_regs; j++)
-                                       regmap_write(regmap, reg_pairs[j].off,
-                                                    reg_pairs[j].val);
+                       if (phy_t1 == TYPE_DP) {
+                               ret = cdns_torrent_dp_get_pll(cdns_phy, phy_t2);
+                               if (ret)
+                                       return ret;
                        }
-               }
 
-               if (phy_t1 == TYPE_DP) {
-                       ret = cdns_torrent_dp_get_pll(cdns_phy, phy_t2);
-                       if (ret)
-                               return ret;
+                       reset_control_deassert(cdns_phy->phys[node].lnk_rst);
                }
-
-               reset_control_deassert(cdns_phy->phys[node].lnk_rst);
        }
 
        /* Take the PHY out of reset */
@@ -2826,6 +2877,7 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
        dev_set_drvdata(dev, cdns_phy);
        cdns_phy->dev = dev;
        cdns_phy->init_data = data;
+       cdns_phy->protocol_bitmask = 0;
 
        cdns_phy->sd_base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(cdns_phy->sd_base))
@@ -3010,6 +3062,7 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
                }
 
                cdns_phy->phys[node].phy = gphy;
+               cdns_phy->protocol_bitmask |= BIT(cdns_phy->phys[node].phy_type);
                phy_set_drvdata(gphy, &cdns_phy->phys[node]);
 
                node++;