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 <cnsztl@immortalwrt.org> Link: https://github.com/openwrt/openwrt/pull/22923 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
committed by
Hauke Mehrtens
parent
3956287514
commit
5917c67af9
@@ -689,6 +689,22 @@ endef
|
|||||||
$(eval $(call KernelPackage,phy-motorcomm))
|
$(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
|
define KernelPackage/dsa
|
||||||
SUBMENU:=$(NETWORK_DEVICES_MENU)
|
SUBMENU:=$(NETWORK_DEVICES_MENU)
|
||||||
TITLE:=Distributed Switch Architecture support
|
TITLE:=Distributed Switch Architecture support
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
From 365e649361cde842c720ffa4b6a22fe092287990 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Yao Zi <me@ziyao.cc>
|
||||||
|
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 <Frank.Sae@motor-comm.com>
|
||||||
|
Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
|
||||||
|
Signed-off-by: Yao Zi <me@ziyao.cc>
|
||||||
|
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||||
|
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||||
|
Link: https://patch.msgid.link/20260109093445.46791-3-me@ziyao.cc
|
||||||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||||
|
---
|
||||||
|
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;
|
||||||
|
}
|
||||||
+472
@@ -0,0 +1,472 @@
|
|||||||
|
From 02ff155ea2812ea25a086665522fd4fa196ef0ce Mon Sep 17 00:00:00 2001
|
||||||
|
From: Yao Zi <me@ziyao.cc>
|
||||||
|
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 <me@ziyao.cc>
|
||||||
|
Tested-by: Mingcong Bai <jeffbai@aosc.io>
|
||||||
|
Tested-by: Runhua He <hua@aosc.io>
|
||||||
|
Tested-by: Xi Ruoyao <xry111@xry111.site>
|
||||||
|
Reviewed-by: Sai Krishna <saikrishnag@marvell.com>
|
||||||
|
Link: https://patch.msgid.link/20260109093445.46791-4-me@ziyao.cc
|
||||||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||||
|
---
|
||||||
|
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 <me@ziyao.cc>
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/bits.h>
|
||||||
|
+#include <linux/dev_printk.h>
|
||||||
|
+#include <linux/io.h>
|
||||||
|
+#include <linux/iopoll.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/pci.h>
|
||||||
|
+#include <linux/slab.h>
|
||||||
|
+#include <linux/stmmac.h>
|
||||||
|
+
|
||||||
|
+#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 <me@ziyao.cc>");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
From 365e649361cde842c720ffa4b6a22fe092287990 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Yao Zi <me@ziyao.cc>
|
||||||
|
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 <Frank.Sae@motor-comm.com>
|
||||||
|
Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
|
||||||
|
Signed-off-by: Yao Zi <me@ziyao.cc>
|
||||||
|
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||||
|
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||||
|
Link: https://patch.msgid.link/20260109093445.46791-3-me@ziyao.cc
|
||||||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||||
|
---
|
||||||
|
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;
|
||||||
|
}
|
||||||
+467
@@ -0,0 +1,467 @@
|
|||||||
|
From 02ff155ea2812ea25a086665522fd4fa196ef0ce Mon Sep 17 00:00:00 2001
|
||||||
|
From: Yao Zi <me@ziyao.cc>
|
||||||
|
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 <me@ziyao.cc>
|
||||||
|
Tested-by: Mingcong Bai <jeffbai@aosc.io>
|
||||||
|
Tested-by: Runhua He <hua@aosc.io>
|
||||||
|
Tested-by: Xi Ruoyao <xry111@xry111.site>
|
||||||
|
Reviewed-by: Sai Krishna <saikrishnag@marvell.com>
|
||||||
|
Link: https://patch.msgid.link/20260109093445.46791-4-me@ziyao.cc
|
||||||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||||
|
---
|
||||||
|
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 <me@ziyao.cc>
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/bits.h>
|
||||||
|
+#include <linux/dev_printk.h>
|
||||||
|
+#include <linux/io.h>
|
||||||
|
+#include <linux/iopoll.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/pci.h>
|
||||||
|
+#include <linux/slab.h>
|
||||||
|
+#include <linux/stmmac.h>
|
||||||
|
+
|
||||||
|
+#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 <me@ziyao.cc>");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
@@ -1860,6 +1860,7 @@ CONFIG_DVB_MAX_ADAPTERS=16
|
|||||||
# CONFIG_DWMAC_LOONGSON is not set
|
# CONFIG_DWMAC_LOONGSON is not set
|
||||||
# CONFIG_DWMAC_LPC18XX is not set
|
# CONFIG_DWMAC_LPC18XX is not set
|
||||||
# CONFIG_DWMAC_MESON is not set
|
# CONFIG_DWMAC_MESON is not set
|
||||||
|
# CONFIG_DWMAC_MOTORCOMM is not set
|
||||||
# CONFIG_DWMAC_ROCKCHIP is not set
|
# CONFIG_DWMAC_ROCKCHIP is not set
|
||||||
# CONFIG_DWMAC_SOCFPGA is not set
|
# CONFIG_DWMAC_SOCFPGA is not set
|
||||||
# CONFIG_DWMAC_STI is not set
|
# CONFIG_DWMAC_STI is not set
|
||||||
|
|||||||
@@ -1946,6 +1946,7 @@ CONFIG_DVB_MAX_ADAPTERS=16
|
|||||||
# CONFIG_DWMAC_LOONGSON is not set
|
# CONFIG_DWMAC_LOONGSON is not set
|
||||||
# CONFIG_DWMAC_LPC18XX is not set
|
# CONFIG_DWMAC_LPC18XX is not set
|
||||||
# CONFIG_DWMAC_MESON is not set
|
# CONFIG_DWMAC_MESON is not set
|
||||||
|
# CONFIG_DWMAC_MOTORCOMM is not set
|
||||||
# CONFIG_DWMAC_ROCKCHIP is not set
|
# CONFIG_DWMAC_ROCKCHIP is not set
|
||||||
# CONFIG_DWMAC_SOCFPGA is not set
|
# CONFIG_DWMAC_SOCFPGA is not set
|
||||||
# CONFIG_DWMAC_STI is not set
|
# CONFIG_DWMAC_STI is not set
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ Signed-off-by: Jakub Vaněk <linuxtardis@gmail.com>
|
|||||||
#define YTPHY_MISC_CONFIG_REG 0xA006
|
#define YTPHY_MISC_CONFIG_REG 0xA006
|
||||||
#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
|
#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
|
||||||
#define YTPHY_MCR_FIBER_1000BX (0x1 << 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;
|
int ret;
|
||||||
u16 set;
|
u16 set;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ Signed-off-by: Jakub Vaněk <linuxtardis@gmail.com>
|
|||||||
#define YTPHY_MISC_CONFIG_REG 0xA006
|
#define YTPHY_MISC_CONFIG_REG 0xA006
|
||||||
#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
|
#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
|
||||||
#define YTPHY_MCR_FIBER_1000BX (0x1 << 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;
|
int ret;
|
||||||
u16 set;
|
u16 set;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user