openwrt/target/linux/qualcommax/patches-6.6/0711-net-phy-qcom-Introduce-IPQ5018-internal-PHY-driver.patch
George Moussalem 34d9172655 qualcommax: add ipq50xx target
Introduce support for the Qualcomm IPQ50xx SoC.
This series adds support for the following components:
- minimal boot support: GCC/pinctrl/watchdog/CPUFreq/SDI (upstreamed)
- USB2 (upstreamed)
- Thermal/Tsens
- PCIe gen2 1&2-lane PHY and controller
- PWM and PWM LED
- QPIC SPI NAND controller
- CMN PLL Block (provider of fixed rate clocks to GCC/ethernet/more.)
- Ethernet: IPQ5018 Internal GE PHY (1 gbps)
- Remoteproc MPD driver for IPQ5018 (2.4G) & QCN6122 (5/6G) Wifi

Co-developed-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Link: https://github.com/openwrt/openwrt/pull/17182
Signed-off-by: Robert Marko <robimarko@gmail.com>
2025-02-06 09:51:13 +01:00

185 lines
5.4 KiB
Diff

From 77ad12b3a5e21cae859247c0b82cf9a5b661e531 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:11 +0800
Subject: [PATCH 1/3] net: phy: qcom: Introduce IPQ5018 internal PHY driver
Introduce the internal GE PHY driver, part of the Qualcomm IPQ50xx SoC.
The driver registers two clock providers needed and referenced by the GCC
using DT properties and phandles.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/net/phy/qcom/Kconfig | 6 ++
drivers/net/phy/qcom/Makefile | 1 +
drivers/net/phy/qcom/ipq5018.c | 138 +++++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+)
create mode 100644 drivers/net/phy/qcom/ipq5018.c
--- a/drivers/net/phy/qcom/Kconfig
+++ b/drivers/net/phy/qcom/Kconfig
@@ -9,6 +9,12 @@ config AT803X_PHY
help
Currently supports the AR8030, AR8031, AR8033, AR8035 model
+config IPQ5018_PHY
+ tristate "Qualcomm IPQ5018 internal PHYs"
+ select QCOM_NET_PHYLIB
+ help
+ Currently supports the Qualcomm IPQ5018 internal PHY
+
config QCA83XX_PHY
tristate "Qualcomm Atheros QCA833x PHYs"
select QCOM_NET_PHYLIB
--- a/drivers/net/phy/qcom/Makefile
+++ b/drivers/net/phy/qcom/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
obj-$(CONFIG_AT803X_PHY) += at803x.o
+obj-$(CONFIG_IPQ5018_PHY) += ipq5018.o
obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o
obj-$(CONFIG_QCA808X_PHY) += qca808x.o
obj-$(CONFIG_QCA807X_PHY) += qca807x.o
--- /dev/null
+++ b/drivers/net/phy/qcom/ipq5018.c
@@ -0,0 +1,138 @@
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/phy.h>
+#include <linux/reset.h>
+
+#include "qcom.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 int ipq5018_cable_test_start(struct phy_device *phydev)
+{
+ /* we do all the (time consuming) work later */
+ 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 | PHY_POLL_CABLE_TEST,
+ .probe = ipq5018_probe,
+ .soft_reset = ipq5018_soft_reset,
+ .read_status = at803x_read_status,
+ .config_intr = at803x_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .cable_test_start = ipq5018_cable_test_start,
+ .cable_test_get_status = qca808x_cable_test_get_status,
+ },
+};
+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@outlook.com>");