[PATCH 1/8] net: phy: Introduce Qualcomm IPQ5018 internal PHY driver

Ziyang Huang hzyitc at outlook.com
Sun Jan 21 04:42:30 PST 2024


Signed-off-by: Ziyang Huang <hzyitc at outlook.com>
---
 drivers/net/phy/Kconfig            |   5 ++
 drivers/net/phy/Makefile           |   1 +
 drivers/net/phy/ipq5018-internal.c | 125 +++++++++++++++++++++++++++++
 3 files changed, 131 insertions(+)
 create mode 100644 drivers/net/phy/ipq5018-internal.c

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 107880d13d21..2d068fea7008 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -202,6 +202,11 @@ config INTEL_XWAY_PHY
 	  PEF 7061, PEF 7071 and PEF 7072 or integrated into the Intel
 	  SoCs xRX200, xRX300, xRX330, xRX350 and xRX550.
 
+config IPQ5018_INTERNAL_PHY
+	tristate "Qualcomm IPQ5018 internal PHY"
+	help
+	  Supports for the Qualcomm IPQ5018 internal PHY.
+
 config LSI_ET1011C_PHY
 	tristate "LSI ET1011C PHY"
 	help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index c945ed9bd14b..16d65378ae34 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_DP83TD510_PHY)	+= dp83td510.o
 obj-$(CONFIG_FIXED_PHY)		+= fixed_phy.o
 obj-$(CONFIG_ICPLUS_PHY)	+= icplus.o
 obj-$(CONFIG_INTEL_XWAY_PHY)	+= intel-xway.o
+obj-$(CONFIG_IPQ5018_INTERNAL_PHY)	+= ipq5018-internal.o
 obj-$(CONFIG_LSI_ET1011C_PHY)	+= et1011c.o
 obj-$(CONFIG_LXT_PHY)		+= lxt.o
 obj-$(CONFIG_MARVELL_10G_PHY)	+= marvell10g.o
diff --git a/drivers/net/phy/ipq5018-internal.c b/drivers/net/phy/ipq5018-internal.c
new file mode 100644
index 000000000000..d1331951b4d8
--- /dev/null
+++ b/drivers/net/phy/ipq5018-internal.c
@@ -0,0 +1,125 @@
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/phy.h>
+#include <linux/reset.h>
+
+#define IPQ5018_PHY_ID			0x004dd0c0
+
+#define TX_RX_CLK_RATE			125000000 /* 125M */
+
+#define IPQ5018_PHY_FIFO_CONTROL	0x19
+#define  IPQ5018_PHY_FIFO_RESET		GENMASK(1, 0)
+
+struct ipq5018_phy {
+	int num_clks;
+	struct clk_bulk_data *clks;
+	struct reset_control *rst;
+
+	struct clk_hw *clk_rx, *clk_tx;
+	struct clk_hw_onecell_data *clk_data;
+};
+
+static int ipq5018_probe(struct phy_device *phydev)
+{
+	struct ipq5018_phy *priv;
+	struct device *dev = &phydev->mdio.dev;
+	char name[64];
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return dev_err_probe(dev, -ENOMEM,
+				     "failed to allocate priv\n");
+
+	priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks);
+	if (priv->num_clks < 0)
+		return dev_err_probe(dev, priv->num_clks,
+				     "failed to acquire clocks\n");
+
+	ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to enable clocks\n");
+
+	priv->rst = devm_reset_control_array_get_exclusive(dev);
+	if (IS_ERR_OR_NULL(priv->rst))
+		return dev_err_probe(dev, PTR_ERR(priv->rst),
+				     "failed to acquire reset\n");
+
+	ret = reset_control_reset(priv->rst);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to reset\n");
+
+	snprintf(name, sizeof(name), "%s#rx", dev_name(dev));
+	priv->clk_rx = clk_hw_register_fixed_rate(dev, name, NULL, 0,
+						  TX_RX_CLK_RATE);
+	if (IS_ERR_OR_NULL(priv->clk_rx))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_rx),
+				     "failed to register rx clock\n");
+
+	snprintf(name, sizeof(name), "%s#tx", dev_name(dev));
+	priv->clk_tx = clk_hw_register_fixed_rate(dev, name, NULL, 0,
+						  TX_RX_CLK_RATE);
+	if (IS_ERR_OR_NULL(priv->clk_tx))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_tx),
+				     "failed to register tx clock\n");
+
+	priv->clk_data = devm_kzalloc(dev,
+				      struct_size(priv->clk_data, hws, 2),
+				      GFP_KERNEL);
+	if (!priv->clk_data)
+		return dev_err_probe(dev, -ENOMEM,
+				     "failed to allocate clk_data\n");
+
+	priv->clk_data->num = 2;
+	priv->clk_data->hws[0] = priv->clk_rx;
+	priv->clk_data->hws[1] = priv->clk_tx;
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+				     priv->clk_data);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "fail to register clock provider\n");
+
+	return 0;
+}
+
+static int ipq5018_soft_reset(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = phy_modify(phydev, IPQ5018_PHY_FIFO_CONTROL,
+			 IPQ5018_PHY_FIFO_RESET, 0);
+	if (ret < 0)
+		return ret;
+
+	msleep(50);
+
+	ret = phy_modify(phydev, IPQ5018_PHY_FIFO_CONTROL,
+			 IPQ5018_PHY_FIFO_RESET, IPQ5018_PHY_FIFO_RESET);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct phy_driver ipq5018_internal_phy_driver[] = {
+	{
+		PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID),
+		.name		= "Qualcomm IPQ5018 internal PHY",
+		.flags		= PHY_IS_INTERNAL,
+		.probe		= ipq5018_probe,
+		.soft_reset	= ipq5018_soft_reset,
+	},
+};
+module_phy_driver(ipq5018_internal_phy_driver);
+
+static struct mdio_device_id __maybe_unused ipq5018_internal_phy_ids[] = {
+	{ PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID) },
+	{ }
+};
+MODULE_DEVICE_TABLE(mdio, ipq5018_internal_phy_ids);
+
+MODULE_DESCRIPTION("Qualcomm IPQ5018 internal PHY driver");
+MODULE_AUTHOR("Ziyang Huang <hzyitc at outlook.com>");
-- 
2.40.1




More information about the linux-arm-kernel mailing list