[PATCH v1 2/2] net: phy: micrel: port clock select support
Oleksij Rempel
o.rempel at pengutronix.de
Tue Oct 12 03:08:59 PDT 2021
Port devicetree based clock select support from kernel micrel driver (v5.15-rc1).
This support is needed to make netboot work on boards with PHY node and
"rmii-ref" property.
Signed-off-by: Oleksij Rempel <o.rempel at pengutronix.de>
---
drivers/net/phy/micrel.c | 101 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 100 insertions(+), 1 deletion(-)
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index ea193c84a7..958abdd9d6 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -14,6 +14,7 @@
#include <common.h>
#include <init.h>
+#include <linux/clk.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
@@ -38,6 +39,10 @@
/* PHY Control 1 */
#define MII_KSZPHY_CTRL_1 0x1e
+/* PHY Control 2 / PHY Control (if no PHY Control 1) */
+#define MII_KSZPHY_CTRL_2 0x1f
+#define KSZPHY_RMII_REF_CLK_SEL BIT(7)
+
/* Write/read to/from extended registers */
#define MII_KSZPHY_EXTREG 0x0b
#define KSZPHY_EXTREG_WRITE 0x8000
@@ -52,6 +57,20 @@
#define PS_TO_REG 200
+struct kszphy_type {
+ bool has_rmii_ref_clk_sel;
+};
+
+struct kszphy_priv {
+ const struct kszphy_type *type;
+ bool rmii_ref_clk_sel;
+ bool rmii_ref_clk_sel_val;
+};
+
+static const struct kszphy_type ksz8081_type = {
+ .has_rmii_ref_clk_sel = true,
+};
+
static int kszphy_extended_write(struct phy_device *phydev,
u32 regnum, u16 val)
{
@@ -66,6 +85,22 @@ static int kszphy_extended_read(struct phy_device *phydev,
return phy_read(phydev, MII_KSZPHY_EXTREG_READ);
}
+static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val)
+{
+ int ctrl;
+
+ ctrl = phy_read(phydev, MII_KSZPHY_CTRL);
+ if (ctrl < 0)
+ return ctrl;
+
+ if (val)
+ ctrl |= KSZPHY_RMII_REF_CLK_SEL;
+ else
+ ctrl &= ~KSZPHY_RMII_REF_CLK_SEL;
+
+ return phy_write(phydev, MII_KSZPHY_CTRL, ctrl);
+}
+
/* Handle LED mode, shift = position of first led mode bit, usually 4 or 14 */
static int kszphy_led_mode(struct phy_device *phydev, int reg, int shift)
{
@@ -83,6 +118,24 @@ static int kszphy_led_mode(struct phy_device *phydev, int reg, int shift)
return 0;
}
+/* Some config bits need to be set again on resume, handle them here. */
+static int kszphy_config_reset(struct phy_device *phydev)
+{
+ struct kszphy_priv *priv = phydev->priv;
+ int ret;
+
+ if (priv->rmii_ref_clk_sel) {
+ ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val);
+ if (ret) {
+ dev_err(&phydev->dev,
+ "failed to set rmii reference clock\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int kszphy_config_init(struct phy_device *phydev)
{
kszphy_led_mode(phydev, MII_KSZPHY_CTRL_1, 14);
@@ -96,7 +149,7 @@ static int ksz8021_config_init(struct phy_device *phydev)
kszphy_led_mode(phydev, MII_KSZPHY_CTRL, 4);
- return 0;
+ return kszphy_config_reset(phydev);
}
static int ks8051_config_init(struct phy_device *phydev)
@@ -468,6 +521,50 @@ static int ksz8873mll_config_init(struct phy_device *phydev)
return 0;
}
+static int kszphy_probe(struct phy_device *phydev)
+{
+ struct device_d *dev = &phydev->dev;
+ struct device_node *np = dev->device_node;
+ struct phy_driver *drv = to_phy_driver(dev->driver);
+ const struct kszphy_type *type = drv->driver_data;
+ struct kszphy_priv *priv;
+ struct clk *clk;
+
+ priv = xzalloc(sizeof(*priv));
+
+ phydev->priv = priv;
+
+ priv->type = type;
+
+ clk = clk_get(dev, "rmii-ref");
+ /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */
+ if (!IS_ERR_OR_NULL(clk)) {
+ unsigned long rate = clk_get_rate(clk);
+ bool rmii_ref_clk_sel_25_mhz;
+
+ priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel;
+ rmii_ref_clk_sel_25_mhz = of_property_read_bool(np,
+ "micrel,rmii-reference-clock-select-25-mhz");
+
+ if (rate > 24500000 && rate < 25500000) {
+ priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz;
+ } else if (rate > 49500000 && rate < 50500000) {
+ priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz;
+ } else {
+ dev_err(dev, "Clock rate out of range: %ld\n", rate);
+ return -EINVAL;
+ }
+ }
+
+ /* Support legacy board-file configuration */
+ if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) {
+ priv->rmii_ref_clk_sel = true;
+ priv->rmii_ref_clk_sel_val = true;
+ }
+
+ return 0;
+}
+
static struct phy_driver ksphy_driver[] = {
{
.phy_id = PHY_ID_KS8737,
@@ -517,7 +614,9 @@ static struct phy_driver ksphy_driver[] = {
.phy_id = PHY_ID_KSZ8081,
.phy_id_mask = MICREL_PHY_ID_MASK,
.drv.name = "Micrel KSZ8081/91",
+ .driver_data = &ksz8081_type,
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
+ .probe = kszphy_probe,
.config_init = ksz8021_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
--
2.30.2
More information about the barebox
mailing list