diff mbox series

[v1,6/6] net: mv88e61xx: Reset switch PHYs when bootstrapped to !NO_CPU

Message ID 20210317141410.32152-7-lukma@denx.de
State Accepted
Delegated to: Ramon Fried
Headers show
Series Provide support for mv88e6020 Marvell switch | expand

Commit Message

Lukasz Majewski March 17, 2021, 2:14 p.m. UTC
Some devices, when configured in bootstrap to 'no cpu' mode require PHY
manual reset to get them operational and responding to reading their ID
registers.

Without this step - the PHYLIB probing will fail.

In more details - the bootstrap configuration from switch must be read.
The value of CONFIG Data1 (0x71) of Scratch and Misc register is read
to check if 'no_cpu' and 'addr4' bits were set.

Signed-off-by: Lukasz Majewski <lukma@denx.de>

---

 drivers/net/phy/mv88e61xx.c | 63 +++++++++++++++++++++++++++++++++++--
 1 file changed, 61 insertions(+), 2 deletions(-)

Comments

Ramon Fried May 8, 2021, 6:26 a.m. UTC | #1
On Wed, Mar 17, 2021 at 4:14 PM Lukasz Majewski <lukma@denx.de> wrote:
>
> Some devices, when configured in bootstrap to 'no cpu' mode require PHY
> manual reset to get them operational and responding to reading their ID
> registers.
>
> Without this step - the PHYLIB probing will fail.
>
> In more details - the bootstrap configuration from switch must be read.
> The value of CONFIG Data1 (0x71) of Scratch and Misc register is read
> to check if 'no_cpu' and 'addr4' bits were set.
>
> Signed-off-by: Lukasz Majewski <lukma@denx.de>
>
> ---
>
>  drivers/net/phy/mv88e61xx.c | 63 +++++++++++++++++++++++++++++++++++--
>  1 file changed, 61 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/phy/mv88e61xx.c b/drivers/net/phy/mv88e61xx.c
> index 325d5b56135f..1fa821ca162b 100644
> --- a/drivers/net/phy/mv88e61xx.c
> +++ b/drivers/net/phy/mv88e61xx.c
> @@ -202,6 +202,17 @@ struct mv88e61xx_phy_priv {
>         u8 phy_ctrl1_en_det_width; /* Width of 'EDet' bit field */
>         u8 phy_ctrl1_en_det_ctrl;  /* 'EDet' control value */
>         u8 direct_access;          /* Access switch device directly */
> +       /*
> +        * Bootstrap configuration:
> +        *
> +        * If addr4 = 1 device is accessible from 0x10 address on MDIO bus.
> +        */
> +       u8 addr4;
> +       /*
> +        * If no_cpu = 1 switch is automatically setup, otherwise PHY reset is
> +        * required from CPU for normal operation.
> +        */
> +       u8 no_cpu;
>  };
>
>  static inline int smi_cmd(int cmd, int addr, int reg)
> @@ -1235,6 +1246,33 @@ int phy_mv88e61xx_init(void)
>         return 0;
>  }
>
> +static int mv88e61xx_read_bootstrap(struct phy_device *phydev)
> +{
> +       struct mv88e61xx_phy_priv *priv = phydev->priv;
> +       struct mii_dev *mdio_bus = priv->mdio_bus;
> +       int val;
> +
> +       /* mv88e6020 - ID = 0x0200 (REG 3 on non PHY port) */
> +       if (priv->id == PORT_SWITCH_ID_6020) {
> +               /* Prepare to read scratch and misc register */
> +               mdio_bus->write(mdio_bus, priv->global2, 0,
> +                               0x1a /*MV_SCRATCH_MISC*/,
> +                               (0x71 /*MV_CONFIG_DATA1*/ << 8));
> +
> +               val = mdio_bus->read(mdio_bus, priv->global2, 0,
> +                                    0x1a /*MV_SCRATCH_MISC*/);
> +
> +               if (val & (1 << 0))
> +                       priv->no_cpu = 1;
> +               if (val & (1 << 4))
> +                       priv->addr4 = 1;
> +               debug("mv88e6020: no_cpu=%d addr4=%d\n", priv->no_cpu,
> +                     priv->addr4);
> +       }
> +
> +       return 0;
> +}
> +
>  /*
>   * Overload weak get_phy_id definition since we need non-standard functions
>   * to read PHY registers
> @@ -1274,13 +1312,34 @@ int get_phy_id(struct mii_dev *bus, int smi_addr, int devad, u32 *phy_id)
>         if (val < 0)
>                 return val;
>
> -       val = mv88e61xx_phy_read_indirect(&temp_mii, 0, devad, MII_PHYSID1);
> +       mv88e61xx_read_bootstrap(&temp_phy);
> +
> +       /*
> +        * When switch is configured to work with CPU (i.e. NO_CPU == 0), PHYs
> +        * require reset (to at least single one) to have its registers
> +        * accessible.
> +        */
> +       if (!temp_priv.no_cpu && temp_priv.id == PORT_SWITCH_ID_6020) {
> +               /* Reset PHY */
> +               val = mv88e61xx_phy_read_indirect(&temp_mii, temp_phy.addr,
> +                                                 devad, MII_BMCR);
> +               if (val & BMCR_PDOWN)
> +                       val &= ~BMCR_PDOWN;
> +
> +               mv88e61xx_phy_write_indirect(&temp_mii, temp_phy.addr, devad,
> +                                            MII_BMCR, val);
> +       }
> +
> +       /* Read PHY_ID */
> +       val = mv88e61xx_phy_read_indirect(&temp_mii, temp_phy.addr, devad,
> +                                         MII_PHYSID1);
>         if (val < 0)
>                 return -EIO;
>
>         *phy_id = val << 16;
>
> -       val = mv88e61xx_phy_read_indirect(&temp_mii, 0, devad, MII_PHYSID2);
> +       val = mv88e61xx_phy_read_indirect(&temp_mii, temp_phy.addr, devad,
> +                                         MII_PHYSID2);
>         if (val < 0)
>                 return -EIO;
>
> --
> 2.20.1
>
Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
diff mbox series

