Commit 4fda8601 authored by David S. Miller's avatar David S. Miller

Merge branch 'dwmac-meson8b-Ethernet-RX-delay-configuration'

Martin Blumenstingl says:

====================
dwmac-meson8b Ethernet RX delay configuration

The Ethernet TX performance has been historically bad on Meson8b and
Meson8m2 SoCs because high packet loss was seen. I found out that this
was related (yet again) to the RGMII TX delay configuration.
In the process of discussing the big picture (and not just a single
patch) [0] with Andrew I discovered that the IP block behind the
dwmac-meson8b driver actually seems to support the configuration of the
RGMII RX delay (at least on the Meson8b SoC generation).

Since I sent the first RFC I got additional documentation from Jianxin
(many thanks!). Also I have discovered some more interesting details:
- Meson8b Odroid-C1 requires an RX delay (by either the PHY or the MAC)
  Based on the vendor u-boot code (not upstream) I assume that it will
  be the same for all Meson8b and Meson8m2 boards
- Khadas VIM2 seems to have the RX delay built into the PCB trace
  length. When I enable the RX delay on the PHY or MAC I can't get any
  data through. I expect that we will have the same situation on all
  GXBB, GXM, AXG, G12A, G12B and SM1 boards. Further clarification is
  needed here though (since I can't visually see these lengthened
  traces on the PCB). This will be done before sending patches for
  these boards.

Dependencies for this series:
There is a soft dependency for patch #2 on commit f2253143
"dt-bindings: net: dwmac: increase 'maxItems' for 'clocks',
'clock-names' properties" which is currently in Rob's -next tree.
That commit is needed to make the dt-bindings schema validation
pass for patch #2. That patch has been for ~4 weeks in Robs tree,
so I assume that is not going to be dropped.

Changes since RFC v2 at [2]:
- dropped $ref: /schemas/types.yaml#definitions/uint32 from the
  "amlogic,rx-delay-ns" in patch #1 ("Don't need to define the
  type when in standard units." says Rob - thanks, I learned
  something new). Also use "default: 0" for for this property
  instead of explaining it in the description text.
- added a note to the cover-letter about a hidden dependency for
  dt-binding schema validation in patch #2
- Added Andrew's Reviewed-by to patches 1-7. Thank you again for
  the quick and detailed reviews, I appreciate this!
- error out if the (optional) timing-adjustment clock is missing
  but we're asked to enable the RGMII RX delay. The MAC won't
  work in this specific case and either the RX delay has to be
  provided by the PHY or the timing-adjustment clock has to be
  added.
