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>; }; }; ðmac { status = "okay"; pinctrl-0 = <ð_pins>, <ð_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.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
7cd1de76c9
|
@ -74,17 +74,60 @@ allOf:
|
|||
Any configuration is ignored when the phy-mode is set to "rmii".
|
||||
|
||||
amlogic,rx-delay-ns:
|
||||
deprecated: true
|
||||
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".
|
||||
The internal RGMII RX clock delay in nanoseconds. Deprecated, use
|
||||
rx-internal-delay-ps instead.
|
||||
|
||||
rx-internal-delay-ps:
|
||||
default: 0
|
||||
|
||||
- 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:
|
||||
compatible:
|
||||
|
|
|
@ -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 {
|
||||
|
@ -82,7 +93,7 @@ struct meson8b_dwmac {
|
|||
phy_interface_t phy_mode;
|
||||
struct clk *rgmii_tx_clk;
|
||||
u32 tx_delay_ns;
|
||||
u32 rx_delay_ns;
|
||||
u32 rx_delay_ps;
|
||||
struct clk *timing_adj_clk;
|
||||
};
|
||||
|
||||
|
@ -268,32 +279,37 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac,
|
|||
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;
|
||||
|
||||
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_ns == 2)
|
||||
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",
|
||||
|
@ -301,7 +317,7 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rx_dly_config & PRG_ETH0_ADJ_ENABLE) {
|
||||
if (delay_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");
|
||||
|
@ -323,6 +339,16 @@ static int meson8b_init_prg_eth(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;
|
||||
}
|
||||
|
||||
static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
|
||||
{
|
||||
int ret;
|
||||
|
||||
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,
|
||||
|
@ -406,16 +432,30 @@ 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;
|
||||
/* RX delay defaults to 0ps since this is what many boards use */
|
||||
if (of_property_read_u32(pdev->dev.of_node, "rx-internal-delay-ps",
|
||||
&dwmac->rx_delay_ps)) {
|
||||
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) {
|
||||
dev_err(&pdev->dev,
|
||||
"The only allowed RX delays values are: 0ns, 2ns");
|
||||
ret = -EINVAL;
|
||||
goto err_remove_config_dt;
|
||||
if (dwmac->data->has_prg_eth1_rgmii_rx_delay) {
|
||||
if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) {
|
||||
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,
|
||||
|
@ -425,6 +465,10 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
|
|||
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);
|
||||
if (ret)
|
||||
goto err_remove_config_dt;
|
||||
|
@ -453,10 +497,17 @@ err_remove_config_dt:
|
|||
|
||||
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[] = {
|
||||
|
@ -478,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,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue