net: stmmac: dwmac-meson8b: add support for the RGMII RX delay on G12A
authorMartin Blumenstingl <martin.blumenstingl@googlemail.com>
Wed, 6 Jan 2021 13:42:51 +0000 (14:42 +0100)
committerJakub Kicinski <kuba@kernel.org>
Thu, 7 Jan 2021 23:58:33 +0000 (15:58 -0800)
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.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c

index 4937432..55152d7 100644 (file)
  */
 #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;
 }
 
@@ -423,11 +442,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,
@@ -469,10 +497,17 @@ err_remove_config_dt:
 
 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[] = {
@@ -494,7 +529,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,
        },
        { }
 };