- dropped the dts patches (#9-11) which were only added to give
  an overview how this is going to be used. those will be sent
  separately
- dropped the RFC prefix

Changes since RFC v1 at [1]:
- add support for the timing adjustment clock input (dt-bindings and
  in the driver) thanks to the input from the unnamed Ethernet engineer
  at Amlogic. This is the missing link between the fclk_div2 clock and
  the Ethernet controller on Meson8b (no traffic would flow if that
  clock was disabled)
- add support fot the amlogic,rx-delay-ns property. The only supported
  values so far are 0ns and 2ns. The registers seem to allow more
  precise timing adjustments, but I could not make that work so far.
- add more register documentation (for the new RX delay bits) and
  unified the placement of existing register documentation. Again,
  thanks to Jianxin and the unnamed Ethernet engineer at Amlogic
- DO NOT MERGE: .dts patches to show the conversion of the Meson8b
  and Meson8m2 boards to "rgmii-id". I didn't have time for all arm64
  patches yet, but these will switch to phy-mode = "rgmii-txid" with
  amlogic,rx-delay-ns = <0> (because the delay seems to be provided by
  the PCB trace length).

[0] https://patchwork.kernel.org/patch/11309891/
[1] https://patchwork.kernel.org/cover/11310719/
[2] https://patchwork.kernel.org/cover/11518257/
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6cd35888 9308c476
...@@ -40,18 +40,22 @@ allOf: ...@@ -40,18 +40,22 @@ allOf:
then: then:
properties: properties:
clocks: clocks:
minItems: 3
maxItems: 4
items: items:
- description: GMAC main clock - description: GMAC main clock
- description: First parent clock of the internal mux - description: First parent clock of the internal mux
- description: Second 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: clock-names:
minItems: 3 minItems: 3
maxItems: 3 maxItems: 4
items: items:
- const: stmmaceth - const: stmmaceth
- const: clkin0 - const: clkin0
- const: clkin1 - const: clkin1
- const: timing-adjustment
amlogic,tx-delay-ns: amlogic,tx-delay-ns:
$ref: /schemas/types.yaml#definitions/uint32 $ref: /schemas/types.yaml#definitions/uint32
...@@ -67,6 +71,19 @@ allOf: ...@@ -67,6 +71,19 @@ allOf:
PHY and MAC are adding a delay). PHY and MAC are adding a delay).
Any configuration is ignored when the phy-mode is set to "rmii". 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: properties:
compatible: compatible:
additionalItems: true additionalItems: true
...@@ -107,7 +124,7 @@ examples: ...@@ -107,7 +124,7 @@ examples:
reg = <0xc9410000 0x10000>, <0xc8834540 0x8>; reg = <0xc9410000 0x10000>, <0xc8834540 0x8>;
interrupts = <8>; interrupts = <8>;
interrupt-names = "macirq"; interrupt-names = "macirq";
clocks = <&clk_eth>, <&clkc_fclk_div2>, <&clk_mpll2>; clocks = <&clk_eth>, <&clk_fclk_div2>, <&clk_mpll2>, <&clk_fclk_div2>;
clock-names = "stmmaceth", "clkin0", "clkin1"; clock-names = "stmmaceth", "clkin0", "clkin1", "timing-adjustment";
phy-mode = "rgmii"; phy-mode = "rgmii";
}; };
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com> * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*/ */
#include <linux/bitfield.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -32,7 +33,10 @@ ...@@ -32,7 +33,10 @@
#define PRG_ETH0_CLK_M250_SEL_SHIFT 4 #define PRG_ETH0_CLK_M250_SEL_SHIFT 4
#define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4) #define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4)
#define PRG_ETH0_TXDLY_SHIFT 5 /* 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) #define PRG_ETH0_TXDLY_MASK GENMASK(6, 5)
/* divider for the result of m250_sel */ /* divider for the result of m250_sel */
...@@ -44,6 +48,27 @@ ...@@ -44,6 +48,27 @@
#define PRG_ETH0_INVERTED_RMII_CLK BIT(11) #define PRG_ETH0_INVERTED_RMII_CLK BIT(11)
#define PRG_ETH0_TX_AND_PHY_REF_CLK BIT(12) #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 #define MUX_CLK_NUM_PARENTS 2
struct meson8b_dwmac; struct meson8b_dwmac;
...@@ -60,6 +85,8 @@ struct meson8b_dwmac { ...@@ -60,6 +85,8 @@ struct meson8b_dwmac {
phy_interface_t phy_mode; phy_interface_t phy_mode;
struct clk *rgmii_tx_clk; struct clk *rgmii_tx_clk;
u32 tx_delay_ns; u32 tx_delay_ns;
u32 rx_delay_ns;
struct clk *timing_adj_clk;
}; };
struct meson8b_dwmac_clk_configs { struct meson8b_dwmac_clk_configs {
...@@ -240,30 +267,82 @@ static int meson_axg_set_phy_mode(struct meson8b_dwmac *dwmac) ...@@ -240,30 +267,82 @@ static int meson_axg_set_phy_mode(struct meson8b_dwmac *dwmac)
return 0; 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) static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
{ {
u32 tx_dly_config, rx_dly_config, delay_config;
int ret; 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) { switch (dwmac->phy_mode) {
case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII:
delay_config = tx_dly_config | rx_dly_config;
break;
case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_RXID:
/* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where delay_config = tx_dly_config;
* 8ns are exactly one cycle of the 125MHz RGMII TX clock): break;
* 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3
*/
tx_dly_val = dwmac->tx_delay_ns >> 1;
/* fall through */
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_TXID: 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 */ /* only relevant for RMII mode -> disable in RGMII mode */
meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
PRG_ETH0_INVERTED_RMII_CLK, 0); PRG_ETH0_INVERTED_RMII_CLK, 0);
meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
tx_dly_val << PRG_ETH0_TXDLY_SHIFT);
/* Configure the 125MHz RGMII TX clock, the IP block changes /* Configure the 125MHz RGMII TX clock, the IP block changes
* the output automatically (= without us having to configure * the output automatically (= without us having to configure
* a register) based on the line-speed (125MHz for Gbit speeds, * a register) based on the line-speed (125MHz for Gbit speeds,
...@@ -276,34 +355,18 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) ...@@ -276,34 +355,18 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
return ret; return ret;
} }
ret = clk_prepare_enable(dwmac->rgmii_tx_clk); ret = meson8b_devm_clk_prepare_enable(dwmac,
dwmac->rgmii_tx_clk);
if (ret) { if (ret) {
dev_err(dwmac->dev, dev_err(dwmac->dev,
"failed to enable the RGMII TX clock\n"); "failed to enable the RGMII TX clock\n");
return ret; return ret;
} }
} else {
devm_add_action_or_reset(dwmac->dev,
(void(*)(void *))clk_disable_unprepare,
dwmac->rgmii_tx_clk);
break;
case PHY_INTERFACE_MODE_RMII:
/* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */ /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
PRG_ETH0_INVERTED_RMII_CLK, PRG_ETH0_INVERTED_RMII_CLK,
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 */ /* enable TX_CLK and PHY_REF_CLK generator */
...@@ -358,6 +421,25 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) ...@@ -358,6 +421,25 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
&dwmac->tx_delay_ns)) &dwmac->tx_delay_ns))
dwmac->tx_delay_ns = 2; 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)) {
ret = PTR_ERR(dwmac->timing_adj_clk);
goto err_remove_config_dt;
}
ret = meson8b_init_rgmii_tx_clk(dwmac); ret = meson8b_init_rgmii_tx_clk(dwmac);
if (ret) if (ret)
goto err_remove_config_dt; goto err_remove_config_dt;
......
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