mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-08-09 16:56:58 +02:00
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:
parent
7fd2795a4b
commit
f05ba765a1
@ -240,6 +240,7 @@ struct cdns_torrent_inst {
|
|||||||
|
|
||||||
struct cdns_torrent_phy {
|
struct cdns_torrent_phy {
|
||||||
void __iomem *sd_base; /* SD0801 register base */
|
void __iomem *sd_base; /* SD0801 register base */
|
||||||
|
u32 protocol_bitmask;
|
||||||
size_t size;
|
size_t size;
|
||||||
struct reset_control *phy_rst;
|
struct reset_control *phy_rst;
|
||||||
struct udevice *dev;
|
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;
|
struct cdns_reg_pairs *reg_pairs;
|
||||||
enum cdns_torrent_ssc_mode ssc;
|
enum cdns_torrent_ssc_mode ssc;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
u32 num_regs;
|
u32 num_regs, num_protocols, protocol;
|
||||||
|
|
||||||
/* Maximum 2 links (subnodes) are supported */
|
num_protocols = hweight32(cdns_phy->protocol_bitmask);
|
||||||
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;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
phy_t1 = cdns_phy->phys[0].phy_type;
|
if (cdns_phy->nsubnodes == 2) {
|
||||||
phy_t2 = cdns_phy->phys[1].phy_type;
|
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
|
* Configure all links with the protocol phy_t1 first followed by
|
||||||
* values are [phy_t1][phy_t2][ssc].
|
* 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 fist link with phy_t1 is configured, then
|
* For the case where num_protocols is 1,
|
||||||
* configure the PHY for second link with phy_t2.
|
* phy_t1 = phy_t2 and the swap is unnecessary.
|
||||||
* Get the array values as [phy_t2][phy_t1][ssc]
|
*
|
||||||
*/
|
* 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;
|
tmp_phy_type = phy_t1;
|
||||||
phy_t1 = phy_t2;
|
phy_t1 = phy_t2;
|
||||||
phy_t2 = tmp_phy_type;
|
phy_t2 = tmp_phy_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
mlane = cdns_phy->phys[node].mlane;
|
for (node = 0; node < cdns_phy->nsubnodes; node++) {
|
||||||
ssc = cdns_phy->phys[node].ssc_mode;
|
if (cdns_phy->phys[node].phy_type != phy_t1)
|
||||||
num_lanes = cdns_phy->phys[node].num_lanes;
|
continue;
|
||||||
|
|
||||||
/**
|
mlane = cdns_phy->phys[node].mlane;
|
||||||
* PHY configuration specific registers:
|
ssc = cdns_phy->phys[node].ssc_mode;
|
||||||
* link_cmn_vals depend on combination of PHY types being
|
num_lanes = cdns_phy->phys[node].num_lanes;
|
||||||
* 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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First array value in link_cmn_vals must be of
|
* PHY configuration specific registers:
|
||||||
* PHY_PLL_CFG register
|
* 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,
|
link_cmn_vals = init_data->link_cmn_vals[phy_t1][phy_t2][ssc];
|
||||||
reg_pairs[0].val);
|
if (link_cmn_vals) {
|
||||||
|
reg_pairs = link_cmn_vals->reg_pairs;
|
||||||
|
num_regs = link_cmn_vals->num_regs;
|
||||||
|
regmap = cdns_phy->regmap_common_cdb;
|
||||||
|
|
||||||
for (i = 1; i < num_regs; i++)
|
/**
|
||||||
regmap_write(regmap, reg_pairs[i].off,
|
* First array value in link_cmn_vals must be of
|
||||||
reg_pairs[i].val);
|
* PHY_PLL_CFG register
|
||||||
}
|
*/
|
||||||
|
regmap_field_write(cdns_phy->phy_pll_cfg,
|
||||||
|
reg_pairs[0].val);
|
||||||
|
|
||||||
xcvr_diag_vals = init_data->xcvr_diag_vals[phy_t1][phy_t2][ssc];
|
for (i = 1; i < num_regs; i++)
|
||||||
if (xcvr_diag_vals) {
|
regmap_write(regmap, reg_pairs[i].off,
|
||||||
reg_pairs = xcvr_diag_vals->reg_pairs;
|
reg_pairs[i].val);
|
||||||
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 */
|
xcvr_diag_vals = init_data->xcvr_diag_vals[phy_t1][phy_t2][ssc];
|
||||||
pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc];
|
if (xcvr_diag_vals) {
|
||||||
if (pcs_cmn_vals) {
|
reg_pairs = xcvr_diag_vals->reg_pairs;
|
||||||
reg_pairs = pcs_cmn_vals->reg_pairs;
|
num_regs = xcvr_diag_vals->num_regs;
|
||||||
num_regs = pcs_cmn_vals->num_regs;
|
for (i = 0; i < num_lanes; i++) {
|
||||||
regmap = cdns_phy->regmap_phy_pcs_common_cdb;
|
regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane];
|
||||||
for (i = 0; i < num_regs; i++)
|
for (j = 0; j < num_regs; j++)
|
||||||
regmap_write(regmap, reg_pairs[i].off,
|
regmap_write(regmap, reg_pairs[j].off,
|
||||||
reg_pairs[i].val);
|
reg_pairs[j].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 RX lane registers configurations */
|
/* PHY PCS common registers configurations */
|
||||||
rx_ln_vals = init_data->rx_ln_vals[phy_t1][phy_t2][ssc];
|
pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc];
|
||||||
if (rx_ln_vals) {
|
if (pcs_cmn_vals) {
|
||||||
reg_pairs = rx_ln_vals->reg_pairs;
|
reg_pairs = pcs_cmn_vals->reg_pairs;
|
||||||
num_regs = rx_ln_vals->num_regs;
|
num_regs = pcs_cmn_vals->num_regs;
|
||||||
for (i = 0; i < num_lanes; i++) {
|
regmap = cdns_phy->regmap_phy_pcs_common_cdb;
|
||||||
regmap = cdns_phy->regmap_rx_lane_cdb[i + mlane];
|
for (i = 0; i < num_regs; i++)
|
||||||
for (j = 0; j < num_regs; j++)
|
regmap_write(regmap, reg_pairs[i].off,
|
||||||
regmap_write(regmap, reg_pairs[j].off,
|
reg_pairs[i].val);
|
||||||
reg_pairs[j].val);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
reset_deassert_bulk(cdns_phy->phys[node].lnk_rst);
|
/* 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 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Take the PHY out of reset */
|
/* 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 */
|
/* Get init data for this phy */
|
||||||
data = (struct cdns_torrent_data *)dev_get_driver_data(dev);
|
data = (struct cdns_torrent_data *)dev_get_driver_data(dev);
|
||||||
cdns_phy->init_data = data;
|
cdns_phy->init_data = data;
|
||||||
|
cdns_phy->protocol_bitmask = 0;
|
||||||
|
|
||||||
cdns_phy->phy_rst = devm_reset_control_get_by_index(dev, 0);
|
cdns_phy->phy_rst = devm_reset_control_get_by_index(dev, 0);
|
||||||
if (IS_ERR(cdns_phy->phy_rst)) {
|
if (IS_ERR(cdns_phy->phy_rst)) {
|
||||||
@ -677,6 +710,8 @@ static int cdns_torrent_phy_probe(struct udevice *dev)
|
|||||||
/* Get SSC mode */
|
/* Get SSC mode */
|
||||||
ofnode_read_u32(child, "cdns,ssc-mode",
|
ofnode_read_u32(child, "cdns,ssc-mode",
|
||||||
&cdns_phy->phys[node].ssc_mode);
|
&cdns_phy->phys[node].ssc_mode);
|
||||||
|
|
||||||
|
cdns_phy->protocol_bitmask |= BIT(cdns_phy->phys[node].phy_type);
|
||||||
node++;
|
node++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user