Commit ba5560e5 authored by Lucien.Jheng's avatar Lucien.Jheng Committed by Jakub Kicinski
Browse files

net: phy: air_en8811h: Add clk provider for CKO pin



EN8811H outputs 25MHz or 50MHz clocks on CKO, selected by GPIO3.
CKO clock operates continuously from power-up through md32 loading.
Implement clk provider driver so we can disable the clock output in case
it isn't needed, which also helps to reduce EMF noise

Signed-off-by: default avatarLucien.Jheng <lucienx123@gmail.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250409150902.3596-1-lucienx123@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent faeefc17
Loading
Loading
Loading
Loading
+100 −3
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
 * Copyright (C) 2023 Airoha Technology Corp.
 */

#include <linux/clk-provider.h>
#include <linux/phy.h>
#include <linux/firmware.h>
#include <linux/property.h>
@@ -115,6 +116,11 @@
#define EN8811H_GPIO_OUTPUT		0xcf8b8
#define   EN8811H_GPIO_OUTPUT_345		(BIT(3) | BIT(4) | BIT(5))

#define EN8811H_HWTRAP1			0xcf914
#define   EN8811H_HWTRAP1_CKO			BIT(12)
#define EN8811H_CLK_CGM			0xcf958
#define   EN8811H_CLK_CGM_CKO			BIT(26)

#define EN8811H_FW_CTRL_1		0x0f0018
#define   EN8811H_FW_CTRL_1_START		0x0
#define   EN8811H_FW_CTRL_1_FINISH		0x1
@@ -142,10 +148,15 @@ struct led {
	unsigned long state;
};

#define clk_hw_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,86 @@ static int en8811h_led_hw_is_supported(struct phy_device *phydev, u8 index,
	return 0;
};

static unsigned long en8811h_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, EN8811H_HWTRAP1, &pbus_value);
	if (ret < 0)
		return ret;

	return (pbus_value & EN8811H_HWTRAP1_CKO) ? 50000000 : 25000000;
}

static int en8811h_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, EN8811H_CLK_CGM,
				       EN8811H_CLK_CGM_CKO,
				       EN8811H_CLK_CGM_CKO);
}

static void en8811h_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, EN8811H_CLK_CGM,
				EN8811H_CLK_CGM_CKO, 0);
}

static int en8811h_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, 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_clk_recalc_rate,
	.enable		= en8811h_clk_enable,
	.disable	= en8811h_clk_disable,
	.is_enabled	= en8811h_clk_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-cko",
				   fwnode_get_name(dev_fwnode(dev)));
	if (!init.name)
		return -ENOMEM;

	init.ops = &en8811h_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 int en8811h_probe(struct phy_device *phydev)
{
	struct en8811h_priv *priv;
@@ -838,6 +929,12 @@ static int en8811h_probe(struct phy_device *phydev)
		return ret;
	}

	priv->phydev = phydev;
	/* Co-Clock Output */
	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,