[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250316141900.50991-2-lucienX123@gmail.com>
Date: Sun, 16 Mar 2025 22:19:00 +0800
From: "Lucien.Jheng" <lucienx123@...il.com>
To: andrew@...n.ch,
hkallweit1@...il.com,
linux@...linux.org.uk,
kuba@...nel.org,
davem@...emloft.net,
edumazet@...gle.com,
pabeni@...hat.com,
daniel@...rotopia.org,
ericwouds@...il.com
Cc: netdev@...r.kernel.org,
linux-kernel@...r.kernel.org,
joseph.lin@...oha.com,
wenshin.chung@...oha.com,
"Lucien.Jheng" <lucienX123@...il.com>
Subject: [PATCH v3 net-next PATCH 1/1] net: phy: air_en8811h: Add clk provider for CKO pin
The EN8811H generates 25MHz or 50MHz clocks on its CKO pin, selected by GPIO3 hardware trap.
Register 0xcf914, read via buckpbus API, shows the frequency with bit 12: 0 for 25MHz, 1 for 50MHz.
CKO clock output is active from power-up through md32 firmware loading.
Signed-off-by: Lucien.Jheng <lucienX123@...il.com>
---
drivers/net/phy/air_en8811h.c | 95 +++++++++++++++++++++++++++++++++++
1 file changed, 95 insertions(+)
diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
index e9fd24cb7270..ed90ccefe842 100644
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -16,6 +16,7 @@
#include <linux/property.h>
#include <linux/wordpart.h>
#include <linux/unaligned.h>
+#include <linux/clk-provider.h>
#define EN8811H_PHY_ID 0x03a2a411
@@ -112,6 +113,11 @@
#define EN8811H_POLARITY_TX_NORMAL BIT(0)
#define EN8811H_POLARITY_RX_REVERSE BIT(1)
+#define EN8811H_CLK_CGM 0xcf958
+#define EN8811H_CLK_CGM_CKO BIT(26)
+#define EN8811H_HWTRAP1 0xcf914
+#define EN8811H_HWTRAP1_CKO BIT(12)
+
#define EN8811H_GPIO_OUTPUT 0xcf8b8
#define EN8811H_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5))
@@ -142,10 +148,15 @@ struct led {
unsigned long state;
};
+#define to_en8811h_priv(_hw) \
+ container_of(_hw, struct en8811h_priv, hw)
+
struct en8811h_priv {
u32 firmware_version;
bool mcu_needs_restart;
struct led led[EN8811H_LED_COUNT];
+ struct clk_hw hw;
+ struct phy_device *phydev;
};
enum {
@@ -806,6 +817,84 @@ static int en8811h_led_hw_is_supported(struct phy_device *phydev, u8 index,
return 0;
};
+static unsigned long en8811h_recalc_rate(struct clk_hw *hw, unsigned long parent)
+{
+ struct en8811h_priv *priv = to_en8811h_priv(hw);
+ struct phy_device *phydev = priv->phydev;
+ u32 pbus_value;
+ int ret;
+
+ ret = air_buckpbus_reg_read(phydev, EN8811H_HWTRAP1, &pbus_value);
+ if (ret < 0)
+ return ret;
+
+ return (pbus_value & EN8811H_HWTRAP1_CKO) ? 50000000 : 25000000;
+}
+
+static int en8811h_enable(struct clk_hw *hw)
+{
+ struct en8811h_priv *priv = to_en8811h_priv(hw);
+ struct phy_device *phydev = priv->phydev;
+
+ return air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM,
+ EN8811H_CLK_CGM_CKO, EN8811H_CLK_CGM_CKO);
+}
+
+static void en8811h_disable(struct clk_hw *hw)
+{
+ struct en8811h_priv *priv = to_en8811h_priv(hw);
+ struct phy_device *phydev = priv->phydev;
+
+ air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM,
+ EN8811H_CLK_CGM_CKO, 0);
+}
+
+static int en8811h_is_enabled(struct clk_hw *hw)
+{
+ struct en8811h_priv *priv = to_en8811h_priv(hw);
+ struct phy_device *phydev = priv->phydev;
+ int ret = 0;
+ u32 pbus_value;
+
+ ret = air_buckpbus_reg_read(phydev, EN8811H_CLK_CGM, &pbus_value);
+ if (ret < 0)
+ return ret;
+
+ return (pbus_value & EN8811H_CLK_CGM_CKO);
+}
+
+static const struct clk_ops en8811h_clk_ops = {
+ .recalc_rate = en8811h_recalc_rate,
+ .enable = en8811h_enable,
+ .disable = en8811h_disable,
+ .is_enabled = en8811h_is_enabled,
+};
+
+static int en8811h_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-clk",
+ fwnode_get_name(dev_fwnode(dev)));
+ if (!init.name)
+ return -ENOMEM;
+
+ init.ops = &en8811h_clk_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ 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 int en8811h_probe(struct phy_device *phydev)
{
struct en8811h_priv *priv;
@@ -838,6 +927,12 @@ static int en8811h_probe(struct phy_device *phydev)
return ret;
}
+ priv->phydev = phydev;
+ /* Co-Clock */
+ ret = en8811h_clk_provider_setup(&phydev->mdio.dev, &priv->hw);
+ if (ret)
+ return ret;
+
/* Configure led gpio pins as output */
ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT,
EN8811H_GPIO_OUTPUT_345,
--
2.34.1
Powered by blists - more mailing lists