From 7af4c8451d80d0a8622483c27ab141a7c1a94573 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 12 May 2020 23:10:56 +0200 Subject: [PATCH 1/8] dt-bindings: net: meson-dwmac: Add the amlogic,rx-delay-ns property The PRG_ETHERNET registers on Meson8b and newer SoCs can add an RX delay. Add a property with the known supported values so it can be configured according to the board layout. Reviewed-by: Andrew Lunn Signed-off-by: Martin Blumenstingl Signed-off-by: David S. Miller --- .../bindings/net/amlogic,meson-dwmac.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml b/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml index ae91aa9d86164..66074314e57af 100644 --- a/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml @@ -67,6 +67,19 @@ allOf: PHY and MAC are adding a delay). Any configuration is ignored when the phy-mode is set to "rmii". + amlogic,rx-delay-ns: + enum: + - 0 + - 2 + default: 0 + description: + The internal RGMII RX clock delay (provided by this IP block) in + nanoseconds. When phy-mode is set to "rgmii" then the RX delay + should be explicitly configured. When the phy-mode is set to + either "rgmii-id" or "rgmii-rxid" the RX clock delay is already + provided by the PHY. Any configuration is ignored when the + phy-mode is set to "rmii". + properties: compatible: additionalItems: true From ee0b8e6d02186321be0ea4ec0fb2cbd35bec7e29 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 12 May 2020 23:10:57 +0200 Subject: [PATCH 2/8] dt-bindings: net: dwmac-meson: Document the "timing-adjustment" clock The PRG_ETHERNET registers can add an RX delay in RGMII mode. This requires an internal re-timing circuit whose input clock is called "timing adjustment clock". Document this clock input so the clock can be enabled as needed. Reviewed-by: Andrew Lunn Signed-off-by: Martin Blumenstingl Signed-off-by: David S. Miller --- .../devicetree/bindings/net/amlogic,meson-dwmac.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml b/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml index 66074314e57af..64c20c92c07dd 100644 --- a/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml @@ -40,18 +40,22 @@ allOf: then: properties: clocks: + minItems: 3 + maxItems: 4 items: - description: GMAC main clock - description: First parent clock of the internal mux - description: Second parent clock of the internal mux + - description: The clock which drives the timing adjustment logic clock-names: minItems: 3 - maxItems: 3 + maxItems: 4 items: - const: stmmaceth - const: clkin0 - const: clkin1 + - const: timing-adjustment amlogic,tx-delay-ns: $ref: /schemas/types.yaml#definitions/uint32 @@ -120,7 +124,7 @@ examples: reg = <0xc9410000 0x10000>, <0xc8834540 0x8>; interrupts = <8>; interrupt-names = "macirq"; - clocks = <&clk_eth>, <&clkc_fclk_div2>, <&clk_mpll2>; - clock-names = "stmmaceth", "clkin0", "clkin1"; + clocks = <&clk_eth>, <&clk_fclk_div2>, <&clk_mpll2>, <&clk_fclk_div2>; + clock-names = "stmmaceth", "clkin0", "clkin1", "timing-adjustment"; phy-mode = "rgmii"; }; From 3649abe43251de4357bdd6ef0163de25f96554e9 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 12 May 2020 23:10:58 +0200 Subject: [PATCH 3/8] net: stmmac: dwmac-meson8b: use FIELD_PREP instead of open-coding it Use FIELD_PREP() to shift a value to the correct offset based on a bitmask instead of open-coding the logic. No functional changes. Reviewed-by: Andrew Lunn Signed-off-by: Martin Blumenstingl Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index a3934ca6a043b..c9ec0cb68082f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -5,6 +5,7 @@ * Copyright (C) 2016 Martin Blumenstingl */ +#include #include #include #include @@ -32,7 +33,6 @@ #define PRG_ETH0_CLK_M250_SEL_SHIFT 4 #define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4) -#define PRG_ETH0_TXDLY_SHIFT 5 #define PRG_ETH0_TXDLY_MASK GENMASK(6, 5) /* divider for the result of m250_sel */ @@ -262,7 +262,8 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) PRG_ETH0_INVERTED_RMII_CLK, 0); meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK, - tx_dly_val << PRG_ETH0_TXDLY_SHIFT); + FIELD_PREP(PRG_ETH0_TXDLY_MASK, + tx_dly_val)); /* Configure the 125MHz RGMII TX clock, the IP block changes * the output automatically (= without us having to configure From 889df20305ffeae0a6bbd435761810ba658e223d Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 12 May 2020 23:10:59 +0200 Subject: [PATCH 4/8] net: stmmac: dwmac-meson8b: Move the documentation for the TX delay Move the documentation for the TX delay above the PRG_ETH0_TXDLY_MASK definition. Future commits will add more registers also with documentation above their register bit definitions. Move the existing comment so it will be consistent with the upcoming changes. Reviewed-by: Andrew Lunn Signed-off-by: Martin Blumenstingl Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index c9ec0cb68082f..1d7526ee09dde 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -33,6 +33,10 @@ #define PRG_ETH0_CLK_M250_SEL_SHIFT 4 #define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4) +/* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where 8ns are exactly one + * cycle of the 125MHz RGMII TX clock): + * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3 + */ #define PRG_ETH0_TXDLY_MASK GENMASK(6, 5) /* divider for the result of m250_sel */ @@ -248,10 +252,6 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) switch (dwmac->phy_mode) { case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_RXID: - /* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where - * 8ns are exactly one cycle of the 125MHz RGMII TX clock): - * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3 - */ tx_dly_val = dwmac->tx_delay_ns >> 1; /* fall through */ From c92d1d2311a0513d8f7f8311f5c2b1d7e78005a0 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 12 May 2020 23:11:00 +0200 Subject: [PATCH 5/8] net: stmmac: dwmac-meson8b: Add the PRG_ETH0_ADJ_* bits The PRG_ETH0_ADJ_* are used for applying the RGMII RX delay. The public datasheets only have very limited description for these registers, but Jianxin Pan provided more detailed documentation from an (unnamed) Amlogic engineer. Add the PRG_ETH0_ADJ_* bits along with the improved description. Suggested-by: Jianxin Pan Reviewed-by: Andrew Lunn Signed-off-by: Martin Blumenstingl Signed-off-by: David S. Miller --- .../ethernet/stmicro/stmmac/dwmac-meson8b.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index 1d7526ee09dde..70075628c58eb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -48,6 +48,27 @@ #define PRG_ETH0_INVERTED_RMII_CLK BIT(11) #define PRG_ETH0_TX_AND_PHY_REF_CLK BIT(12) +/* Bypass (= 0, the signal from the GPIO input directly connects to the + * internal sampling) or enable (= 1) the internal logic for RXEN and RXD[3:0] + * timing tuning. + */ +#define PRG_ETH0_ADJ_ENABLE BIT(13) +/* Controls whether the RXEN and RXD[3:0] signals should be aligned with the + * input RX rising/falling edge and sent to the Ethernet internals. This sets + * the automatically delay and skew automatically (internally). + */ +#define PRG_ETH0_ADJ_SETUP BIT(14) +/* An internal counter based on the "timing-adjustment" clock. The counter is + * cleared on both, the falling and rising edge of the RX_CLK. This selects the + * delay (= the counter value) when to start sampling RXEN and RXD[3:0]. + */ +#define PRG_ETH0_ADJ_DELAY GENMASK(19, 15) +/* Adjusts the skew between each bit of RXEN and RXD[3:0]. If a signal has a + * large input delay, the bit for that signal (RXEN = bit 0, RXD[3] = bit 1, + * ...) can be configured to be 1 to compensate for a delay of about 1ns. + */ +#define PRG_ETH0_ADJ_SKEW GENMASK(24, 20) + #define MUX_CLK_NUM_PARENTS 2 struct meson8b_dwmac; From e4227bff804fc77e2f78c77470d3fbd2d4a6a8d0 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 12 May 2020 23:11:01 +0200 Subject: [PATCH 6/8] net: stmmac: dwmac-meson8b: Fetch the "timing-adjustment" clock The PRG_ETHERNET registers have a built-in timing adjustment circuit which can provide the RX delay in RGMII mode. This is driven by an external (to this IP, but internal to the SoC) clock input. Fetch this clock as optional (even though it's there on all supported SoCs) since we just learned about it and existing .dtbs don't specify it. Reviewed-by: Andrew Lunn Signed-off-by: Martin Blumenstingl Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index 70075628c58eb..41f3ef6bea660 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -85,6 +85,7 @@ struct meson8b_dwmac { phy_interface_t phy_mode; struct clk *rgmii_tx_clk; u32 tx_delay_ns; + struct clk *timing_adj_clk; }; struct meson8b_dwmac_clk_configs { @@ -380,6 +381,13 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) &dwmac->tx_delay_ns)) dwmac->tx_delay_ns = 2; + dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev, + "timing-adjustment"); + if (IS_ERR(dwmac->timing_adj_clk)) { + ret = PTR_ERR(dwmac->timing_adj_clk); + goto err_remove_config_dt; + } + ret = meson8b_init_rgmii_tx_clk(dwmac); if (ret) goto err_remove_config_dt; From a54dc4a4904568fe2c6b2ba249dcc97612affebb Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 12 May 2020 23:11:02 +0200 Subject: [PATCH 7/8] net: stmmac: dwmac-meson8b: Make the clock enabling code re-usable The timing adjustment clock will need similar logic as the RGMII clock: It has to be enabled in the driver conditionally and when the driver is unloaded it should be disabled again. Extract the existing code for the RGMII clock into a new function so it can be re-used. Reviewed-by: Andrew Lunn Signed-off-by: Martin Blumenstingl Signed-off-by: David S. Miller --- .../ethernet/stmicro/stmmac/dwmac-meson8b.c | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index 41f3ef6bea660..d31f79c455dec 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -266,6 +266,22 @@ static int meson_axg_set_phy_mode(struct meson8b_dwmac *dwmac) return 0; } +static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac, + struct clk *clk) +{ + int ret; + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + devm_add_action_or_reset(dwmac->dev, + (void(*)(void *))clk_disable_unprepare, + dwmac->rgmii_tx_clk); + + return 0; +} + static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) { int ret; @@ -299,16 +315,13 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) return ret; } - ret = clk_prepare_enable(dwmac->rgmii_tx_clk); + ret = meson8b_devm_clk_prepare_enable(dwmac, + dwmac->rgmii_tx_clk); if (ret) { dev_err(dwmac->dev, "failed to enable the RGMII TX clock\n"); return ret; } - - devm_add_action_or_reset(dwmac->dev, - (void(*)(void *))clk_disable_unprepare, - dwmac->rgmii_tx_clk); break; case PHY_INTERFACE_MODE_RMII: From 9308c47640d515d16e06a7fdf333c51a39c1b0b1 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 12 May 2020 23:11:03 +0200 Subject: [PATCH 8/8] net: stmmac: dwmac-meson8b: add support for the RX delay configuration Configure the PRG_ETH0_ADJ_* bits to enable or disable the RX delay based on the various RGMII PHY modes. For now the only supported RX delay settings are: - disabled, use for example for phy-mode "rgmii-id" - 0ns - this is treated identical to "disabled", used for example on boards where the PHY provides 2ns TX delay and the PCB trace length already adds 2ns RX delay - 2ns - for whenever the PHY cannot add the RX delay and the traces on the PCB don't add any RX delay Disabling the RX delay (in case u-boot enables it, which is the case for example on Meson8b Odroid-C1) simply means that PRG_ETH0_ADJ_ENABLE, PRG_ETH0_ADJ_SETUP, PRG_ETH0_ADJ_DELAY and PRG_ETH0_ADJ_SKEW should be disabled (just disabling PRG_ETH0_ADJ_ENABLE may be enough, since that disables the whole re-timing logic - but I find it makes more sense to clear the other bits as well since they depend on that setting). u-boot on Odroid-C1 uses the following steps to enable a 2ns RX delay: - enabling enabling the timing adjustment clock - enabling the timing adjustment logic by setting PRG_ETH0_ADJ_ENABLE - setting the PRG_ETH0_ADJ_SETUP bit The documentation for the PRG_ETH0_ADJ_DELAY and PRG_ETH0_ADJ_SKEW registers indicates that we can even set different RX delays. However, I could not find out how this works exactly, so for now we only support a 2ns RX delay using the exact same way that Odroid-C1's u-boot does. Signed-off-by: Martin Blumenstingl Signed-off-by: David S. Miller --- .../ethernet/stmicro/stmmac/dwmac-meson8b.c | 85 ++++++++++++++----- 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index d31f79c455dec..234e8b6816ce8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -85,6 +85,7 @@ struct meson8b_dwmac { phy_interface_t phy_mode; struct clk *rgmii_tx_clk; u32 tx_delay_ns; + u32 rx_delay_ns; struct clk *timing_adj_clk; }; @@ -284,25 +285,64 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac, static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) { + u32 tx_dly_config, rx_dly_config, delay_config; int ret; - u8 tx_dly_val = 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; switch (dwmac->phy_mode) { case PHY_INTERFACE_MODE_RGMII: + delay_config = tx_dly_config | rx_dly_config; + break; case PHY_INTERFACE_MODE_RGMII_RXID: - tx_dly_val = dwmac->tx_delay_ns >> 1; - /* fall through */ - - case PHY_INTERFACE_MODE_RGMII_ID: + delay_config = tx_dly_config; + break; case PHY_INTERFACE_MODE_RGMII_TXID: + delay_config = rx_dly_config; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RMII: + delay_config = 0; + break; + default: + dev_err(dwmac->dev, "unsupported phy-mode %s\n", + phy_modes(dwmac->phy_mode)); + return -EINVAL; + }; + + if (rx_dly_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"); + return -EINVAL; + } + + /* The timing adjustment logic is driven by a separate clock */ + ret = meson8b_devm_clk_prepare_enable(dwmac, + dwmac->timing_adj_clk); + if (ret) { + dev_err(dwmac->dev, + "Failed to enable the timing-adjustment clock\n"); + return ret; + } + } + + meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK | + PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP | + PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW, + delay_config); + + 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, PRG_ETH0_INVERTED_RMII_CLK, 0); - meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK, - FIELD_PREP(PRG_ETH0_TXDLY_MASK, - tx_dly_val)); - /* Configure the 125MHz RGMII TX clock, the IP block changes * the output automatically (= without us having to configure * a register) based on the line-speed (125MHz for Gbit speeds, @@ -322,24 +362,11 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) "failed to enable the RGMII TX clock\n"); return ret; } - break; - - case PHY_INTERFACE_MODE_RMII: + } else { /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */ meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_INVERTED_RMII_CLK, PRG_ETH0_INVERTED_RMII_CLK); - - /* TX clock delay cannot be configured in RMII mode */ - meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK, - 0); - - break; - - default: - dev_err(dwmac->dev, "unsupported phy-mode %s\n", - phy_modes(dwmac->phy_mode)); - return -EINVAL; } /* enable TX_CLK and PHY_REF_CLK generator */ @@ -394,6 +421,18 @@ 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; + + 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; + } + dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev, "timing-adjustment"); if (IS_ERR(dwmac->timing_adj_clk)) {