Amlogic Meson G12A (and newer: G12B, SM1) SoCs have a more advanced RX
delay logic. Instead of fine-tuning the delay in the nanoseconds range
it now allows tuning in 200 picosecond steps. This support comes with
new bits in the PRG_ETH1[19:16] register.

Add support for validating the RGMII RX delay as well as configuring the
register accordingly on these platforms.

Signed-off-by: Martin Blumenstingl <martin.blumensti...@googlemail.com>
---
 .../ethernet/stmicro/stmmac/dwmac-meson8b.c   | 61 +++++++++++++++----
 1 file changed, 48 insertions(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
index 353fe0f53620..2184b6c2c784 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
@@ -69,10 +69,21 @@
  */
 #define PRG_ETH0_ADJ_SKEW              GENMASK(24, 20)
 
+#define PRG_ETH1                       0x4
+
+/* Defined for adding a delay to the input RX_CLK for better timing.
+ * Each step is 200ps. These bits are used with external RGMII PHYs
+ * because RGMII RX only has the small window. cfg_rxclk_dly can
+ * adjust the window between RX_CLK and RX_DATA and improve the stability
+ * of "rx data valid".
+ */
+#define PRG_ETH1_CFG_RXCLK_DLY         GENMASK(19, 16)
+
 struct meson8b_dwmac;
 
 struct meson8b_dwmac_data {
        int (*set_phy_mode)(struct meson8b_dwmac *dwmac);
+       bool has_prg_eth1_rgmii_rx_delay;
 };
 
 struct meson8b_dwmac {
@@ -270,30 +281,35 @@ static int meson8b_devm_clk_prepare_enable(struct 
meson8b_dwmac *dwmac,
 
 static int meson8b_init_rgmii_delays(struct meson8b_dwmac *dwmac)
 {
-       u32 tx_dly_config, rx_dly_config, delay_config;
+       u32 tx_dly_config, rx_adj_config, cfg_rxclk_dly, delay_config;
        int ret;
 
+       rx_adj_config = 0;
+       cfg_rxclk_dly = 0;
        tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK,
                                   dwmac->tx_delay_ns >> 1);
 
-       if (dwmac->rx_delay_ps == 2000)
-               rx_dly_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP;
-       else
-               rx_dly_config = 0;
+       if (dwmac->data->has_prg_eth1_rgmii_rx_delay)
+               cfg_rxclk_dly = FIELD_PREP(PRG_ETH1_CFG_RXCLK_DLY,
+                                          dwmac->rx_delay_ps / 200);
+       else if (dwmac->rx_delay_ps == 2000)
+               rx_adj_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP;
 
        switch (dwmac->phy_mode) {
        case PHY_INTERFACE_MODE_RGMII:
-               delay_config = tx_dly_config | rx_dly_config;
+               delay_config = tx_dly_config | rx_adj_config;
                break;
        case PHY_INTERFACE_MODE_RGMII_RXID:
                delay_config = tx_dly_config;
+               cfg_rxclk_dly = 0;
                break;
        case PHY_INTERFACE_MODE_RGMII_TXID:
-               delay_config = rx_dly_config;
+               delay_config = rx_adj_config;
                break;
        case PHY_INTERFACE_MODE_RGMII_ID:
        case PHY_INTERFACE_MODE_RMII:
                delay_config = 0;
+               cfg_rxclk_dly = 0;
                break;
        default:
                dev_err(dwmac->dev, "unsupported phy-mode %s\n",
@@ -323,6 +339,9 @@ static int meson8b_init_rgmii_delays(struct meson8b_dwmac 
*dwmac)
                                PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW,
                                delay_config);
 
+       meson8b_dwmac_mask_bits(dwmac, PRG_ETH1, PRG_ETH1_CFG_RXCLK_DLY,
+                               cfg_rxclk_dly);
+
        return 0;
 }
 
@@ -424,11 +443,20 @@ static int meson8b_dwmac_probe(struct platform_device 
*pdev)
                        dwmac->rx_delay_ps *= 1000;
        }
 
-       if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) {
-               dev_err(&pdev->dev,
-                       "The only allowed RX delays values are: 0ps, 2000ps");
-               ret = -EINVAL;
-               goto err_remove_config_dt;
+       if (dwmac->data->has_prg_eth1_rgmii_rx_delay) {
+               if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) {
+                       dev_err(dwmac->dev,
+                               "The only allowed RGMII RX delays values are: 
0ps, 2000ps");
+                       ret = -EINVAL;
+                       goto err_remove_config_dt;
+               }
+       } else {
+               if (dwmac->rx_delay_ps > 3000 || dwmac->rx_delay_ps % 200) {
+                       dev_err(dwmac->dev,
+                               "The RGMII RX delay range is 0..3000ps in 200ps 
steps");
+                       ret = -EINVAL;
+                       goto err_remove_config_dt;
+               }
        }
 
        dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev,
@@ -470,10 +498,17 @@ static int meson8b_dwmac_probe(struct platform_device 
*pdev)
 
 static const struct meson8b_dwmac_data meson8b_dwmac_data = {
        .set_phy_mode = meson8b_set_phy_mode,
+       .has_prg_eth1_rgmii_rx_delay = false,
 };
 
 static const struct meson8b_dwmac_data meson_axg_dwmac_data = {
        .set_phy_mode = meson_axg_set_phy_mode,
+       .has_prg_eth1_rgmii_rx_delay = false,
+};
+
+static const struct meson8b_dwmac_data meson_g12a_dwmac_data = {
+       .set_phy_mode = meson_axg_set_phy_mode,
+       .has_prg_eth1_rgmii_rx_delay = true,
 };
 
 static const struct of_device_id meson8b_dwmac_match[] = {
@@ -495,7 +530,7 @@ static const struct of_device_id meson8b_dwmac_match[] = {
        },
        {
                .compatible = "amlogic,meson-g12a-dwmac",
-               .data = &meson_axg_dwmac_data,
+               .data = &meson_g12a_dwmac_data,
        },
        { }
 };
-- 
2.29.2

Reply via email to