net: stmmac: dwmac-meson8b: add support for the RX delay configuration
authorMartin Blumenstingl <martin.blumenstingl@googlemail.com>
Tue, 12 May 2020 21:11:03 +0000 (23:11 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 13 May 2020 19:23:14 +0000 (12:23 -0700)
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: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c

index d31f79c..234e8b6 100644 (file)
@@ -85,6 +85,7 @@ struct meson8b_dwmac {
        phy_interface_t                 phy_mode;
        struct clk                      *rgmii_tx_clk;
        u32                             tx_delay_ns;
+       u32                             rx_delay_ns;
        struct clk                      *timing_adj_clk;
 };
 
@@ -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)
 {
+       u32 tx_dly_config, rx_dly_config, delay_config;
        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) {
        case PHY_INTERFACE_MODE_RGMII:
+               delay_config = tx_dly_config | rx_dly_config;
+               break;
        case PHY_INTERFACE_MODE_RGMII_RXID:
-               tx_dly_val = dwmac->tx_delay_ns >> 1;
-               /* fall through */
-
-       case PHY_INTERFACE_MODE_RGMII_ID:
+               delay_config = tx_dly_config;
+               break;
        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 */
                meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
                                        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
                 * the output automatically (= without us having to configure
                 * 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)
                                "failed to enable the RGMII TX clock\n");
                        return ret;
                }
-               break;
-
-       case PHY_INTERFACE_MODE_RMII:
+       } else {
                /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
                meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
                                        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 */
@@ -394,6 +421,18 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
                                 &dwmac->tx_delay_ns))
                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,
                                                      "timing-adjustment");
        if (IS_ERR(dwmac->timing_adj_clk)) {