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

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
Loading
Loading
Loading
Loading
+48 −13
Original line number Diff line number Diff line
@@ -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 {
@@ -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,12 +442,21 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
			dwmac->rx_delay_ps *= 1000;
	}

	if (dwmac->data->has_prg_eth1_rgmii_rx_delay) {
		if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) {
		dev_err(&pdev->dev,
			"The only allowed RX delays values are: 0ps, 2000ps");
			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,
						      "timing-adjustment");
@@ -469,10 +497,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[] = {
@@ -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,
	},
	{ }
};