Commit de94fc10 authored by Martin Blumenstingl's avatar Martin Blumenstingl Committed by Jakub Kicinski

net: stmmac: dwmac-meson8b: add support for the RGMII RX delay on G12A

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: default avatarAndrew Lunn <andrew@lunn.ch>
Reviewed-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarMartin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 7985244d
...@@ -68,10 +68,21 @@ ...@@ -68,10 +68,21 @@
*/ */
#define PRG_ETH0_ADJ_SKEW GENMASK(24, 20) #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;
struct meson8b_dwmac_data { struct meson8b_dwmac_data {
int (*set_phy_mode)(struct meson8b_dwmac *dwmac); int (*set_phy_mode)(struct meson8b_dwmac *dwmac);
bool has_prg_eth1_rgmii_rx_delay;
}; };
struct meson8b_dwmac { struct meson8b_dwmac {
...@@ -270,30 +281,35 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *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) 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; int ret;
rx_adj_config = 0;
cfg_rxclk_dly = 0;
tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK, tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK,
dwmac->tx_delay_ns >> 1); dwmac->tx_delay_ns >> 1);
if (dwmac->rx_delay_ps == 2000) if (dwmac->data->has_prg_eth1_rgmii_rx_delay)
rx_dly_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP; cfg_rxclk_dly = FIELD_PREP(PRG_ETH1_CFG_RXCLK_DLY,
else dwmac->rx_delay_ps / 200);
rx_dly_config = 0; else if (dwmac->rx_delay_ps == 2000)
rx_adj_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP;
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; delay_config = tx_dly_config | rx_adj_config;
break; break;
case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_RXID:
delay_config = tx_dly_config; delay_config = tx_dly_config;
cfg_rxclk_dly = 0;
break; break;
case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RGMII_TXID:
delay_config = rx_dly_config; delay_config = rx_adj_config;
break; break;
case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RMII: case PHY_INTERFACE_MODE_RMII:
delay_config = 0; delay_config = 0;
cfg_rxclk_dly = 0;
break; break;
default: default:
dev_err(dwmac->dev, "unsupported phy-mode %s\n", dev_err(dwmac->dev, "unsupported phy-mode %s\n",
...@@ -323,6 +339,9 @@ static int meson8b_init_rgmii_delays(struct meson8b_dwmac *dwmac) ...@@ -323,6 +339,9 @@ static int meson8b_init_rgmii_delays(struct meson8b_dwmac *dwmac)
PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW, PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW,
delay_config); delay_config);
meson8b_dwmac_mask_bits(dwmac, PRG_ETH1, PRG_ETH1_CFG_RXCLK_DLY,
cfg_rxclk_dly);
return 0; return 0;
} }
...@@ -423,11 +442,20 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) ...@@ -423,11 +442,20 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
dwmac->rx_delay_ps *= 1000; dwmac->rx_delay_ps *= 1000;
} }
if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) { if (dwmac->data->has_prg_eth1_rgmii_rx_delay) {
dev_err(&pdev->dev, if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) {
"The only allowed RX delays values are: 0ps, 2000ps"); dev_err(dwmac->dev,
ret = -EINVAL; "The only allowed RGMII RX delays values are: 0ps, 2000ps");
goto err_remove_config_dt; 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, dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev,
...@@ -469,10 +497,17 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) ...@@ -469,10 +497,17 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
static const struct meson8b_dwmac_data meson8b_dwmac_data = { static const struct meson8b_dwmac_data meson8b_dwmac_data = {
.set_phy_mode = meson8b_set_phy_mode, .set_phy_mode = meson8b_set_phy_mode,
.has_prg_eth1_rgmii_rx_delay = false,
}; };
static const struct meson8b_dwmac_data meson_axg_dwmac_data = { static const struct meson8b_dwmac_data meson_axg_dwmac_data = {
.set_phy_mode = meson_axg_set_phy_mode, .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[] = { static const struct of_device_id meson8b_dwmac_match[] = {
...@@ -494,7 +529,7 @@ 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", .compatible = "amlogic,meson-g12a-dwmac",
.data = &meson_axg_dwmac_data, .data = &meson_g12a_dwmac_data,
}, },
{ } { }
}; };
......
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