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