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

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] 5b7b83a983

Signed-off-by: Hrushikesh Salunke <h-salunke@ti.com>
This commit is contained in:
Hrushikesh Salunke 2025-06-13 11:51:21 +05:30 committed by Tom Rini
parent 7fd2795a4b
commit f05ba765a1

View 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,31 +433,61 @@ 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;
/* 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(cdns_phy->dev, "at most 2 protocols are supported\n");
return -EINVAL;
}
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;
}
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;
@ -551,6 +582,7 @@ static int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_ph
reset_deassert_bulk(cdns_phy->phys[node].lnk_rst);
}
}
/* Take the PHY out of reset */
ret = reset_control_deassert(cdns_phy->phy_rst);
@ -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++;
}