Commit 7cd1de76 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'dwmac-meson8b-picosecond-precision-rx-delay-support'

Martin Blumenstingl says:

====================
dwmac-meson8b: picosecond precision RX delay support

with the help of Jianxin Pan (many thanks!) the meaning of the "new"
PRG_ETH1[19:16] register bits on Amlogic Meson G12A, G12B and SM1 SoCs
are finally known. These SoCs allow fine-tuning the RGMII RX delay in
200ps steps (contrary to what I have thought in the past [0] these are
not some "calibration" values).

The vendor u-boot has code to automatically detect the best RX/TX delay
settings. For now we keep it simple and add a device-tree property with
200ps precision to select the "right" RX delay for each board.

While here, deprecate the "amlogic,rx-delay-ns" property as it's not
used on any upstream .dts (yet). The driver is backwards compatible.

I have tested this on an X96 Air 4GB board (not upstream yet). Testing
with iperf3 gives 938 Mbits/sec in both directions (RX and TX). The
following network settings were used in the .dts (2ns TX delay
generated by the PHY, 800ps RX delay generated by the MAC as the PHY
only supports 0ns or 2ns RX delays):
        &ext_mdio {
                external_phy: ethernet-phy@0 {
                        /* Realtek RTL8211F (0x001cc916) */
                        reg = <0>;
                        eee-broken-1000t;

                        reset-assert-us = <10000>;
                        reset-deassert-us = <30000>;
                        reset-gpios = <&gpio GPIOZ_15 (GPIO_ACTIVE_LOW |
                                                GPIO_OPEN_DRAIN)>;

                        interrupt-parent = <&gpio_intc>;
                        /* MAC_INTR on GPIOZ_14 */
                        interrupts = <26 IRQ_TYPE_LEVEL_LOW>;
                };
        };

        &ethmac {
                status = "okay";

                pinctrl-0 = <&eth_pins>, <&eth_rgmii_pins>;
                pinctrl-names = "default";

                phy-mode = "rgmii-txid";
                phy-handle = <&external_phy>;

                amlogic,rgmii-rx-delay-ps = <800>;
        };

To use the same settings from vendor u-boot (which in my case has broken
Ethernet) the following commands can be used:
  mw.l 0xff634540 0x1621
  mw.l 0xff634544 0x30000
  phyreg w 0x0 0x1040
  phyreg w 0x1f 0xd08
  phyreg w 0x11 0x9
  phyreg w 0x15 0x11
  phyreg w 0x1f 0x0
  phyreg w 0x0 0x9200

Also I have tested this on a X96 Max board without any .dts changes
to confirm that other boards with the same IP block still work fine
with these changes.

Changes since v3 at [3].
- added Florian's Reviewed-by to patch 1 (thank you!)
- rebased on top of net-next

Changes since v2 at [2]:
- use the generic property name "rx-internal-delay-ps" as suggested by
  Rob (thanks!). This affects patches #1 and #3. The biggest change is
  is in patch #1 which is why I didn't add Florian's and Andrew's
  Reviewed-by
- added Andrew's and Florian's Reviewed-by to patches 2, 3, 4, 5 (many
  thanks to both!). I decided to do this despite renaming the property
  to the generic name "rx-internal-delay-ps" as it only affects the
  patch description and one line of code
- updated patch description of patch #3 to explain why there's not a
  lot of validation when parsing the old device-tree property (in
  nanosecond precision)
- dropped RFC status

Changes since v1 at [1]:
- updated patch 1 by making it more clear when the RX delay is applied.
  Thanks to Andrew for the suggestion!
- added a fix to enabling the timing-adjustment clock only when really
  needed. Found by Andrew - thanks!
- added testing not about X96 Max
- v1 did not go to the netdev mailing list, v2 fixes this

[0] https://lore.kernel.org/netdev/CAFBinCATt4Hi9rigj52nMf3oygyFbnopZcsakGL=KyWnsjY3JA@mail.gmail.com/
[1] https://patchwork.kernel.org/project/linux-amlogic/list/?series=384279&state=%2A&archive=both
[2] https://patchwork.kernel.org/project/linux-amlogic/list/?series=384491&state=%2A&archive=both
[3] https://patchwork.kernel.org/project/linux-amlogic/list/?series=406005&state=%2A&archive=both
====================

