[PATCH 5.4 29/56] net: phy: bcm7xxx: request and manage GPHY clock

From: Greg Kroah-Hartman
Date: Mon Oct 04 2021 - 09:17:13 EST


From: Florian Fainelli <f.fainelli@xxxxxxxxx>

[ Upstream commit ba4ee3c053659119472135231dbef8f6880ce1fb ]

The internal Gigabit PHY on Broadcom STB chips has a digital clock which
drives its MDIO interface among other things, the driver now requests
and manage that clock during .probe() and .remove() accordingly.

Because the PHY driver can be probed with the clocks turned off we need
to apply the dummy BMSR workaround during the driver probe function to
ensure subsequent MDIO read or write towards the PHY will succeed.

Signed-off-by: Florian Fainelli <f.fainelli@xxxxxxxxx>
Reviewed-by: Andrew Lunn <andrew@xxxxxxx>
Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
drivers/net/phy/bcm7xxx.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index af8eabe7a6d4..e68dd2e9443c 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -11,6 +11,7 @@
#include "bcm-phy-lib.h"
#include <linux/bitops.h>
#include <linux/brcmphy.h>
+#include <linux/clk.h>
#include <linux/mdio.h>

/* Broadcom BCM7xxx internal PHY registers */
@@ -39,6 +40,7 @@

struct bcm7xxx_phy_priv {
u64 *stats;
+ struct clk *clk;
};

static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
@@ -517,6 +519,7 @@ static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev,
static int bcm7xxx_28nm_probe(struct phy_device *phydev)
{
struct bcm7xxx_phy_priv *priv;
+ int ret = 0;

priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -530,7 +533,30 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev)
if (!priv->stats)
return -ENOMEM;

- return 0;
+ priv->clk = devm_clk_get_optional(&phydev->mdio.dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ /* Dummy read to a register to workaround an issue upon reset where the
+ * internal inverter may not allow the first MDIO transaction to pass
+ * the MDIO management controller and make us return 0xffff for such
+ * reads. This is needed to ensure that any subsequent reads to the
+ * PHY will succeed.
+ */
+ phy_read(phydev, MII_BMSR);
+
+ return ret;
+}
+
+static void bcm7xxx_28nm_remove(struct phy_device *phydev)
+{
+ struct bcm7xxx_phy_priv *priv = phydev->priv;
+
+ clk_disable_unprepare(priv->clk);
}

#define BCM7XXX_28NM_GPHY(_oui, _name) \
@@ -548,6 +574,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev)
.get_strings = bcm_phy_get_strings, \
.get_stats = bcm7xxx_28nm_get_phy_stats, \
.probe = bcm7xxx_28nm_probe, \
+ .remove = bcm7xxx_28nm_remove, \
}

#define BCM7XXX_28NM_EPHY(_oui, _name) \
@@ -563,6 +590,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev)
.get_strings = bcm_phy_get_strings, \
.get_stats = bcm7xxx_28nm_get_phy_stats, \
.probe = bcm7xxx_28nm_probe, \
+ .remove = bcm7xxx_28nm_remove, \
}

#define BCM7XXX_40NM_EPHY(_oui, _name) \
--
2.33.0