From 5917c67af960190c4b0b682027b3305c39b5a42d Mon Sep 17 00:00:00 2001 From: Tianling Shen Date: Thu, 26 Mar 2026 14:05:35 +0800 Subject: [PATCH] kernel: backport Motorcomm YT6801 PCIe ethernet driver support Motorcomm YT6801 is a PCIe ethernet controller based on DWMAC4 IP. It integrates an GbE phy, supporting WOL, VLAN tagging and various types of offloading. It ships an on-chip eFuse for storing various vendor configuration, including MAC address. The PM and plat_data functions were slightly modified to build with current kernel. Signed-off-by: Tianling Shen Link: https://github.com/openwrt/openwrt/pull/22923 Signed-off-by: Hauke Mehrtens --- package/kernel/linux/modules/netdevices.mk | 16 + ...pport-YT8531S-PHY-in-YT6801-Ethernet.patch | 36 ++ ...driver-for-Motorcomm-YT6801-ethernet.patch | 472 ++++++++++++++++++ ...pport-YT8531S-PHY-in-YT6801-Ethernet.patch | 36 ++ ...driver-for-Motorcomm-YT6801-ethernet.patch | 467 +++++++++++++++++ target/linux/generic/config-6.12 | 1 + target/linux/generic/config-6.18 | 1 + ...comm-yt8821-bus-collision-workaround.patch | 2 +- ...comm-yt8821-bus-collision-workaround.patch | 2 +- 9 files changed, 1031 insertions(+), 2 deletions(-) create mode 100644 target/linux/generic/backport-6.12/705-01-v7.0-net-phy-motorcomm-Support-YT8531S-PHY-in-YT6801-Ethernet.patch create mode 100644 target/linux/generic/backport-6.12/705-02-v7.0-net-stmmac-Add-glue-driver-for-Motorcomm-YT6801-ethernet.patch create mode 100644 target/linux/generic/backport-6.18/705-01-v7.0-net-phy-motorcomm-Support-YT8531S-PHY-in-YT6801-Ethernet.patch create mode 100644 target/linux/generic/backport-6.18/705-02-v7.0-net-stmmac-Add-glue-driver-for-Motorcomm-YT6801-ethernet.patch diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk index c96c52f713d..58d7ad0627f 100644 --- a/package/kernel/linux/modules/netdevices.mk +++ b/package/kernel/linux/modules/netdevices.mk @@ -689,6 +689,22 @@ endef $(eval $(call KernelPackage,phy-motorcomm)) +define KernelPackage/dwmac-motorcomm + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Motorcomm PCI DWMAC support + DEPENDS:=@PCI_SUPPORT +kmod-phy-motorcomm +kmod-stmmac-core + KCONFIG:=CONFIG_DWMAC_MOTORCOMM + FILES:=$(LINUX_DIR)/drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.ko + AUTOLOAD:=$(call AutoProbe,dwmac-motorcomm,1) +endef + +define KernelPackage/dwmac-motorcomm/description + Supports the Motorcomm DWMAC-based PCI Ethernet controllers. +endef + +$(eval $(call KernelPackage,dwmac-motorcomm)) + + define KernelPackage/dsa SUBMENU:=$(NETWORK_DEVICES_MENU) TITLE:=Distributed Switch Architecture support diff --git a/target/linux/generic/backport-6.12/705-01-v7.0-net-phy-motorcomm-Support-YT8531S-PHY-in-YT6801-Ethernet.patch b/target/linux/generic/backport-6.12/705-01-v7.0-net-phy-motorcomm-Support-YT8531S-PHY-in-YT6801-Ethernet.patch new file mode 100644 index 00000000000..e6d1c6fc323 --- /dev/null +++ b/target/linux/generic/backport-6.12/705-01-v7.0-net-phy-motorcomm-Support-YT8531S-PHY-in-YT6801-Ethernet.patch @@ -0,0 +1,36 @@ +From 365e649361cde842c720ffa4b6a22fe092287990 Mon Sep 17 00:00:00 2001 +From: Yao Zi +Date: Fri, 9 Jan 2026 09:34:44 +0000 +Subject: [PATCH] net: phy: motorcomm: Support YT8531S PHY in YT6801 Ethernet + controller + +YT6801's internal PHY is confirmed as a GMII-capable variant of YT8531S +by a previous series[1] and reading PHY ID. Add support for +PHY_INTERFACE_MODE_GMII for YT8531S to allow the Ethernet driver to +reuse the PHY code for its internal PHY. + +Link: https://lore.kernel.org/all/a48d76ac-db08-46d5-9528-f046a7b541dc@motor-comm.com/ # [1] +Co-developed-by: Frank Sae +Signed-off-by: Frank Sae +Signed-off-by: Yao Zi +Reviewed-by: Andrew Lunn +Reviewed-by: Russell King (Oracle) +Link: https://patch.msgid.link/20260109093445.46791-3-me@ziyao.cc +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/motorcomm.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/phy/motorcomm.c ++++ b/drivers/net/phy/motorcomm.c +@@ -896,6 +896,10 @@ static int ytphy_rgmii_clk_delay_config( + val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg) | + FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg); + break; ++ case PHY_INTERFACE_MODE_GMII: ++ if (phydev->drv->phy_id != PHY_ID_YT8531S) ++ return -EOPNOTSUPP; ++ return 0; + default: /* do not support other modes */ + return -EOPNOTSUPP; + } diff --git a/target/linux/generic/backport-6.12/705-02-v7.0-net-stmmac-Add-glue-driver-for-Motorcomm-YT6801-ethernet.patch b/target/linux/generic/backport-6.12/705-02-v7.0-net-stmmac-Add-glue-driver-for-Motorcomm-YT6801-ethernet.patch new file mode 100644 index 00000000000..b4e593db401 --- /dev/null +++ b/target/linux/generic/backport-6.12/705-02-v7.0-net-stmmac-Add-glue-driver-for-Motorcomm-YT6801-ethernet.patch @@ -0,0 +1,472 @@ +From 02ff155ea2812ea25a086665522fd4fa196ef0ce Mon Sep 17 00:00:00 2001 +From: Yao Zi +Date: Fri, 9 Jan 2026 09:34:45 +0000 +Subject: [PATCH] net: stmmac: Add glue driver for Motorcomm YT6801 ethernet + controller + +Motorcomm YT6801 is a PCIe ethernet controller based on DWMAC4 IP. It +integrates an GbE phy, supporting WOL, VLAN tagging and various types +of offloading. It ships an on-chip eFuse for storing various vendor +configuration, including MAC address. + +This patch adds basic glue code for the controller, allowing it to be +set up and transmit data at a reasonable speed. Features like WOL could +be implemented in the future. + +Signed-off-by: Yao Zi +Tested-by: Mingcong Bai +Tested-by: Runhua He +Tested-by: Xi Ruoyao +Reviewed-by: Sai Krishna +Link: https://patch.msgid.link/20260109093445.46791-4-me@ziyao.cc +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/stmicro/stmmac/Kconfig | 9 + + drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + + .../ethernet/stmicro/stmmac/dwmac-motorcomm.c | 384 ++++++++++++++++++ + 3 files changed, 394 insertions(+) + create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c + +--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig ++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig +@@ -298,6 +298,14 @@ config DWMAC_LOONGSON + This selects the LOONGSON PCI bus support for the stmmac driver, + Support for ethernet controller on Loongson-2K1000 SoC and LS7A1000 bridge. + ++config DWMAC_MOTORCOMM ++ tristate "Motorcomm PCI DWMAC support" ++ depends on PCI ++ select MOTORCOMM_PHY ++ help ++ This enables glue driver for Motorcomm DWMAC-based PCI Ethernet ++ controllers. Currently only YT6801 is supported. ++ + config STMMAC_PCI + tristate "STMMAC PCI bus support" + depends on STMMAC_ETH && PCI +--- a/drivers/net/ethernet/stmicro/stmmac/Makefile ++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile +@@ -41,4 +41,5 @@ dwmac-altr-socfpga-objs := dwmac-socfpga + obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o + obj-$(CONFIG_DWMAC_INTEL) += dwmac-intel.o + obj-$(CONFIG_DWMAC_LOONGSON) += dwmac-loongson.o ++obj-$(CONFIG_DWMAC_MOTORCOMM) += dwmac-motorcomm.o + stmmac-pci-objs:= stmmac_pci.o +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c +@@ -0,0 +1,415 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * DWMAC glue driver for Motorcomm PCI Ethernet controllers ++ * ++ * Copyright (c) 2025-2026 Yao Zi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dwmac4.h" ++#include "stmmac.h" ++ ++#define DRIVER_NAME "dwmac-motorcomm" ++ ++#define PCI_VENDOR_ID_MOTORCOMM 0x1f0a ++ ++/* Register definition */ ++#define EPHY_CTRL 0x1004 ++/* Clearing this bit asserts resets for internal MDIO bus and PHY */ ++#define EPHY_MDIO_PHY_RESET BIT(0) ++#define OOB_WOL_CTRL 0x1010 ++#define OOB_WOL_CTRL_DIS BIT(0) ++#define MGMT_INT_CTRL0 0x1100 ++#define INT_MODERATION 0x1108 ++#define INT_MODERATION_RX GENMASK(11, 0) ++#define INT_MODERATION_TX GENMASK(27, 16) ++#define EFUSE_OP_CTRL_0 0x1500 ++#define EFUSE_OP_MODE GENMASK(1, 0) ++#define EFUSE_OP_ROW_READ 0x1 ++#define EFUSE_OP_START BIT(2) ++#define EFUSE_OP_ADDR GENMASK(15, 8) ++#define EFUSE_OP_CTRL_1 0x1504 ++#define EFUSE_OP_DONE BIT(1) ++#define EFUSE_OP_RD_DATA GENMASK(31, 24) ++#define SYS_RESET 0x152c ++#define SYS_RESET_RESET BIT(31) ++#define GMAC_OFFSET 0x2000 ++ ++/* Constants */ ++#define EFUSE_READ_TIMEOUT_US 20000 ++#define EFUSE_PATCH_REGION_OFFSET 18 ++#define EFUSE_PATCH_MAX_NUM 39 ++#define EFUSE_ADDR_MACA0LR 0x1520 ++#define EFUSE_ADDR_MACA0HR 0x1524 ++ ++struct motorcomm_efuse_patch { ++ __le16 addr; ++ __le32 data; ++} __packed; ++ ++struct dwmac_motorcomm_priv { ++ void __iomem *base; ++}; ++ ++static int motorcomm_efuse_read_byte(struct dwmac_motorcomm_priv *priv, ++ u8 offset, u8 *byte) ++{ ++ u32 reg; ++ int ret; ++ ++ writel(FIELD_PREP(EFUSE_OP_MODE, EFUSE_OP_ROW_READ) | ++ FIELD_PREP(EFUSE_OP_ADDR, offset) | ++ EFUSE_OP_START, priv->base + EFUSE_OP_CTRL_0); ++ ++ ret = readl_poll_timeout(priv->base + EFUSE_OP_CTRL_1, ++ reg, reg & EFUSE_OP_DONE, 2000, ++ EFUSE_READ_TIMEOUT_US); ++ ++ *byte = FIELD_GET(EFUSE_OP_RD_DATA, reg); ++ ++ return ret; ++} ++ ++static int motorcomm_efuse_read_patch(struct dwmac_motorcomm_priv *priv, ++ u8 index, ++ struct motorcomm_efuse_patch *patch) ++{ ++ u8 *p = (u8 *)patch, offset; ++ int i, ret; ++ ++ for (i = 0; i < sizeof(*patch); i++) { ++ offset = EFUSE_PATCH_REGION_OFFSET + sizeof(*patch) * index + i; ++ ++ ret = motorcomm_efuse_read_byte(priv, offset, &p[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int motorcomm_efuse_get_patch_value(struct dwmac_motorcomm_priv *priv, ++ u16 addr, u32 *value) ++{ ++ struct motorcomm_efuse_patch patch; ++ int i, ret; ++ ++ for (i = 0; i < EFUSE_PATCH_MAX_NUM; i++) { ++ ret = motorcomm_efuse_read_patch(priv, i, &patch); ++ if (ret) ++ return ret; ++ ++ if (patch.addr == 0) { ++ return -ENOENT; ++ } else if (le16_to_cpu(patch.addr) == addr) { ++ *value = le32_to_cpu(patch.data); ++ return 0; ++ } ++ } ++ ++ return -ENOENT; ++} ++ ++static int motorcomm_efuse_read_mac(struct device *dev, ++ struct dwmac_motorcomm_priv *priv, u8 *mac) ++{ ++ u32 maca0lr, maca0hr; ++ int ret; ++ ++ ret = motorcomm_efuse_get_patch_value(priv, EFUSE_ADDR_MACA0LR, ++ &maca0lr); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "failed to read maca0lr from eFuse\n"); ++ ++ ret = motorcomm_efuse_get_patch_value(priv, EFUSE_ADDR_MACA0HR, ++ &maca0hr); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "failed to read maca0hr from eFuse\n"); ++ ++ mac[0] = FIELD_GET(GENMASK(15, 8), maca0hr); ++ mac[1] = FIELD_GET(GENMASK(7, 0), maca0hr); ++ mac[2] = FIELD_GET(GENMASK(31, 24), maca0lr); ++ mac[3] = FIELD_GET(GENMASK(23, 16), maca0lr); ++ mac[4] = FIELD_GET(GENMASK(15, 8), maca0lr); ++ mac[5] = FIELD_GET(GENMASK(7, 0), maca0lr); ++ ++ return 0; ++} ++ ++static void motorcomm_deassert_mdio_phy_reset(struct dwmac_motorcomm_priv *priv) ++{ ++ u32 reg = readl(priv->base + EPHY_CTRL); ++ ++ reg |= EPHY_MDIO_PHY_RESET; ++ ++ writel(reg, priv->base + EPHY_CTRL); ++} ++ ++static void motorcomm_reset(struct dwmac_motorcomm_priv *priv) ++{ ++ u32 reg = readl(priv->base + SYS_RESET); ++ ++ reg &= ~SYS_RESET_RESET; ++ writel(reg, priv->base + SYS_RESET); ++ ++ reg |= SYS_RESET_RESET; ++ writel(reg, priv->base + SYS_RESET); ++ ++ motorcomm_deassert_mdio_phy_reset(priv); ++} ++ ++static void motorcomm_init(struct dwmac_motorcomm_priv *priv) ++{ ++ writel(0x0, priv->base + MGMT_INT_CTRL0); ++ ++ writel(FIELD_PREP(INT_MODERATION_RX, 200) | ++ FIELD_PREP(INT_MODERATION_TX, 200), ++ priv->base + INT_MODERATION); ++ ++ /* ++ * OOB WOL must be disabled during normal operation, or DMA interrupts ++ * cannot be delivered to the host. ++ */ ++ writel(OOB_WOL_CTRL_DIS, priv->base + OOB_WOL_CTRL); ++} ++ ++static struct plat_stmmacenet_data * ++motorcomm_default_plat_data(struct pci_dev *pdev) ++{ ++ struct plat_stmmacenet_data *plat; ++ struct device *dev = &pdev->dev; ++ ++ plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL); ++ if (!plat) ++ return NULL; ++ ++ plat->mdio_bus_data = devm_kzalloc(dev, sizeof(*plat->mdio_bus_data), ++ GFP_KERNEL); ++ if (!plat->mdio_bus_data) ++ return NULL; ++ ++ plat->dma_cfg = devm_kzalloc(dev, sizeof(*plat->dma_cfg), GFP_KERNEL); ++ if (!plat->dma_cfg) ++ return NULL; ++ ++ plat->axi = devm_kzalloc(dev, sizeof(*plat->axi), GFP_KERNEL); ++ if (!plat->axi) ++ return NULL; ++ ++ plat->dma_cfg->pbl = DEFAULT_DMA_PBL; ++ plat->dma_cfg->pblx8 = true; ++ plat->dma_cfg->txpbl = 32; ++ plat->dma_cfg->rxpbl = 32; ++ plat->dma_cfg->eame = true; ++ plat->dma_cfg->mixed_burst = true; ++ ++ plat->axi->axi_wr_osr_lmt = 1; ++ plat->axi->axi_rd_osr_lmt = 1; ++ plat->axi->axi_mb = true; ++ plat->axi->axi_blen[0] = 4; ++ plat->axi->axi_blen[1] = 8; ++ plat->axi->axi_blen[2] = 16; ++ plat->axi->axi_blen[3] = 32; ++ ++ plat->bus_id = pci_dev_id(pdev); ++ plat->phy_addr = -1; ++ plat->phy_interface = PHY_INTERFACE_MODE_GMII; ++ /* ++ * YT6801 requires an 25MHz clock input/oscillator to function, which ++ * is likely the source of CSR clock. ++ */ ++ plat->clk_csr = STMMAC_CSR_20_35M; ++ plat->tx_coe = 1; ++ plat->rx_coe = 1; ++ plat->maxmtu = JUMBO_LEN; ++ plat->rx_queues_to_use = 1; ++ plat->tx_queues_to_use = 1; ++ plat->clk_ref_rate = 125000000; ++ plat->has_gmac4 = 1; ++ plat->flags = STMMAC_FLAG_TSO_EN; ++ ++ return plat; ++} ++ ++static void motorcomm_free_irq(void *data) ++{ ++ struct pci_dev *pdev = data; ++ ++ pci_free_irq_vectors(pdev); ++} ++ ++static int motorcomm_setup_irq(struct pci_dev *pdev, ++ struct stmmac_resources *res, ++ struct plat_stmmacenet_data *plat) ++{ ++ int ret; ++ ++ ret = pci_alloc_irq_vectors(pdev, 6, 6, PCI_IRQ_MSIX); ++ if (ret > 0) { ++ res->rx_irq[0] = pci_irq_vector(pdev, 0); ++ res->tx_irq[0] = pci_irq_vector(pdev, 4); ++ res->irq = pci_irq_vector(pdev, 5); ++ ++ plat->flags |= STMMAC_FLAG_MULTI_MSI_EN; ++ } else { ++ dev_info(&pdev->dev, "failed to allocate MSI-X vector: %d\n", ++ ret); ++ dev_info(&pdev->dev, "try MSI instead\n"); ++ ++ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); ++ if (ret < 0) ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to allocate MSI\n"); ++ ++ res->irq = pci_irq_vector(pdev, 0); ++ } ++ ++ return devm_add_action_or_reset(&pdev->dev, motorcomm_free_irq, pdev); ++} ++ ++static int motorcomm_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ struct plat_stmmacenet_data *plat; ++ struct dwmac_motorcomm_priv *priv; ++ struct stmmac_resources res = {}; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ plat = motorcomm_default_plat_data(pdev); ++ if (!plat) ++ return -ENOMEM; ++ ++ plat->bsp_priv = priv; ++ ++ ret = pcim_enable_device(pdev); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to enable device\n"); ++ ++ priv->base = pcim_iomap_region(pdev, 0, DRIVER_NAME); ++ if (IS_ERR(priv->base)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->base), ++ "failed to map IO region\n"); ++ ++ pci_set_master(pdev); ++ ++ /* ++ * Some PCIe addons cards based on YT6801 don't deliver MSI(X) with ASPM ++ * enabled. Sadly there isn't a reliable way to read out OEM of the ++ * card, so let's disable L1 state unconditionally for safety. ++ */ ++ ret = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1); ++ if (ret) ++ dev_warn(&pdev->dev, "failed to disable L1 state: %d\n", ret); ++ ++ motorcomm_reset(priv); ++ ++ ret = motorcomm_efuse_read_mac(&pdev->dev, priv, res.mac); ++ if (ret == -ENOENT) { ++ dev_warn(&pdev->dev, "eFuse contains no valid MAC address\n"); ++ dev_warn(&pdev->dev, "fallback to random MAC address\n"); ++ ++ eth_random_addr(res.mac); ++ } else if (ret) { ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to read MAC address from eFuse\n"); ++ } ++ ++ ret = motorcomm_setup_irq(pdev, &res, plat); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "failed to setup IRQ\n"); ++ ++ motorcomm_init(priv); ++ ++ res.addr = priv->base + GMAC_OFFSET; ++ ++ return stmmac_dvr_probe(&pdev->dev, plat, &res); ++} ++ ++static void motorcomm_remove(struct pci_dev *pdev) ++{ ++ stmmac_dvr_remove(&pdev->dev); ++} ++ ++static int __maybe_unused motorcomm_dwmac_suspend(struct device *dev) ++{ ++ struct pci_dev *pdev = to_pci_dev(dev); ++ int ret; ++ ++ ret = stmmac_suspend(dev); ++ if (ret) ++ return ret; ++ ++ ret = pci_save_state(pdev); ++ if (ret) ++ return ret; ++ ++ pci_disable_device(pdev); ++ pci_wake_from_d3(pdev, true); ++ ++ return 0; ++} ++ ++static int __maybe_unused motorcomm_dwmac_resume(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ struct stmmac_priv *priv = netdev_priv(ndev); ++ struct pci_dev *pdev = to_pci_dev(dev); ++ int ret; ++ ++ pci_restore_state(pdev); ++ pci_set_power_state(pdev, PCI_D0); ++ ++ ret = pci_enable_device(pdev); ++ if (ret) ++ return ret; ++ ++ pci_set_master(pdev); ++ ++ /* ++ * When recovering from D3hot, EPHY_MDIO_PHY_RESET is automatically ++ * asserted, and must be deasserted for normal operation. ++ */ ++ motorcomm_deassert_mdio_phy_reset(priv->plat->bsp_priv); ++ motorcomm_init(priv->plat->bsp_priv); ++ ++ return stmmac_resume(dev); ++} ++ ++static SIMPLE_DEV_PM_OPS(motorcomm_dwmac_pm_ops, motorcomm_dwmac_suspend, ++ motorcomm_dwmac_resume); ++ ++static const struct pci_device_id dwmac_motorcomm_pci_id_table[] = { ++ { PCI_DEVICE(PCI_VENDOR_ID_MOTORCOMM, 0x6801) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(pci, dwmac_motorcomm_pci_id_table); ++ ++static struct pci_driver dwmac_motorcomm_pci_driver = { ++ .name = DRIVER_NAME, ++ .id_table = dwmac_motorcomm_pci_id_table, ++ .probe = motorcomm_probe, ++ .remove = motorcomm_remove, ++ .driver = { ++ .pm = &motorcomm_dwmac_pm_ops, ++ }, ++}; ++ ++module_pci_driver(dwmac_motorcomm_pci_driver); ++ ++MODULE_DESCRIPTION("DWMAC glue driver for Motorcomm PCI Ethernet controllers"); ++MODULE_AUTHOR("Yao Zi "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.18/705-01-v7.0-net-phy-motorcomm-Support-YT8531S-PHY-in-YT6801-Ethernet.patch b/target/linux/generic/backport-6.18/705-01-v7.0-net-phy-motorcomm-Support-YT8531S-PHY-in-YT6801-Ethernet.patch new file mode 100644 index 00000000000..36c422ed43d --- /dev/null +++ b/target/linux/generic/backport-6.18/705-01-v7.0-net-phy-motorcomm-Support-YT8531S-PHY-in-YT6801-Ethernet.patch @@ -0,0 +1,36 @@ +From 365e649361cde842c720ffa4b6a22fe092287990 Mon Sep 17 00:00:00 2001 +From: Yao Zi +Date: Fri, 9 Jan 2026 09:34:44 +0000 +Subject: [PATCH] net: phy: motorcomm: Support YT8531S PHY in YT6801 Ethernet + controller + +YT6801's internal PHY is confirmed as a GMII-capable variant of YT8531S +by a previous series[1] and reading PHY ID. Add support for +PHY_INTERFACE_MODE_GMII for YT8531S to allow the Ethernet driver to +reuse the PHY code for its internal PHY. + +Link: https://lore.kernel.org/all/a48d76ac-db08-46d5-9528-f046a7b541dc@motor-comm.com/ # [1] +Co-developed-by: Frank Sae +Signed-off-by: Frank Sae +Signed-off-by: Yao Zi +Reviewed-by: Andrew Lunn +Reviewed-by: Russell King (Oracle) +Link: https://patch.msgid.link/20260109093445.46791-3-me@ziyao.cc +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/motorcomm.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/phy/motorcomm.c ++++ b/drivers/net/phy/motorcomm.c +@@ -910,6 +910,10 @@ static int ytphy_rgmii_clk_delay_config( + val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg) | + FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg); + break; ++ case PHY_INTERFACE_MODE_GMII: ++ if (phydev->drv->phy_id != PHY_ID_YT8531S) ++ return -EOPNOTSUPP; ++ return 0; + default: /* do not support other modes */ + return -EOPNOTSUPP; + } diff --git a/target/linux/generic/backport-6.18/705-02-v7.0-net-stmmac-Add-glue-driver-for-Motorcomm-YT6801-ethernet.patch b/target/linux/generic/backport-6.18/705-02-v7.0-net-stmmac-Add-glue-driver-for-Motorcomm-YT6801-ethernet.patch new file mode 100644 index 00000000000..22a83a50040 --- /dev/null +++ b/target/linux/generic/backport-6.18/705-02-v7.0-net-stmmac-Add-glue-driver-for-Motorcomm-YT6801-ethernet.patch @@ -0,0 +1,467 @@ +From 02ff155ea2812ea25a086665522fd4fa196ef0ce Mon Sep 17 00:00:00 2001 +From: Yao Zi +Date: Fri, 9 Jan 2026 09:34:45 +0000 +Subject: [PATCH] net: stmmac: Add glue driver for Motorcomm YT6801 ethernet + controller + +Motorcomm YT6801 is a PCIe ethernet controller based on DWMAC4 IP. It +integrates an GbE phy, supporting WOL, VLAN tagging and various types +of offloading. It ships an on-chip eFuse for storing various vendor +configuration, including MAC address. + +This patch adds basic glue code for the controller, allowing it to be +set up and transmit data at a reasonable speed. Features like WOL could +be implemented in the future. + +Signed-off-by: Yao Zi +Tested-by: Mingcong Bai +Tested-by: Runhua He +Tested-by: Xi Ruoyao +Reviewed-by: Sai Krishna +Link: https://patch.msgid.link/20260109093445.46791-4-me@ziyao.cc +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/stmicro/stmmac/Kconfig | 9 + + drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + + .../ethernet/stmicro/stmmac/dwmac-motorcomm.c | 384 ++++++++++++++++++ + 3 files changed, 394 insertions(+) + create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c + +--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig ++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig +@@ -358,6 +358,14 @@ config DWMAC_LOONGSON + This selects the LOONGSON PCI bus support for the stmmac driver, + Support for ethernet controller on Loongson-2K1000 SoC and LS7A1000 bridge. + ++config DWMAC_MOTORCOMM ++ tristate "Motorcomm PCI DWMAC support" ++ depends on PCI ++ select MOTORCOMM_PHY ++ help ++ This enables glue driver for Motorcomm DWMAC-based PCI Ethernet ++ controllers. Currently only YT6801 is supported. ++ + config STMMAC_PCI + tristate "STMMAC PCI bus support" + depends on STMMAC_ETH && PCI +--- a/drivers/net/ethernet/stmicro/stmmac/Makefile ++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile +@@ -46,4 +46,5 @@ dwmac-altr-socfpga-objs := dwmac-socfpga + obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o + obj-$(CONFIG_DWMAC_INTEL) += dwmac-intel.o + obj-$(CONFIG_DWMAC_LOONGSON) += dwmac-loongson.o ++obj-$(CONFIG_DWMAC_MOTORCOMM) += dwmac-motorcomm.o + stmmac-pci-objs:= stmmac_pci.o +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c +@@ -0,0 +1,410 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * DWMAC glue driver for Motorcomm PCI Ethernet controllers ++ * ++ * Copyright (c) 2025-2026 Yao Zi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dwmac4.h" ++#include "stmmac.h" ++ ++#define DRIVER_NAME "dwmac-motorcomm" ++ ++#define PCI_VENDOR_ID_MOTORCOMM 0x1f0a ++ ++/* Register definition */ ++#define EPHY_CTRL 0x1004 ++/* Clearing this bit asserts resets for internal MDIO bus and PHY */ ++#define EPHY_MDIO_PHY_RESET BIT(0) ++#define OOB_WOL_CTRL 0x1010 ++#define OOB_WOL_CTRL_DIS BIT(0) ++#define MGMT_INT_CTRL0 0x1100 ++#define INT_MODERATION 0x1108 ++#define INT_MODERATION_RX GENMASK(11, 0) ++#define INT_MODERATION_TX GENMASK(27, 16) ++#define EFUSE_OP_CTRL_0 0x1500 ++#define EFUSE_OP_MODE GENMASK(1, 0) ++#define EFUSE_OP_ROW_READ 0x1 ++#define EFUSE_OP_START BIT(2) ++#define EFUSE_OP_ADDR GENMASK(15, 8) ++#define EFUSE_OP_CTRL_1 0x1504 ++#define EFUSE_OP_DONE BIT(1) ++#define EFUSE_OP_RD_DATA GENMASK(31, 24) ++#define SYS_RESET 0x152c ++#define SYS_RESET_RESET BIT(31) ++#define GMAC_OFFSET 0x2000 ++ ++/* Constants */ ++#define EFUSE_READ_TIMEOUT_US 20000 ++#define EFUSE_PATCH_REGION_OFFSET 18 ++#define EFUSE_PATCH_MAX_NUM 39 ++#define EFUSE_ADDR_MACA0LR 0x1520 ++#define EFUSE_ADDR_MACA0HR 0x1524 ++ ++struct motorcomm_efuse_patch { ++ __le16 addr; ++ __le32 data; ++} __packed; ++ ++struct dwmac_motorcomm_priv { ++ void __iomem *base; ++}; ++ ++static int motorcomm_efuse_read_byte(struct dwmac_motorcomm_priv *priv, ++ u8 offset, u8 *byte) ++{ ++ u32 reg; ++ int ret; ++ ++ writel(FIELD_PREP(EFUSE_OP_MODE, EFUSE_OP_ROW_READ) | ++ FIELD_PREP(EFUSE_OP_ADDR, offset) | ++ EFUSE_OP_START, priv->base + EFUSE_OP_CTRL_0); ++ ++ ret = readl_poll_timeout(priv->base + EFUSE_OP_CTRL_1, ++ reg, reg & EFUSE_OP_DONE, 2000, ++ EFUSE_READ_TIMEOUT_US); ++ ++ *byte = FIELD_GET(EFUSE_OP_RD_DATA, reg); ++ ++ return ret; ++} ++ ++static int motorcomm_efuse_read_patch(struct dwmac_motorcomm_priv *priv, ++ u8 index, ++ struct motorcomm_efuse_patch *patch) ++{ ++ u8 *p = (u8 *)patch, offset; ++ int i, ret; ++ ++ for (i = 0; i < sizeof(*patch); i++) { ++ offset = EFUSE_PATCH_REGION_OFFSET + sizeof(*patch) * index + i; ++ ++ ret = motorcomm_efuse_read_byte(priv, offset, &p[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int motorcomm_efuse_get_patch_value(struct dwmac_motorcomm_priv *priv, ++ u16 addr, u32 *value) ++{ ++ struct motorcomm_efuse_patch patch; ++ int i, ret; ++ ++ for (i = 0; i < EFUSE_PATCH_MAX_NUM; i++) { ++ ret = motorcomm_efuse_read_patch(priv, i, &patch); ++ if (ret) ++ return ret; ++ ++ if (patch.addr == 0) { ++ return -ENOENT; ++ } else if (le16_to_cpu(patch.addr) == addr) { ++ *value = le32_to_cpu(patch.data); ++ return 0; ++ } ++ } ++ ++ return -ENOENT; ++} ++ ++static int motorcomm_efuse_read_mac(struct device *dev, ++ struct dwmac_motorcomm_priv *priv, u8 *mac) ++{ ++ u32 maca0lr, maca0hr; ++ int ret; ++ ++ ret = motorcomm_efuse_get_patch_value(priv, EFUSE_ADDR_MACA0LR, ++ &maca0lr); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "failed to read maca0lr from eFuse\n"); ++ ++ ret = motorcomm_efuse_get_patch_value(priv, EFUSE_ADDR_MACA0HR, ++ &maca0hr); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "failed to read maca0hr from eFuse\n"); ++ ++ mac[0] = FIELD_GET(GENMASK(15, 8), maca0hr); ++ mac[1] = FIELD_GET(GENMASK(7, 0), maca0hr); ++ mac[2] = FIELD_GET(GENMASK(31, 24), maca0lr); ++ mac[3] = FIELD_GET(GENMASK(23, 16), maca0lr); ++ mac[4] = FIELD_GET(GENMASK(15, 8), maca0lr); ++ mac[5] = FIELD_GET(GENMASK(7, 0), maca0lr); ++ ++ return 0; ++} ++ ++static void motorcomm_deassert_mdio_phy_reset(struct dwmac_motorcomm_priv *priv) ++{ ++ u32 reg = readl(priv->base + EPHY_CTRL); ++ ++ reg |= EPHY_MDIO_PHY_RESET; ++ ++ writel(reg, priv->base + EPHY_CTRL); ++} ++ ++static void motorcomm_reset(struct dwmac_motorcomm_priv *priv) ++{ ++ u32 reg = readl(priv->base + SYS_RESET); ++ ++ reg &= ~SYS_RESET_RESET; ++ writel(reg, priv->base + SYS_RESET); ++ ++ reg |= SYS_RESET_RESET; ++ writel(reg, priv->base + SYS_RESET); ++ ++ motorcomm_deassert_mdio_phy_reset(priv); ++} ++ ++static void motorcomm_init(struct dwmac_motorcomm_priv *priv) ++{ ++ writel(0x0, priv->base + MGMT_INT_CTRL0); ++ ++ writel(FIELD_PREP(INT_MODERATION_RX, 200) | ++ FIELD_PREP(INT_MODERATION_TX, 200), ++ priv->base + INT_MODERATION); ++ ++ /* ++ * OOB WOL must be disabled during normal operation, or DMA interrupts ++ * cannot be delivered to the host. ++ */ ++ writel(OOB_WOL_CTRL_DIS, priv->base + OOB_WOL_CTRL); ++} ++ ++static int motorcomm_suspend(struct device *dev, void *bsp_priv) ++{ ++ struct pci_dev *pdev = to_pci_dev(dev); ++ int ret; ++ ++ ret = pci_save_state(pdev); ++ if (ret) ++ return ret; ++ ++ pci_disable_device(pdev); ++ pci_wake_from_d3(pdev, true); ++ ++ return 0; ++} ++ ++static int motorcomm_resume(struct device *dev, void *bsp_priv) ++{ ++ struct dwmac_motorcomm_priv *priv = bsp_priv; ++ struct pci_dev *pdev = to_pci_dev(dev); ++ int ret; ++ ++ pci_restore_state(pdev); ++ pci_set_power_state(pdev, PCI_D0); ++ ++ ret = pci_enable_device(pdev); ++ if (ret) ++ return ret; ++ ++ pci_set_master(pdev); ++ ++ /* ++ * When recovering from D3hot, EPHY_MDIO_PHY_RESET is automatically ++ * asserted, and must be deasserted for normal operation. ++ */ ++ motorcomm_deassert_mdio_phy_reset(priv); ++ motorcomm_init(priv); ++ ++ return 0; ++} ++ ++static struct plat_stmmacenet_data * ++motorcomm_default_plat_data(struct pci_dev *pdev) ++{ ++ struct plat_stmmacenet_data *plat; ++ struct device *dev = &pdev->dev; ++ ++ plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL); ++ if (!plat) ++ return NULL; ++ ++ plat->mdio_bus_data = devm_kzalloc(dev, sizeof(*plat->mdio_bus_data), ++ GFP_KERNEL); ++ if (!plat->mdio_bus_data) ++ return NULL; ++ ++ plat->dma_cfg = devm_kzalloc(dev, sizeof(*plat->dma_cfg), GFP_KERNEL); ++ if (!plat->dma_cfg) ++ return NULL; ++ ++ plat->axi = devm_kzalloc(dev, sizeof(*plat->axi), GFP_KERNEL); ++ if (!plat->axi) ++ return NULL; ++ ++ plat->dma_cfg->pbl = DEFAULT_DMA_PBL; ++ plat->dma_cfg->pblx8 = true; ++ plat->dma_cfg->txpbl = 32; ++ plat->dma_cfg->rxpbl = 32; ++ plat->dma_cfg->eame = true; ++ plat->dma_cfg->mixed_burst = true; ++ ++ plat->axi->axi_wr_osr_lmt = 1; ++ plat->axi->axi_rd_osr_lmt = 1; ++ plat->axi->axi_mb = true; ++ plat->axi->axi_blen[0] = 4; ++ plat->axi->axi_blen[1] = 8; ++ plat->axi->axi_blen[2] = 16; ++ plat->axi->axi_blen[3] = 32; ++ ++ plat->bus_id = pci_dev_id(pdev); ++ plat->phy_addr = -1; ++ plat->phy_interface = PHY_INTERFACE_MODE_GMII; ++ /* ++ * YT6801 requires an 25MHz clock input/oscillator to function, which ++ * is likely the source of CSR clock. ++ */ ++ plat->clk_csr = STMMAC_CSR_20_35M; ++ plat->tx_coe = 1; ++ plat->rx_coe = 1; ++ plat->maxmtu = JUMBO_LEN; ++ plat->rx_queues_to_use = 1; ++ plat->tx_queues_to_use = 1; ++ plat->clk_ref_rate = 125000000; ++ plat->core_type = DWMAC_CORE_GMAC4; ++ plat->suspend = motorcomm_suspend; ++ plat->resume = motorcomm_resume; ++ plat->flags = STMMAC_FLAG_TSO_EN | ++ STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP; ++ ++ return plat; ++} ++ ++static void motorcomm_free_irq(void *data) ++{ ++ struct pci_dev *pdev = data; ++ ++ pci_free_irq_vectors(pdev); ++} ++ ++static int motorcomm_setup_irq(struct pci_dev *pdev, ++ struct stmmac_resources *res, ++ struct plat_stmmacenet_data *plat) ++{ ++ int ret; ++ ++ ret = pci_alloc_irq_vectors(pdev, 6, 6, PCI_IRQ_MSIX); ++ if (ret > 0) { ++ res->rx_irq[0] = pci_irq_vector(pdev, 0); ++ res->tx_irq[0] = pci_irq_vector(pdev, 4); ++ res->irq = pci_irq_vector(pdev, 5); ++ ++ plat->flags |= STMMAC_FLAG_MULTI_MSI_EN; ++ } else { ++ dev_info(&pdev->dev, "failed to allocate MSI-X vector: %d\n", ++ ret); ++ dev_info(&pdev->dev, "try MSI instead\n"); ++ ++ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); ++ if (ret < 0) ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to allocate MSI\n"); ++ ++ res->irq = pci_irq_vector(pdev, 0); ++ } ++ ++ return devm_add_action_or_reset(&pdev->dev, motorcomm_free_irq, pdev); ++} ++ ++static int motorcomm_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ struct plat_stmmacenet_data *plat; ++ struct dwmac_motorcomm_priv *priv; ++ struct stmmac_resources res = {}; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ plat = motorcomm_default_plat_data(pdev); ++ if (!plat) ++ return -ENOMEM; ++ ++ plat->bsp_priv = priv; ++ ++ ret = pcim_enable_device(pdev); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to enable device\n"); ++ ++ priv->base = pcim_iomap_region(pdev, 0, DRIVER_NAME); ++ if (IS_ERR(priv->base)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->base), ++ "failed to map IO region\n"); ++ ++ pci_set_master(pdev); ++ ++ /* ++ * Some PCIe addons cards based on YT6801 don't deliver MSI(X) with ASPM ++ * enabled. Sadly there isn't a reliable way to read out OEM of the ++ * card, so let's disable L1 state unconditionally for safety. ++ */ ++ ret = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1); ++ if (ret) ++ dev_warn(&pdev->dev, "failed to disable L1 state: %d\n", ret); ++ ++ motorcomm_reset(priv); ++ ++ ret = motorcomm_efuse_read_mac(&pdev->dev, priv, res.mac); ++ if (ret == -ENOENT) { ++ dev_warn(&pdev->dev, "eFuse contains no valid MAC address\n"); ++ dev_warn(&pdev->dev, "fallback to random MAC address\n"); ++ ++ eth_random_addr(res.mac); ++ } else if (ret) { ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to read MAC address from eFuse\n"); ++ } ++ ++ ret = motorcomm_setup_irq(pdev, &res, plat); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "failed to setup IRQ\n"); ++ ++ motorcomm_init(priv); ++ ++ res.addr = priv->base + GMAC_OFFSET; ++ ++ return stmmac_dvr_probe(&pdev->dev, plat, &res); ++} ++ ++static void motorcomm_remove(struct pci_dev *pdev) ++{ ++ stmmac_dvr_remove(&pdev->dev); ++} ++ ++static const struct pci_device_id dwmac_motorcomm_pci_id_table[] = { ++ { PCI_DEVICE(PCI_VENDOR_ID_MOTORCOMM, 0x6801) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(pci, dwmac_motorcomm_pci_id_table); ++ ++static struct pci_driver dwmac_motorcomm_pci_driver = { ++ .name = DRIVER_NAME, ++ .id_table = dwmac_motorcomm_pci_id_table, ++ .probe = motorcomm_probe, ++ .remove = motorcomm_remove, ++ .driver = { ++ .pm = &stmmac_simple_pm_ops, ++ }, ++}; ++ ++module_pci_driver(dwmac_motorcomm_pci_driver); ++ ++MODULE_DESCRIPTION("DWMAC glue driver for Motorcomm PCI Ethernet controllers"); ++MODULE_AUTHOR("Yao Zi "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12 index c45063d68e0..98fa67369c8 100644 --- a/target/linux/generic/config-6.12 +++ b/target/linux/generic/config-6.12 @@ -1860,6 +1860,7 @@ CONFIG_DVB_MAX_ADAPTERS=16 # CONFIG_DWMAC_LOONGSON is not set # CONFIG_DWMAC_LPC18XX is not set # CONFIG_DWMAC_MESON is not set +# CONFIG_DWMAC_MOTORCOMM is not set # CONFIG_DWMAC_ROCKCHIP is not set # CONFIG_DWMAC_SOCFPGA is not set # CONFIG_DWMAC_STI is not set diff --git a/target/linux/generic/config-6.18 b/target/linux/generic/config-6.18 index 4253505cf83..77657b53e11 100644 --- a/target/linux/generic/config-6.18 +++ b/target/linux/generic/config-6.18 @@ -1946,6 +1946,7 @@ CONFIG_DVB_MAX_ADAPTERS=16 # CONFIG_DWMAC_LOONGSON is not set # CONFIG_DWMAC_LPC18XX is not set # CONFIG_DWMAC_MESON is not set +# CONFIG_DWMAC_MOTORCOMM is not set # CONFIG_DWMAC_ROCKCHIP is not set # CONFIG_DWMAC_SOCFPGA is not set # CONFIG_DWMAC_STI is not set diff --git a/target/linux/generic/hack-6.12/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch b/target/linux/generic/hack-6.12/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch index 28c4c702b37..8c6ad7e7596 100644 --- a/target/linux/generic/hack-6.12/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch +++ b/target/linux/generic/hack-6.12/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch @@ -59,7 +59,7 @@ Signed-off-by: Jakub Vaněk #define YTPHY_MISC_CONFIG_REG 0xA006 #define YTPHY_MCR_FIBER_SPEED_MASK BIT(0) #define YTPHY_MCR_FIBER_1000BX (0x1 << 0) -@@ -2659,6 +2662,23 @@ static int yt8821_config_init(struct phy +@@ -2663,6 +2666,23 @@ static int yt8821_config_init(struct phy int ret; u16 set; diff --git a/target/linux/generic/hack-6.18/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch b/target/linux/generic/hack-6.18/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch index 07836e75515..fa12bca39d4 100644 --- a/target/linux/generic/hack-6.18/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch +++ b/target/linux/generic/hack-6.18/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch @@ -59,7 +59,7 @@ Signed-off-by: Jakub Vaněk #define YTPHY_MISC_CONFIG_REG 0xA006 #define YTPHY_MCR_FIBER_SPEED_MASK BIT(0) #define YTPHY_MCR_FIBER_1000BX (0x1 << 0) -@@ -2773,6 +2776,23 @@ static int yt8821_config_init(struct phy +@@ -2777,6 +2780,23 @@ static int yt8821_config_init(struct phy int ret; u16 set;