Link: https://lore.kernel.org/r/20210106134251.45264-1-martin.blumenstingl@googlemail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 85b277de de94fc10
...@@ -74,17 +74,60 @@ allOf: ...@@ -74,17 +74,60 @@ allOf:
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: amlogic,rx-delay-ns:
deprecated: true
enum: enum:
- 0 - 0
- 2 - 2
default: 0 default: 0
description: description:
The internal RGMII RX clock delay (provided by this IP block) in The internal RGMII RX clock delay in nanoseconds. Deprecated, use
nanoseconds. When phy-mode is set to "rgmii" then the RX delay rx-internal-delay-ps instead.
should be explicitly configured. When the phy-mode is set to
either "rgmii-id" or "rgmii-rxid" the RX clock delay is already rx-internal-delay-ps:
provided by the PHY. Any configuration is ignored when the default: 0
phy-mode is set to "rmii".
- if:
properties:
compatible:
contains:
enum:
- amlogic,meson8b-dwmac
- amlogic,meson8m2-dwmac
- amlogic,meson-gxbb-dwmac
- amlogic,meson-axg-dwmac
then:
properties:
rx-internal-delay-ps:
enum:
- 0
- 2000
- if:
properties:
compatible:
contains:
enum:
- amlogic,meson-g12a-dwmac
then:
properties:
rx-internal-delay-ps:
enum:
- 0
- 200
- 400
- 600
- 800
- 1000
- 1200
- 1400
- 1600
- 1800
- 2000
- 2200
- 2400
- 2600
- 2800
- 3000
properties: properties:
compatible: compatible:
......
...@@ -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 {
...@@ -82,7 +93,7 @@ struct meson8b_dwmac { ...@@ -82,7 +93,7 @@ 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; u32 rx_delay_ps;
struct clk *timing_adj_clk; struct clk *timing_adj_clk;
}; };
...@@ -268,32 +279,37 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac, ...@@ -268,32 +279,37 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac,
return 0; 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; 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_ns == 2) 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",
...@@ -301,7 +317,7 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) ...@@ -301,7 +317,7 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
return -EINVAL; return -EINVAL;
} }
if (rx_dly_config & PRG_ETH0_ADJ_ENABLE) { if (delay_config & PRG_ETH0_ADJ_ENABLE) {
if (!dwmac->timing_adj_clk) { if (!dwmac->timing_adj_clk) {
dev_err(dwmac->dev, dev_err(dwmac->dev,
"The timing-adjustment clock is mandatory for the RX delay re-timing\n"); "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) ...@@ -323,6 +339,16 @@ static int meson8b_init_prg_eth(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;
}
static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
{
int ret;
if (phy_interface_mode_is_rgmii(dwmac->phy_mode)) { 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,
...@@ -406,17 +432,31 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) ...@@ -406,17 +432,31 @@ 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 */ /* RX delay defaults to 0ps since this is what many boards use */
if (of_property_read_u32(pdev->dev.of_node, "amlogic,rx-delay-ns", if (of_property_read_u32(pdev->dev.of_node, "rx-internal-delay-ps",
&dwmac->rx_delay_ns)) &dwmac->rx_delay_ps)) {
dwmac->rx_delay_ns = 0; 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) { 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: 0ns, 2ns"); dev_err(dwmac->dev,
"The only allowed RGMII RX delays values are: 0ps, 2000ps");
ret = -EINVAL; ret = -EINVAL;
goto err_remove_config_dt; 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,
"timing-adjustment"); "timing-adjustment");
...@@ -425,6 +465,10 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) ...@@ -425,6 +465,10 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
goto err_remove_config_dt; 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); ret = meson8b_init_rgmii_tx_clk(dwmac);
if (ret) if (ret)
goto err_remove_config_dt; goto err_remove_config_dt;
...@@ -453,10 +497,17 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) ...@@ -453,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[] = {
...@@ -478,7 +529,7 @@ 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", .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