[PATCH] net: phy: dp83867: fix hfs boot in rgmii mode

From: Grygorii Strashko
Date: Wed Dec 04 2019 - 07:22:44 EST


The commit ef87f7da6b28 ("net: phy: dp83867: move dt parsing to probe")
causes regression on TI dra71x-evm and dra72x-evm, where DP83867 PHY is
used in "rgmii-id" mode - the networking stops working.
Unfortunately, it's not enough to just move DT parsing code to .probe() as
it depends on phydev->interface value, which is set to correct value abter
the .probe() is completed and before calling .config_init(). So, RGMII
configuration can't be loaded from DT.

To fix and issue
- move RGMII validation code to .config_init()
- parse RGMII parameters in dp83867_of_init(), but consider them as
optional.

Fixes: ef87f7da6b28 ("net: phy: dp83867: move dt parsing to probe")
Signed-off-by: Grygorii Strashko <grygorii.strashko@xxxxxx>
---
Hi All,

i'm very sorry for this regression. I've tested it on only one board and
was just lucky probably.
I still think it's better to DT parsing at .probe(),
but as second option - buggy patch can be reverted.

drivers/net/phy/dp83867.c | 117 +++++++++++++++++++++++---------------
1 file changed, 71 insertions(+), 46 deletions(-)

diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index 0b95e7a2e273..f3fc49a93245 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -101,8 +101,11 @@
/* RGMIIDCTL bits */
#define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf
#define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4
+#define DP83867_RGMII_TX_CLK_DELAY_INV (DP83867_RGMII_TX_CLK_DELAY_MAX + 1)
#define DP83867_RGMII_RX_CLK_DELAY_MAX 0xf
#define DP83867_RGMII_RX_CLK_DELAY_SHIFT 0
+#define DP83867_RGMII_RX_CLK_DELAY_INV (DP83867_RGMII_RX_CLK_DELAY_MAX + 1)
+

/* IO_MUX_CFG bits */
#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK 0x1f
@@ -294,6 +297,48 @@ static int dp83867_config_port_mirroring(struct phy_device *phydev)
return 0;
}

+static int dp83867_verify_rgmii_cfg(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867 = phydev->priv;
+
+ /* Existing behavior was to use default pin strapping delay in rgmii
+ * mode, but rgmii should have meant no delay. Warn existing users.
+ */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
+ const u16 val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_STRAP_STS2);
+ const u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >>
+ DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT;
+ const u16 rxskew = (val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK) >>
+ DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT;
+
+ if (txskew != DP83867_STRAP_STS2_CLK_SKEW_NONE ||
+ rxskew != DP83867_STRAP_STS2_CLK_SKEW_NONE)
+ phydev_warn(phydev,
+ "PHY has delays via pin strapping, but phy-mode = 'rgmii'\n"
+ "Should be 'rgmii-id' to use internal delays txskew:%x rxskew:%x\n",
+ txskew, rxskew);
+ }
+
+ /* RX delay *must* be specified if internal delay of RX is used. */
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) &&
+ dp83867->rx_id_delay == DP83867_RGMII_RX_CLK_DELAY_INV) {
+ phydev_err(phydev, "ti,rx-internal-delay must be specified\n");
+ return -EINVAL;
+ }
+
+ /* TX delay *must* be specified if internal delay of RX is used. */
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) &&
+ dp83867->tx_id_delay == DP83867_RGMII_TX_CLK_DELAY_INV) {
+ phydev_err(phydev, "ti,tx-internal-delay must be specified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_OF_MDIO
static int dp83867_of_init(struct phy_device *phydev)
{
@@ -335,55 +380,27 @@ static int dp83867_of_init(struct phy_device *phydev)
dp83867->sgmii_ref_clk_en = of_property_read_bool(of_node,
"ti,sgmii-ref-clock-output-enable");

- /* Existing behavior was to use default pin strapping delay in rgmii
- * mode, but rgmii should have meant no delay. Warn existing users.
- */
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
- const u16 val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS2);
- const u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >>
- DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT;
- const u16 rxskew = (val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK) >>
- DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT;
-
- if (txskew != DP83867_STRAP_STS2_CLK_SKEW_NONE ||
- rxskew != DP83867_STRAP_STS2_CLK_SKEW_NONE)
- phydev_warn(phydev,
- "PHY has delays via pin strapping, but phy-mode = 'rgmii'\n"
- "Should be 'rgmii-id' to use internal delays\n");
- }

/* RX delay *must* be specified if internal delay of RX is used. */
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
- phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
- ret = of_property_read_u32(of_node, "ti,rx-internal-delay",
- &dp83867->rx_id_delay);
- if (ret) {
- phydev_err(phydev, "ti,rx-internal-delay must be specified\n");
- return ret;
- }
- if (dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
- phydev_err(phydev,
- "ti,rx-internal-delay value of %u out of range\n",
- dp83867->rx_id_delay);
- return -EINVAL;
- }
+ dp83867->rx_id_delay = DP83867_RGMII_RX_CLK_DELAY_INV;
+ ret = of_property_read_u32(of_node, "ti,rx-internal-delay",
+ &dp83867->rx_id_delay);
+ if (!ret && dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
+ phydev_err(phydev,
+ "ti,rx-internal-delay value of %u out of range\n",
+ dp83867->rx_id_delay);
+ return -EINVAL;
}

/* TX delay *must* be specified if internal delay of RX is used. */
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
- phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
- ret = of_property_read_u32(of_node, "ti,tx-internal-delay",
- &dp83867->tx_id_delay);
- if (ret) {
- phydev_err(phydev, "ti,tx-internal-delay must be specified\n");
- return ret;
- }
- if (dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
- phydev_err(phydev,
- "ti,tx-internal-delay value of %u out of range\n",
- dp83867->tx_id_delay);
- return -EINVAL;
- }
+ dp83867->tx_id_delay = DP83867_RGMII_TX_CLK_DELAY_INV;
+ ret = of_property_read_u32(of_node, "ti,tx-internal-delay",
+ &dp83867->tx_id_delay);
+ if (!ret && dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
+ phydev_err(phydev,
+ "ti,tx-internal-delay value of %u out of range\n",
+ dp83867->tx_id_delay);
+ return -EINVAL;
}

if (of_property_read_bool(of_node, "enet-phy-lane-swap"))
@@ -434,6 +451,10 @@ static int dp83867_config_init(struct phy_device *phydev)
int ret, val, bs;
u16 delay;

+ ret = dp83867_verify_rgmii_cfg(phydev);
+ if (ret)
+ return ret;
+
/* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */
if (dp83867->rxctrl_strap_quirk)
phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
@@ -485,8 +506,12 @@ static int dp83867_config_init(struct phy_device *phydev)

phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val);

- delay = (dp83867->rx_id_delay |
- (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT));
+ delay = 0;
+ if (dp83867->rx_id_delay != DP83867_RGMII_RX_CLK_DELAY_INV)
+ delay |= dp83867->rx_id_delay;
+ if (dp83867->tx_id_delay != DP83867_RGMII_TX_CLK_DELAY_INV)
+ delay |= dp83867->tx_id_delay <<
+ DP83867_RGMII_TX_CLK_DELAY_SHIFT;

phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL,
delay);
--
2.17.1