Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'add-dwmac-glue-driver-for-motorcomm-yt6801'

Yao Zi says:

====================
Add DWMAC glue driver for Motorcomm YT6801

This series adds glue driver for Motorcomm YT6801 PCIe ethernet
controller, which is considered mostly compatible with DWMAC-4 IP by
inspecting the register layout[1]. It integrates a Motorcomm YT8531S PHY
(confirmed by reading PHY ID) and GMII is used to connect the PHY to
MAC[2].

The initialization logic of the MAC is mostly based on previous upstream
effort for the controller[3] and the Deepin-maintained downstream Linux
driver[4] licensed under GPL-2.0 according to its SPDX headers. However,
this series is a completely re-write of the previous patch series,
utilizing the existing DWMAC4 driver and introducing a glue driver only.

This series only aims to add basic networking functions for the
controller, features like WoL, RSS and LED control are omitted for now.
Testing is done on i3-4170, it reaches 939Mbps (TX)/933Mbps (RX) on
average,

YT6801 TX

Connecting to host 192.168.114.51, port 5201
[ 5] local 192.168.114.50 port 52986 connected to 192.168.114.51 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 112 MBytes 938 Mbits/sec 0 950 KBytes
[ 5] 1.00-2.00 sec 113 MBytes 949 Mbits/sec 0 1.08 MBytes
[ 5] 2.00-3.00 sec 112 MBytes 938 Mbits/sec 0 1.08 MBytes
[ 5] 3.00-4.00 sec 111 MBytes 932 Mbits/sec 0 1.13 MBytes
[ 5] 4.00-5.00 sec 113 MBytes 945 Mbits/sec 0 1.13 MBytes
[ 5] 5.00-6.00 sec 112 MBytes 936 Mbits/sec 0 1.13 MBytes
[ 5] 6.00-7.00 sec 112 MBytes 942 Mbits/sec 0 1.19 MBytes
[ 5] 7.00-8.00 sec 112 MBytes 935 Mbits/sec 0 1.19 MBytes
[ 5] 8.00-9.00 sec 113 MBytes 948 Mbits/sec 0 1.19 MBytes
[ 5] 9.00-10.00 sec 111 MBytes 931 Mbits/sec 0 1.19 MBytes

YT6801 RX

Connecting to host 192.168.114.50, port 5201
[ 5] local 192.168.114.51 port 41578 connected to 192.168.114.50 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 113 MBytes 944 Mbits/sec 0 542 KBytes
[ 5] 1.00-2.00 sec 111 MBytes 934 Mbits/sec 0 850 KBytes
[ 5] 2.00-3.00 sec 111 MBytes 933 Mbits/sec 0 1.01 MBytes
[ 5] 3.00-4.00 sec 112 MBytes 943 Mbits/sec 0 1.01 MBytes
[ 5] 4.00-5.00 sec 111 MBytes 932 Mbits/sec 0 1.01 MBytes
[ 5] 5.00-6.00 sec 111 MBytes 929 Mbits/sec 0 1.01 MBytes
[ 5] 6.00-7.00 sec 112 MBytes 937 Mbits/sec 0 1.01 MBytes
[ 5] 7.00-8.00 sec 112 MBytes 941 Mbits/sec 0 1.01 MBytes
[ 5] 8.00-9.00 sec 111 MBytes 929 Mbits/sec 0 1.01 MBytes
[ 5] 9.00-10.00 sec 111 MBytes 932 Mbits/sec 0 1.01 MBytes
====================

