[PATCH 3/8] net: stmmac: Introduce Qualcomm IPQ50xx DWMAC driver

From: Ziyang Huang
Date: Sun Jan 21 2024 - 07:44:00 EST


Signed-off-by: Ziyang Huang <hzyitc@xxxxxxxxxxx>
---
drivers/net/ethernet/stmicro/stmmac/Kconfig | 7 +
drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
.../ethernet/stmicro/stmmac/dwmac-ipq50xx.c | 155 ++++++++++++++++++
3 files changed, 163 insertions(+)
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-ipq50xx.c

diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 06c6871f8788..baf6601d759c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -78,6 +78,13 @@ config DWMAC_INGENIC
device driver. This driver is used on for the Ingenic SoCs
MAC ethernet controller.

+config DWMAC_IPQ50XX
+ tristate "Qualcomm IPQ50xx DWMAC driver"
+ default ARCH_QCOM
+ depends on OF && (ARCH_QCOM || COMPILE_TEST)
+ help
+ Support for the Qualcomm IPQ50xx DWMAC.
+
config DWMAC_IPQ806X
tristate "QCA IPQ806x DWMAC support"
default ARCH_QCOM
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index 5b57aee19267..eec4405d76d2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -15,6 +15,7 @@ stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o
obj-$(CONFIG_DWMAC_INGENIC) += dwmac-ingenic.o
+obj-$(CONFIG_DWMAC_IPQ50XX) += dwmac-ipq50xx.o
obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o
obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq50xx.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq50xx.c
new file mode 100644
index 000000000000..de8e9a0b2cb6
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq50xx.c
@@ -0,0 +1,155 @@
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/stmmac.h>
+
+#include "stmmac_platform.h"
+
+enum {
+ IPQ50XX_GMAC_CLK_SYS,
+ IPQ50XX_GMAC_CLK_CFG,
+ IPQ50XX_GMAC_CLK_AHB,
+ IPQ50XX_GMAC_CLK_AXI,
+ IPQ50XX_GMAC_CLK_RX,
+ IPQ50XX_GMAC_CLK_TX,
+ IPQ50XX_GMAC_CLK_PTP,
+};
+
+static const struct clk_bulk_data ipq50xx_gmac_clks[] = {
+ [IPQ50XX_GMAC_CLK_SYS] = { .id = "sys" },
+ [IPQ50XX_GMAC_CLK_CFG] = { .id = "cfg" },
+ [IPQ50XX_GMAC_CLK_AHB] = { .id = "ahb" },
+ [IPQ50XX_GMAC_CLK_AXI] = { .id = "axi" },
+ [IPQ50XX_GMAC_CLK_RX] = { .id = "rx" },
+ [IPQ50XX_GMAC_CLK_TX] = { .id = "tx" },
+ [IPQ50XX_GMAC_CLK_PTP] = { .id = "ptp" },
+};
+
+struct ipq50xx_gmac {
+ struct device *dev;
+ struct clk_bulk_data clks[ARRAY_SIZE(ipq50xx_gmac_clks)];
+ struct reset_control *rst;
+ struct phy *uniphy;
+};
+
+static int ipq50xx_gmac_powerup(struct net_device *ndev, void *priv)
+{
+ struct ipq50xx_gmac *gmac = priv;
+ int ret;
+
+ ret = phy_init(gmac->uniphy);
+ if (ret)
+ return ret;
+
+ ret = phy_power_on(gmac->uniphy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void ipq50xx_gmac_fix_speed(void *priv, unsigned int speed, unsigned int mode)
+{
+ struct ipq50xx_gmac *gmac = priv;
+ unsigned long rate;
+
+ switch(speed) {
+ case SPEED_10:
+ rate = 2500000;
+ break;
+ case SPEED_100:
+ rate = 25000000;
+ break;
+ case SPEED_1000:
+ rate = 125000000;
+ break;
+ case SPEED_2500:
+ rate = 312500000;
+ break;
+ default:
+ dev_err(gmac->dev, "Unsupported speed: %d\n", speed);
+ rate = 125000000;
+ break;
+ }
+
+ clk_set_rate(gmac->clks[IPQ50XX_GMAC_CLK_RX].clk, rate);
+ clk_set_rate(gmac->clks[IPQ50XX_GMAC_CLK_TX].clk, rate);
+ phy_calibrate(gmac->uniphy);
+}
+
+static int ipq50xx_gmac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stmmac_resources stmmac_res;
+ struct plat_stmmacenet_data *plat_dat;
+ struct ipq50xx_gmac *gmac;
+ int ret;
+
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get stmmac platform resources\n");
+
+ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR_OR_NULL(plat_dat))
+ return dev_err_probe(dev, PTR_ERR(plat_dat),
+ "failed to parse stmmac dt parameters\n");
+
+ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
+ if (!gmac)
+ return dev_err_probe(dev, -ENOMEM,
+ "failed to allocate priv\n");
+
+ gmac->dev = dev;
+
+ memcpy(gmac->clks, ipq50xx_gmac_clks, sizeof(gmac->clks));
+ ret = devm_clk_bulk_get_optional(dev, ARRAY_SIZE(gmac->clks), gmac->clks);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to acquire clocks\n");
+
+ ret = clk_bulk_prepare_enable(ARRAY_SIZE(gmac->clks), gmac->clks);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to enable clocks\n");
+
+ gmac->rst = devm_reset_control_array_get_exclusive(dev);
+ if (IS_ERR_OR_NULL(gmac->rst))
+ return dev_err_probe(dev, PTR_ERR(gmac->rst),
+ "failed to acquire reset\n");
+
+ ret = reset_control_reset(gmac->rst);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to reset\n");
+
+ gmac->uniphy = devm_phy_optional_get(dev, "uniphy");
+ if (IS_ERR(gmac->uniphy))
+ return dev_err_probe(dev, PTR_ERR(gmac->uniphy),
+ "failed to acquire uniphy\n");
+
+ plat_dat->bsp_priv = gmac;
+ plat_dat->serdes_powerup = ipq50xx_gmac_powerup;
+ plat_dat->fix_mac_speed = ipq50xx_gmac_fix_speed;
+
+ return stmmac_dvr_probe(dev, plat_dat, &stmmac_res);
+}
+
+static const struct of_device_id ipq50xx_gmac_dwmac_match[] = {
+ { .compatible = "qcom,ipq50xx-gmac" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ipq50xx_gmac_dwmac_match);
+
+static struct platform_driver ipq50xx_gmac_dwmac_driver = {
+ .probe = ipq50xx_gmac_probe,
+ .driver = {
+ .name = "ipq50xx-gmac-dwmac",
+ .of_match_table = ipq50xx_gmac_dwmac_match,
+ },
+};
+module_platform_driver(ipq50xx_gmac_dwmac_driver);
+
+MODULE_DESCRIPTION("Qualcomm IPQ50xx DWMAC driver");
+MODULE_AUTHOR("Ziyang Huang <hzyitc@xxxxxxxxxxx>");
--
2.40.1