]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
phy: cadence: torrent: add support for three or more links using 2 protocols
authorHrushikesh Salunke <h-salunke@ti.com>
Fri, 13 Jun 2025 06:21:21 +0000 (11:51 +0530)
committerTom Rini <trini@konsulko.com>
Thu, 26 Jun 2025 19:48:36 +0000 (13:48 -0600)
This is a port of the corresponding commit in the Linux kernel which
adds the same support for the Cadence Torrent driver[0]. The commit
message below is taken as-is from the Linux kernel commit being ported.

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

[0] https://github.com/torvalds/linux/commit/5b7b83a9839be643410c31d56f17c2d430245813

Signed-off-by: Hrushikesh Salunke <h-salunke@ti.com>
drivers/phy/cadence/phy-cadence-torrent.c

index d4e8ece4935a28b7a730e2a719273e9becc73d96..1f566d082f998f131bfce87b906f046c42220d09 100644 (file)
@@ -240,6 +240,7 @@ struct cdns_torrent_inst {
 
 struct cdns_torrent_phy {
        void __iomem *sd_base;  /* SD0801 register base  */
+       u32 protocol_bitmask;
        size_t size;
        struct reset_control *phy_rst;
        struct udevice *dev;
@@ -432,124 +433,155 @@ static int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_ph
        struct cdns_reg_pairs *reg_pairs;
        enum cdns_torrent_ssc_mode ssc;
        struct regmap *regmap;
-       u32 num_regs;
+       u32 num_regs, num_protocols, protocol;
+
+       num_protocols = hweight32(cdns_phy->protocol_bitmask);
 
-       /* Maximum 2 links (subnodes) are supported */
-       if (cdns_phy->nsubnodes != 2)
+       /* Maximum 2 protocols are supported */
+       if (num_protocols > 2) {
+               dev_err(cdns_phy->dev, "at most 2 protocols are supported\n");
                return -EINVAL;
+       }
 
-       phy_t1 = cdns_phy->phys[0].phy_type;
-       phy_t2 = cdns_phy->phys[1].phy_type;
+       if (cdns_phy->nsubnodes == 2) {
+               phy_t1 = cdns_phy->phys[0].phy_type;
+               phy_t2 = cdns_phy->phys[1].phy_type;
+       } else {
+               if (num_protocols != 2) {
+                       dev_err(cdns_phy->dev, "incorrect representation of link\n");
+                       return -EINVAL;
+               }
+               phy_t1 = __ffs(cdns_phy->protocol_bitmask);
+               phy_t2 = __fls(cdns_phy->protocol_bitmask);
+       }
 
-       /*
-        * First configure the PHY for first link with phy_t1. Geth the array
-        * values are [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) {
-                       /*
-                        * If fist 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]
-                        */
+
+       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) {
                        tmp_phy_type = phy_t1;
                        phy_t1 = phy_t2;
                        phy_t2 = tmp_phy_type;
                }
 
-               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 = init_data->link_cmn_vals[phy_t1][phy_t2][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);
-
-                       for (i = 1; i < num_regs; i++)
-                               regmap_write(regmap, reg_pairs[i].off,
-                                            reg_pairs[i].val);
-               }
+                       link_cmn_vals = init_data->link_cmn_vals[phy_t1][phy_t2][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);
+                       }
 
-               xcvr_diag_vals = init_data->xcvr_diag_vals[phy_t1][phy_t2][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 = init_data->xcvr_diag_vals[phy_t1][phy_t2][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 = 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 = 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 = 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 = 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);
+                       }
 
-               /* PMA common registers configurations */
-               cmn_vals = init_data->cmn_vals[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 = init_data->cmn_vals[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 = init_data->tx_ln_vals[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 = init_data->tx_ln_vals[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 = init_data->rx_ln_vals[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 = init_data->rx_ln_vals[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);
+                               }
                        }
-               }
 
-               reset_deassert_bulk(cdns_phy->phys[node].lnk_rst);
+                       reset_deassert_bulk(cdns_phy->phys[node].lnk_rst);
+               }
        }
 
        /* Take the PHY out of reset */
@@ -575,6 +607,7 @@ static int cdns_torrent_phy_probe(struct udevice *dev)
        /* Get init data for this phy  */
        data = (struct cdns_torrent_data *)dev_get_driver_data(dev);
        cdns_phy->init_data = data;
+       cdns_phy->protocol_bitmask = 0;
 
        cdns_phy->phy_rst = devm_reset_control_get_by_index(dev, 0);
        if (IS_ERR(cdns_phy->phy_rst)) {
@@ -677,6 +710,8 @@ static int cdns_torrent_phy_probe(struct udevice *dev)
                /* Get SSC mode */
                ofnode_read_u32(child, "cdns,ssc-mode",
                                &cdns_phy->phys[node].ssc_mode);
+
+               cdns_phy->protocol_bitmask |= BIT(cdns_phy->phys[node].phy_type);
                node++;
        }