xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
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