diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro')
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 91 |
1 files changed, 71 insertions, 20 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index f184b00f5116..55152d7ba99a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -68,10 +68,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 { @@ -82,7 +93,7 @@ struct meson8b_dwmac { phy_interface_t phy_mode; struct clk *rgmii_tx_clk; u32 tx_delay_ns; - u32 rx_delay_ns; + u32 rx_delay_ps; struct clk *timing_adj_clk; }; @@ -268,32 +279,37 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac, return 0; } -static int meson8b_init_prg_eth(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_ns == 2) - 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", @@ -301,7 +317,7 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) return -EINVAL; } - if (rx_dly_config & PRG_ETH0_ADJ_ENABLE) { + if (delay_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"); @@ -323,6 +339,16 @@ static int meson8b_init_prg_eth(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; +} + +static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) +{ + int ret; + 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, @@ -406,16 +432,30 @@ 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; + /* RX delay defaults to 0ps since this is what many boards use */ + if (of_property_read_u32(pdev->dev.of_node, "rx-internal-delay-ps", + &dwmac->rx_delay_ps)) { + if (!of_property_read_u32(pdev->dev.of_node, + "amlogic,rx-delay-ns", + &dwmac->rx_delay_ps)) + /* convert ns to ps */ + dwmac->rx_delay_ps *= 1000; + } - 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; + 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, @@ -425,6 +465,10 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) goto err_remove_config_dt; } + ret = meson8b_init_rgmii_delays(dwmac); + if (ret) + goto err_remove_config_dt; + ret = meson8b_init_rgmii_tx_clk(dwmac); if (ret) goto err_remove_config_dt; @@ -453,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[] = { @@ -478,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, }, { } }; |