Commit 9308c476 authored by Martin Blumenstingl's avatar Martin Blumenstingl Committed by David S. Miller

net: stmmac: dwmac-meson8b: add support for the RX delay configuration

Configure the PRG_ETH0_ADJ_* bits to enable or disable the RX delay
based on the various RGMII PHY modes. For now the only supported RX
delay settings are:
- disabled, use for example for phy-mode "rgmii-id"
- 0ns - this is treated identical to "disabled", used for example on
  boards where the PHY provides 2ns TX delay and the PCB trace length
  already adds 2ns RX delay
- 2ns - for whenever the PHY cannot add the RX delay and the traces on
  the PCB don't add any RX delay

Disabling the RX delay (in case u-boot enables it, which is the case
for example on Meson8b Odroid-C1) simply means that PRG_ETH0_ADJ_ENABLE,
PRG_ETH0_ADJ_SETUP, PRG_ETH0_ADJ_DELAY and PRG_ETH0_ADJ_SKEW should be
disabled (just disabling PRG_ETH0_ADJ_ENABLE may be enough, since that
disables the whole re-timing logic - but I find it makes more sense to
clear the other bits as well since they depend on that setting).

u-boot on Odroid-C1 uses the following steps to enable a 2ns RX delay:
- enabling enabling the timing adjustment clock
- enabling the timing adjustment logic by setting PRG_ETH0_ADJ_ENABLE
- setting the PRG_ETH0_ADJ_SETUP bit

The documentation for the PRG_ETH0_ADJ_DELAY and PRG_ETH0_ADJ_SKEW
registers indicates that we can even set different RX delays. However,
I could not find out how this works exactly, so for now we only support
a 2ns RX delay using the exact same way that Odroid-C1's u-boot does.
Signed-off-by: default avatarMartin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a54dc4a4
...@@ -85,6 +85,7 @@ struct meson8b_dwmac { ...@@ -85,6 +85,7 @@ struct meson8b_dwmac {
phy_interface_t phy_mode; phy_interface_t phy_mode;
struct clk *rgmii_tx_clk; struct clk *rgmii_tx_clk;
u32 tx_delay_ns; u32 tx_delay_ns;
u32 rx_delay_ns;
struct clk *timing_adj_clk; struct clk *timing_adj_clk;
}; };
...@@ -284,25 +285,64 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac, ...@@ -284,25 +285,64 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac,
static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
{ {
u32 tx_dly_config, rx_dly_config, delay_config;
int ret; int ret;
u8 tx_dly_val = 0;
tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK,
dwmac->tx_delay_ns >> 1);
if (dwmac->rx_delay_ns == 2)
rx_dly_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP;
else
rx_dly_config = 0;
switch (dwmac->phy_mode) { switch (dwmac->phy_mode) {
case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII:
delay_config = tx_dly_config | rx_dly_config;
break;
case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_RXID:
tx_dly_val = dwmac->tx_delay_ns >> 1; delay_config = tx_dly_config;
/* fall through */ break;
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RGMII_TXID:
delay_config = rx_dly_config;
break;
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RMII:
delay_config = 0;
break;
default:
dev_err(dwmac->dev, "unsupported phy-mode %s\n",
phy_modes(dwmac->phy_mode));
return -EINVAL;
};
if (rx_dly_config & PRG_ETH0_ADJ_ENABLE) {
if (!dwmac->timing_adj_clk) {
dev_err(dwmac->dev,
"The timing-adjustment clock is mandatory for the RX delay re-timing\n");
return -EINVAL;
}
/* The timing adjustment logic is driven by a separate clock */
ret = meson8b_devm_clk_prepare_enable(dwmac,
dwmac->timing_adj_clk);
if (ret) {
dev_err(dwmac->dev,
"Failed to enable the timing-adjustment clock\n");
return ret;
}
}
meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK |
PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP |
PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW,
delay_config);
if (phy_interface_mode_is_rgmii(dwmac->phy_mode)) {
/* only relevant for RMII mode -> disable in RGMII mode */ /* only relevant for RMII mode -> disable in RGMII mode */
meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
PRG_ETH0_INVERTED_RMII_CLK, 0); PRG_ETH0_INVERTED_RMII_CLK, 0);
meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
FIELD_PREP(PRG_ETH0_TXDLY_MASK,
tx_dly_val));
/* Configure the 125MHz RGMII TX clock, the IP block changes /* Configure the 125MHz RGMII TX clock, the IP block changes
* the output automatically (= without us having to configure * the output automatically (= without us having to configure
* a register) based on the line-speed (125MHz for Gbit speeds, * a register) based on the line-speed (125MHz for Gbit speeds,
...@@ -322,24 +362,11 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) ...@@ -322,24 +362,11 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
"failed to enable the RGMII TX clock\n"); "failed to enable the RGMII TX clock\n");
return ret; return ret;
} }
break; } else {
case PHY_INTERFACE_MODE_RMII:
/* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */ /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
PRG_ETH0_INVERTED_RMII_CLK, PRG_ETH0_INVERTED_RMII_CLK,
PRG_ETH0_INVERTED_RMII_CLK); PRG_ETH0_INVERTED_RMII_CLK);
/* TX clock delay cannot be configured in RMII mode */
meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
0);
break;
default:
dev_err(dwmac->dev, "unsupported phy-mode %s\n",
phy_modes(dwmac->phy_mode));
return -EINVAL;
} }
/* enable TX_CLK and PHY_REF_CLK generator */ /* enable TX_CLK and PHY_REF_CLK generator */
...@@ -394,6 +421,18 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) ...@@ -394,6 +421,18 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
&dwmac->tx_delay_ns)) &dwmac->tx_delay_ns))
dwmac->tx_delay_ns = 2; dwmac->tx_delay_ns = 2;
/* use 0ns as fallback since this is what most boards actually use */
if (of_property_read_u32(pdev->dev.of_node, "amlogic,rx-delay-ns",
&dwmac->rx_delay_ns))
dwmac->rx_delay_ns = 0;
if (dwmac->rx_delay_ns != 0 && dwmac->rx_delay_ns != 2) {
dev_err(&pdev->dev,
"The only allowed RX delays values are: 0ns, 2ns");
ret = -EINVAL;
goto err_remove_config_dt;
}
dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev, dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev,
"timing-adjustment"); "timing-adjustment");
if (IS_ERR(dwmac->timing_adj_clk)) { if (IS_ERR(dwmac->timing_adj_clk)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment