]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: phy: air_en8811h: Add clk provider for an8811hb
authorBjørn Mork <bjorn@mork.no>
Tue, 27 Jan 2026 12:55:47 +0000 (13:55 +0100)
committerJakub Kicinski <kuba@kernel.org>
Fri, 30 Jan 2026 03:15:46 +0000 (19:15 -0800)
Implement clk provider driver so we can disable the clock output when it
isn't needed. This helps to reduce EMF noise

Signed-off-by: Bjørn Mork <bjorn@mork.no>
Link: https://patch.msgid.link/20260127125547.1475164-4-bjorn@mork.no
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/air_en8811h.c

index 67a1bf60255f535457dfdc63e3804ba6de766e12..29ae73e65caaa9cdebe2253b5349aa6c7478dc85 100644 (file)
@@ -949,6 +949,105 @@ static int en8811h_led_hw_is_supported(struct phy_device *phydev, u8 index,
        return 0;
 };
 
+static unsigned long an8811hb_clk_recalc_rate(struct clk_hw *hw,
+                                             unsigned long parent)
+{
+       struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
+       struct phy_device *phydev = priv->phydev;
+       u32 pbus_value;
+       int ret;
+
+       ret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);
+       if (ret < 0)
+               return ret;
+
+       return (pbus_value & AN8811HB_HWTRAP2_CKO) ? 50000000 : 25000000;
+}
+
+static int an8811hb_clk_enable(struct clk_hw *hw)
+{
+       struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
+       struct phy_device *phydev = priv->phydev;
+
+       return air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
+                                      AN8811HB_CLK_DRV_CKO_MASK,
+                                      AN8811HB_CLK_DRV_CKO_MASK);
+}
+
+static void an8811hb_clk_disable(struct clk_hw *hw)
+{
+       struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
+       struct phy_device *phydev = priv->phydev;
+
+       air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
+                               AN8811HB_CLK_DRV_CKO_MASK, 0);
+}
+
+static int an8811hb_clk_is_enabled(struct clk_hw *hw)
+{
+       struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
+       struct phy_device *phydev = priv->phydev;
+       u32 pbus_value;
+       int ret;
+
+       ret = air_buckpbus_reg_read(phydev, AN8811HB_CLK_DRV, &pbus_value);
+       if (ret < 0)
+               return ret;
+
+       return (pbus_value & AN8811HB_CLK_DRV_CKO_MASK);
+}
+
+static int an8811hb_clk_save_context(struct clk_hw *hw)
+{
+       struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
+
+       priv->cko_is_enabled = an8811hb_clk_is_enabled(hw);
+
+       return 0;
+}
+
+static void an8811hb_clk_restore_context(struct clk_hw *hw)
+{
+       struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
+
+       if (!priv->cko_is_enabled)
+               an8811hb_clk_disable(hw);
+}
+
+static const struct clk_ops an8811hb_clk_ops = {
+       .recalc_rate            = an8811hb_clk_recalc_rate,
+       .enable                 = an8811hb_clk_enable,
+       .disable                = an8811hb_clk_disable,
+       .is_enabled             = an8811hb_clk_is_enabled,
+       .save_context           = an8811hb_clk_save_context,
+       .restore_context        = an8811hb_clk_restore_context,
+};
+
+static int an8811hb_clk_provider_setup(struct device *dev, struct clk_hw *hw)
+{
+       struct clk_init_data init;
+       int ret;
+
+       if (!IS_ENABLED(CONFIG_COMMON_CLK))
+               return 0;
+
+       init.name = devm_kasprintf(dev, GFP_KERNEL, "%s-cko",
+                                  fwnode_get_name(dev_fwnode(dev)));
+       if (!init.name)
+               return -ENOMEM;
+
+       init.ops = &an8811hb_clk_ops;
+       init.flags = 0;
+       init.num_parents = 0;
+       hw->init = &init;
+
+       ret = devm_clk_hw_register(dev, hw);
+       if (ret)
+               return ret;
+
+       return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+}
+
 static unsigned long en8811h_clk_recalc_rate(struct clk_hw *hw,
                                             unsigned long parent)
 {
@@ -1094,6 +1193,12 @@ static int an8811hb_probe(struct phy_device *phydev)
        if (ret < 0)
                return ret;
 
+       priv->phydev = phydev;
+       /* Co-Clock Output */
+       ret = an8811hb_clk_provider_setup(&phydev->mdio.dev, &priv->hw);
+       if (ret)
+               return ret;
+
        /* Configure led gpio pins as output */
        ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
                                      AN8811HB_GPIO_OUTPUT_345,
@@ -1487,6 +1592,8 @@ static struct phy_driver en8811h_driver[] = {
        .get_rate_matching      = en8811h_get_rate_matching,
        .config_aneg            = en8811h_config_aneg,
        .read_status            = en8811h_read_status,
+       .resume                 = en8811h_resume,
+       .suspend                = en8811h_suspend,
        .config_intr            = en8811h_clear_intr,
        .handle_interrupt       = en8811h_handle_interrupt,
        .led_hw_is_supported    = en8811h_led_hw_is_supported,