Commit 5b7b83a9 authored by Siddharth Vadapalli's avatar Siddharth Vadapalli Committed by Vinod Koul
Browse files

phy: cadence-torrent: add support for three or more links using 2 protocols



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: default avatarSiddharth Vadapalli <s-vadapalli@ti.com>
Reviewed-by: default avatarRoger Quadros <rogerq@kernel.org>
Link: https://lore.kernel.org/r/20240805092607.143869-1-s-vadapalli@ti.com


Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 0f20e326
Loading
Loading
Loading
Loading
+172 −119
Original line number Diff line number Diff line
@@ -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,32 +2474,80 @@ 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 = 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++) {
		/**
			 * If first link with phy_t1 is configured, then
			 * configure the PHY for second link with phy_t2.
		 * 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 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);
		}

		for (node = 0; node < cdns_phy->nsubnodes; node++) {
			if (cdns_phy->phys[node].phy_type != phy_t1)
				continue;

			mlane = cdns_phy->phys[node].mlane;
			ssc = cdns_phy->phys[node].ssc_mode;
			num_lanes = cdns_phy->phys[node].num_lanes;
@@ -2561,9 +2610,10 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy)
			}

			/* 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);
			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;
@@ -2624,6 +2674,7 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy)

			reset_control_deassert(cdns_phy->phys[node].lnk_rst);
		}
	}

	/* Take the PHY out of reset */
	ret = reset_control_deassert(cdns_phy->phy_rst);
@@ -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++;