Patch

diff --git a/drivers/net/phy/mv88e61xx.c b/drivers/net/phy/mv88e61xx.c
index 325d5b56135f..1fa821ca162b 100644
--- a/drivers/net/phy/mv88e61xx.c
+++ b/drivers/net/phy/mv88e61xx.c
@@ -202,6 +202,17 @@  struct mv88e61xx_phy_priv {
 	u8 phy_ctrl1_en_det_width; /* Width of 'EDet' bit field */
 	u8 phy_ctrl1_en_det_ctrl;  /* 'EDet' control value */
 	u8 direct_access;          /* Access switch device directly */
+	/*
+	 * Bootstrap configuration:
+	 *
+	 * If addr4 = 1 device is accessible from 0x10 address on MDIO bus.
+	 */
+	u8 addr4;
+	/*
+	 * If no_cpu = 1 switch is automatically setup, otherwise PHY reset is
+	 * required from CPU for normal operation.
+	 */
+	u8 no_cpu;
 };
 
 static inline int smi_cmd(int cmd, int addr, int reg)
@@ -1235,6 +1246,33 @@  int phy_mv88e61xx_init(void)
 	return 0;
 }
 
+static int mv88e61xx_read_bootstrap(struct phy_device *phydev)
+{
+	struct mv88e61xx_phy_priv *priv = phydev->priv;
+	struct mii_dev *mdio_bus = priv->mdio_bus;
+	int val;
+
+	/* mv88e6020 - ID = 0x0200 (REG 3 on non PHY port) */
+	if (priv->id == PORT_SWITCH_ID_6020) {
+		/* Prepare to read scratch and misc register */
+		mdio_bus->write(mdio_bus, priv->global2, 0,
+				0x1a /*MV_SCRATCH_MISC*/,
+				(0x71 /*MV_CONFIG_DATA1*/ << 8));
+
+		val = mdio_bus->read(mdio_bus, priv->global2, 0,
+				     0x1a /*MV_SCRATCH_MISC*/);
+
+		if (val & (1 << 0))
+			priv->no_cpu = 1;
+		if (val & (1 << 4))
+			priv->addr4 = 1;
+		debug("mv88e6020: no_cpu=%d addr4=%d\n", priv->no_cpu,
+		      priv->addr4);
+	}
+
+	return 0;
+}
+
 /*
  * Overload weak get_phy_id definition since we need non-standard functions
  * to read PHY registers
@@ -1274,13 +1312,34 @@  int get_phy_id(struct mii_dev *bus, int smi_addr, int devad, u32 *phy_id)
 	if (val < 0)
 		return val;
 
-	val = mv88e61xx_phy_read_indirect(&temp_mii, 0, devad, MII_PHYSID1);
+	mv88e61xx_read_bootstrap(&temp_phy);
+
+	/*
+	 * When switch is configured to work with CPU (i.e. NO_CPU == 0), PHYs
+	 * require reset (to at least single one) to have its registers
+	 * accessible.
+	 */
+	if (!temp_priv.no_cpu && temp_priv.id == PORT_SWITCH_ID_6020) {
+		/* Reset PHY */
+		val = mv88e61xx_phy_read_indirect(&temp_mii, temp_phy.addr,
+						  devad, MII_BMCR);
+		if (val & BMCR_PDOWN)
+			val &= ~BMCR_PDOWN;
+
+		mv88e61xx_phy_write_indirect(&temp_mii, temp_phy.addr, devad,
+					     MII_BMCR, val);
+	}
+
+	/* Read PHY_ID */
+	val = mv88e61xx_phy_read_indirect(&temp_mii, temp_phy.addr, devad,
+					  MII_PHYSID1);
 	if (val < 0)
 		return -EIO;
 
 	*phy_id = val << 16;
 
-	val = mv88e61xx_phy_read_indirect(&temp_mii, 0, devad, MII_PHYSID2);
+	val = mv88e61xx_phy_read_indirect(&temp_mii, temp_phy.addr, devad,
+					  MII_PHYSID2);
 	if (val < 0)
 		return -EIO;