Link: https://patch.msgid.link/20260109093445.46791-2-me@ziyao.cc
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+404
+6
MAINTAINERS
··· 17659 17659 F: drivers/staging/most/ 17660 17660 F: include/linux/most.h 17661 17661 17662 + MOTORCOMM DWMAC GLUE DRIVER 17663 + M: Yao Zi <me@ziyao.cc> 17664 + L: netdev@vger.kernel.org 17665 + S: Maintained 17666 + F: drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c 17667 + 17662 17668 MOTORCOMM PHY DRIVER 17663 17669 M: Frank <Frank.Sae@motor-comm.com> 17664 17670 L: netdev@vger.kernel.org
+9
drivers/net/ethernet/stmicro/stmmac/Kconfig
··· 374 374 This selects the LOONGSON PCI bus support for the stmmac driver, 375 375 Support for ethernet controller on Loongson-2K1000 SoC and LS7A1000 bridge. 376 376 377 + config DWMAC_MOTORCOMM 378 + tristate "Motorcomm PCI DWMAC support" 379 + depends on PCI 380 + select MOTORCOMM_PHY 381 + select STMMAC_LIBPCI 382 + help 383 + This enables glue driver for Motorcomm DWMAC-based PCI Ethernet 384 + controllers. Currently only YT6801 is supported. 385 + 377 386 config STMMAC_PCI 378 387 tristate "STMMAC PCI bus support" 379 388 depends on PCI
+1
drivers/net/ethernet/stmicro/stmmac/Makefile
··· 48 48 obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o 49 49 obj-$(CONFIG_DWMAC_INTEL) += dwmac-intel.o 50 50 obj-$(CONFIG_DWMAC_LOONGSON) += dwmac-loongson.o 51 + obj-$(CONFIG_DWMAC_MOTORCOMM) += dwmac-motorcomm.o 51 52 stmmac-pci-objs:= stmmac_pci.o
+384
drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * DWMAC glue driver for Motorcomm PCI Ethernet controllers 4 + * 5 + * Copyright (c) 2025-2026 Yao Zi <me@ziyao.cc> 6 + */ 7 + 8 + #include <linux/bits.h> 9 + #include <linux/dev_printk.h> 10 + #include <linux/io.h> 11 + #include <linux/iopoll.h> 12 + #include <linux/module.h> 13 + #include <linux/pci.h> 14 + #include <linux/slab.h> 15 + #include <linux/stmmac.h> 16 + 17 + #include "dwmac4.h" 18 + #include "stmmac.h" 19 + #include "stmmac_libpci.h" 20 + 21 + #define DRIVER_NAME "dwmac-motorcomm" 22 + 23 + #define PCI_VENDOR_ID_MOTORCOMM 0x1f0a 24 + 25 + /* Register definition */ 26 + #define EPHY_CTRL 0x1004 27 + /* Clearing this bit asserts resets for internal MDIO bus and PHY */ 28 + #define EPHY_MDIO_PHY_RESET BIT(0) 29 + #define OOB_WOL_CTRL 0x1010 30 + #define OOB_WOL_CTRL_DIS BIT(0) 31 + #define MGMT_INT_CTRL0 0x1100 32 + #define INT_MODERATION 0x1108 33 + #define INT_MODERATION_RX GENMASK(11, 0) 34 + #define INT_MODERATION_TX GENMASK(27, 16) 35 + #define EFUSE_OP_CTRL_0 0x1500 36 + #define EFUSE_OP_MODE GENMASK(1, 0) 37 + #define EFUSE_OP_ROW_READ 0x1 38 + #define EFUSE_OP_START BIT(2) 39 + #define EFUSE_OP_ADDR GENMASK(15, 8) 40 + #define EFUSE_OP_CTRL_1 0x1504 41 + #define EFUSE_OP_DONE BIT(1) 42 + #define EFUSE_OP_RD_DATA GENMASK(31, 24) 43 + #define SYS_RESET 0x152c 44 + #define SYS_RESET_RESET BIT(31) 45 + #define GMAC_OFFSET 0x2000 46 + 47 + /* Constants */ 48 + #define EFUSE_READ_TIMEOUT_US 20000 49 + #define EFUSE_PATCH_REGION_OFFSET 18 50 + #define EFUSE_PATCH_MAX_NUM 39 51 + #define EFUSE_ADDR_MACA0LR 0x1520 52 + #define EFUSE_ADDR_MACA0HR 0x1524 53 + 54 + struct motorcomm_efuse_patch { 55 + __le16 addr; 56 + __le32 data; 57 + } __packed; 58 + 59 + struct dwmac_motorcomm_priv { 60 + void __iomem *base; 61 + }; 62 + 63 + static int motorcomm_efuse_read_byte(struct dwmac_motorcomm_priv *priv, 64 + u8 offset, u8 *byte) 65 + { 66 + u32 reg; 67 + int ret; 68 + 69 + writel(FIELD_PREP(EFUSE_OP_MODE, EFUSE_OP_ROW_READ) | 70 + FIELD_PREP(EFUSE_OP_ADDR, offset) | 71 + EFUSE_OP_START, priv->base + EFUSE_OP_CTRL_0); 72 + 73 + ret = readl_poll_timeout(priv->base + EFUSE_OP_CTRL_1, 74 + reg, reg & EFUSE_OP_DONE, 2000, 75 + EFUSE_READ_TIMEOUT_US); 76 + 77 + *byte = FIELD_GET(EFUSE_OP_RD_DATA, reg); 78 + 79 + return ret; 80 + } 81 + 82 + static int motorcomm_efuse_read_patch(struct dwmac_motorcomm_priv *priv, 83 + u8 index, 84 + struct motorcomm_efuse_patch *patch) 85 + { 86 + u8 *p = (u8 *)patch, offset; 87 + int i, ret; 88 + 89 + for (i = 0; i < sizeof(*patch); i++) { 90 + offset = EFUSE_PATCH_REGION_OFFSET + sizeof(*patch) * index + i; 91 + 92 + ret = motorcomm_efuse_read_byte(priv, offset, &p[i]); 93 + if (ret) 94 + return ret; 95 + } 96 + 97 + return 0; 98 + } 99 + 100 + static int motorcomm_efuse_get_patch_value(struct dwmac_motorcomm_priv *priv, 101 + u16 addr, u32 *value) 102 + { 103 + struct motorcomm_efuse_patch patch; 104 + int i, ret; 105 + 106 + for (i = 0; i < EFUSE_PATCH_MAX_NUM; i++) { 107 + ret = motorcomm_efuse_read_patch(priv, i, &patch); 108 + if (ret) 109 + return ret; 110 + 111 + if (patch.addr == 0) { 112 + return -ENOENT; 113 + } else if (le16_to_cpu(patch.addr) == addr) { 114 + *value = le32_to_cpu(patch.data); 115 + return 0; 116 + } 117 + } 118 + 119 + return -ENOENT; 120 + } 121 + 122 + static int motorcomm_efuse_read_mac(struct device *dev, 123 + struct dwmac_motorcomm_priv *priv, u8 *mac) 124 + { 125 + u32 maca0lr, maca0hr; 126 + int ret; 127 + 128 + ret = motorcomm_efuse_get_patch_value(priv, EFUSE_ADDR_MACA0LR, 129 + &maca0lr); 130 + if (ret) 131 + return dev_err_probe(dev, ret, 132 + "failed to read maca0lr from eFuse\n"); 133 + 134 + ret = motorcomm_efuse_get_patch_value(priv, EFUSE_ADDR_MACA0HR, 135 + &maca0hr); 136 + if (ret) 137 + return dev_err_probe(dev, ret, 138 + "failed to read maca0hr from eFuse\n"); 139 + 140 + mac[0] = FIELD_GET(GENMASK(15, 8), maca0hr); 141 + mac[1] = FIELD_GET(GENMASK(7, 0), maca0hr); 142 + mac[2] = FIELD_GET(GENMASK(31, 24), maca0lr); 143 + mac[3] = FIELD_GET(GENMASK(23, 16), maca0lr); 144 + mac[4] = FIELD_GET(GENMASK(15, 8), maca0lr); 145 + mac[5] = FIELD_GET(GENMASK(7, 0), maca0lr); 146 + 147 + return 0; 148 + } 149 + 150 + static void motorcomm_deassert_mdio_phy_reset(struct dwmac_motorcomm_priv *priv) 151 + { 152 + u32 reg = readl(priv->base + EPHY_CTRL); 153 + 154 + reg |= EPHY_MDIO_PHY_RESET; 155 + 156 + writel(reg, priv->base + EPHY_CTRL); 157 + } 158 + 159 + static void motorcomm_reset(struct dwmac_motorcomm_priv *priv) 160 + { 161 + u32 reg = readl(priv->base + SYS_RESET); 162 + 163 + reg &= ~SYS_RESET_RESET; 164 + writel(reg, priv->base + SYS_RESET); 165 + 166 + reg |= SYS_RESET_RESET; 167 + writel(reg, priv->base + SYS_RESET); 168 + 169 + motorcomm_deassert_mdio_phy_reset(priv); 170 + } 171 + 172 + static void motorcomm_init(struct dwmac_motorcomm_priv *priv) 173 + { 174 + writel(0x0, priv->base + MGMT_INT_CTRL0); 175 + 176 + writel(FIELD_PREP(INT_MODERATION_RX, 200) | 177 + FIELD_PREP(INT_MODERATION_TX, 200), 178 + priv->base + INT_MODERATION); 179 + 180 + /* 181 + * OOB WOL must be disabled during normal operation, or DMA interrupts 182 + * cannot be delivered to the host. 183 + */ 184 + writel(OOB_WOL_CTRL_DIS, priv->base + OOB_WOL_CTRL); 185 + } 186 + 187 + static int motorcomm_resume(struct device *dev, void *bsp_priv) 188 + { 189 + struct dwmac_motorcomm_priv *priv = bsp_priv; 190 + int ret; 191 + 192 + ret = stmmac_pci_plat_resume(dev, bsp_priv); 193 + if (ret) 194 + return ret; 195 + 196 + /* 197 + * When recovering from D3hot, EPHY_MDIO_PHY_RESET is automatically 198 + * asserted, and must be deasserted for normal operation. 199 + */ 200 + motorcomm_deassert_mdio_phy_reset(priv); 201 + motorcomm_init(priv); 202 + 203 + return 0; 204 + } 205 + 206 + static struct plat_stmmacenet_data * 207 + motorcomm_default_plat_data(struct pci_dev *pdev) 208 + { 209 + struct plat_stmmacenet_data *plat; 210 + struct device *dev = &pdev->dev; 211 + 212 + plat = stmmac_plat_dat_alloc(dev); 213 + if (!plat) 214 + return NULL; 215 + 216 + plat->mdio_bus_data = devm_kzalloc(dev, sizeof(*plat->mdio_bus_data), 217 + GFP_KERNEL); 218 + if (!plat->mdio_bus_data) 219 + return NULL; 220 + 221 + plat->dma_cfg = devm_kzalloc(dev, sizeof(*plat->dma_cfg), GFP_KERNEL); 222 + if (!plat->dma_cfg) 223 + return NULL; 224 + 225 + plat->axi = devm_kzalloc(dev, sizeof(*plat->axi), GFP_KERNEL); 226 + if (!plat->axi) 227 + return NULL; 228 + 229 + plat->dma_cfg->pbl = DEFAULT_DMA_PBL; 230 + plat->dma_cfg->pblx8 = true; 231 + plat->dma_cfg->txpbl = 32; 232 + plat->dma_cfg->rxpbl = 32; 233 + plat->dma_cfg->eame = true; 234 + plat->dma_cfg->mixed_burst = true; 235 + 236 + plat->axi->axi_wr_osr_lmt = 1; 237 + plat->axi->axi_rd_osr_lmt = 1; 238 + plat->axi->axi_mb = true; 239 + plat->axi->axi_blen_regval = DMA_AXI_BLEN4 | DMA_AXI_BLEN8 | 240 + DMA_AXI_BLEN16 | DMA_AXI_BLEN32; 241 + 242 + plat->bus_id = pci_dev_id(pdev); 243 + plat->phy_interface = PHY_INTERFACE_MODE_GMII; 244 + /* 245 + * YT6801 requires an 25MHz clock input/oscillator to function, which 246 + * is likely the source of CSR clock. 247 + */ 248 + plat->clk_csr = STMMAC_CSR_20_35M; 249 + plat->tx_coe = 1; 250 + plat->rx_coe = 1; 251 + plat->clk_ref_rate = 125000000; 252 + plat->core_type = DWMAC_CORE_GMAC4; 253 + plat->suspend = stmmac_pci_plat_suspend; 254 + plat->resume = motorcomm_resume; 255 + plat->flags = STMMAC_FLAG_TSO_EN | 256 + STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP; 257 + 258 + return plat; 259 + } 260 + 261 + static void motorcomm_free_irq(void *data) 262 + { 263 + struct pci_dev *pdev = data; 264 + 265 + pci_free_irq_vectors(pdev); 266 + } 267 + 268 + static int motorcomm_setup_irq(struct pci_dev *pdev, 269 + struct stmmac_resources *res, 270 + struct plat_stmmacenet_data *plat) 271 + { 272 + int ret; 273 + 274 + ret = pci_alloc_irq_vectors(pdev, 6, 6, PCI_IRQ_MSIX); 275 + if (ret > 0) { 276 + res->rx_irq[0] = pci_irq_vector(pdev, 0); 277 + res->tx_irq[0] = pci_irq_vector(pdev, 4); 278 + res->irq = pci_irq_vector(pdev, 5); 279 + 280 + plat->flags |= STMMAC_FLAG_MULTI_MSI_EN; 281 + } else { 282 + dev_info(&pdev->dev, "failed to allocate MSI-X vector: %d\n", 283 + ret); 284 + dev_info(&pdev->dev, "try MSI instead\n"); 285 + 286 + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); 287 + if (ret < 0) 288 + return dev_err_probe(&pdev->dev, ret, 289 + "failed to allocate MSI\n"); 290 + 291 + res->irq = pci_irq_vector(pdev, 0); 292 + } 293 + 294 + return devm_add_action_or_reset(&pdev->dev, motorcomm_free_irq, pdev); 295 + } 296 + 297 + static int motorcomm_probe(struct pci_dev *pdev, const struct pci_device_id *id) 298 + { 299 + struct plat_stmmacenet_data *plat; 300 + struct dwmac_motorcomm_priv *priv; 301 + struct stmmac_resources res = {}; 302 + int ret; 303 + 304 + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 305 + if (!priv) 306 + return -ENOMEM; 307 + 308 + plat = motorcomm_default_plat_data(pdev); 309 + if (!plat) 310 + return -ENOMEM; 311 + 312 + plat->bsp_priv = priv; 313 + 314 + ret = pcim_enable_device(pdev); 315 + if (ret) 316 + return dev_err_probe(&pdev->dev, ret, 317 + "failed to enable device\n"); 318 + 319 + priv->base = pcim_iomap_region(pdev, 0, DRIVER_NAME); 320 + if (IS_ERR(priv->base)) 321 + return dev_err_probe(&pdev->dev, PTR_ERR(priv->base), 322 + "failed to map IO region\n"); 323 + 324 + pci_set_master(pdev); 325 + 326 + /* 327 + * Some PCIe addons cards based on YT6801 don't deliver MSI(X) with ASPM 328 + * enabled. Sadly there isn't a reliable way to read out OEM of the 329 + * card, so let's disable L1 state unconditionally for safety. 330 + */ 331 + ret = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1); 332 + if (ret) 333 + dev_warn(&pdev->dev, "failed to disable L1 state: %d\n", ret); 334 + 335 + motorcomm_reset(priv); 336 + 337 + ret = motorcomm_efuse_read_mac(&pdev->dev, priv, res.mac); 338 + if (ret == -ENOENT) { 339 + dev_warn(&pdev->dev, "eFuse contains no valid MAC address\n"); 340 + dev_warn(&pdev->dev, "fallback to random MAC address\n"); 341 + 342 + eth_random_addr(res.mac); 343 + } else if (ret) { 344 + return dev_err_probe(&pdev->dev, ret, 345 + "failed to read MAC address from eFuse\n"); 346 + } 347 + 348 + ret = motorcomm_setup_irq(pdev, &res, plat); 349 + if (ret) 350 + return dev_err_probe(&pdev->dev, ret, "failed to setup IRQ\n"); 351 + 352 + motorcomm_init(priv); 353 + 354 + res.addr = priv->base + GMAC_OFFSET; 355 + 356 + return stmmac_dvr_probe(&pdev->dev, plat, &res); 357 + } 358 + 359 + static void motorcomm_remove(struct pci_dev *pdev) 360 + { 361 + stmmac_dvr_remove(&pdev->dev); 362 + } 363 + 364 + static const struct pci_device_id dwmac_motorcomm_pci_id_table[] = { 365 + { PCI_DEVICE(PCI_VENDOR_ID_MOTORCOMM, 0x6801) }, 366 + { }, 367 + }; 368 + MODULE_DEVICE_TABLE(pci, dwmac_motorcomm_pci_id_table); 369 + 370 + static struct pci_driver dwmac_motorcomm_pci_driver = { 371 + .name = DRIVER_NAME, 372 + .id_table = dwmac_motorcomm_pci_id_table, 373 + .probe = motorcomm_probe, 374 + .remove = motorcomm_remove, 375 + .driver = { 376 + .pm = &stmmac_simple_pm_ops, 377 + }, 378 + }; 379 + 380 + module_pci_driver(dwmac_motorcomm_pci_driver); 381 + 382 + MODULE_DESCRIPTION("DWMAC glue driver for Motorcomm PCI Ethernet controllers"); 383 + MODULE_AUTHOR("Yao Zi <me@ziyao.cc>"); 384 + MODULE_LICENSE("GPL");
+4
drivers/net/phy/motorcomm.c
··· 910 910 val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg) | 911 911 FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg); 912 912 break; 913 + case PHY_INTERFACE_MODE_GMII: 914 + if (phydev->drv->phy_id != PHY_ID_YT8531S) 915 + return -EOPNOTSUPP; 916 + return 0; 913 917 default: /* do not support other modes */ 914 918 return -EOPNOTSUPP; 915 919 }