FILE
dwmac-loongson.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2020, Loongson Corporation 3 */ 4 5#include <linux/clk-provider.h> 6#include <linux/pci.h> 7#include <linux/dmi.h> 8#include <linux/device.h> 9#include <linux/of_irq.h> 10#include "stmmac.h" 11#include "stmmac_libpci.h" 12#include "dwmac_dma.h" 13#include "dwmac1000.h" 14 15#define DRIVER_NAME "dwmac-loongson-pci" 16 17/* Normal Loongson Tx Summary */ 18#define DMA_INTR_ENA_NIE_TX_LOONGSON 0x00040000 19/* Normal Loongson Rx Summary */ 20#define DMA_INTR_ENA_NIE_RX_LOONGSON 0x00020000 21 22#define DMA_INTR_NORMAL_LOONGSON (DMA_INTR_ENA_NIE_TX_LOONGSON | \ 23 DMA_INTR_ENA_NIE_RX_LOONGSON | \ 24 DMA_INTR_ENA_RIE | DMA_INTR_ENA_TIE) 25 26/* Abnormal Loongson Tx Summary */ 27#define DMA_INTR_ENA_AIE_TX_LOONGSON 0x00010000 28/* Abnormal Loongson Rx Summary */ 29#define DMA_INTR_ENA_AIE_RX_LOONGSON 0x00008000 30 31#define DMA_INTR_ABNORMAL_LOONGSON (DMA_INTR_ENA_AIE_TX_LOONGSON | \ 32 DMA_INTR_ENA_AIE_RX_LOONGSON | \ 33 DMA_INTR_ENA_FBE | DMA_INTR_ENA_UNE) 34 35#define DMA_INTR_DEFAULT_MASK_LOONGSON (DMA_INTR_NORMAL_LOONGSON | \ 36 DMA_INTR_ABNORMAL_LOONGSON) 37 38/* Normal Loongson Tx Interrupt Summary */ 39#define DMA_STATUS_NIS_TX_LOONGSON 0x00040000 40/* Normal Loongson Rx Interrupt Summary */ 41#define DMA_STATUS_NIS_RX_LOONGSON 0x00020000 42 43/* Abnormal Loongson Tx Interrupt Summary */ 44#define DMA_STATUS_AIS_TX_LOONGSON 0x00010000 45/* Abnormal Loongson Rx Interrupt Summary */ 46#define DMA_STATUS_AIS_RX_LOONGSON 0x00008000 47 48/* Fatal Loongson Tx Bus Error Interrupt */ 49#define DMA_STATUS_FBI_TX_LOONGSON 0x00002000 50/* Fatal Loongson Rx Bus Error Interrupt */ 51#define DMA_STATUS_FBI_RX_LOONGSON 0x00001000 52 53#define DMA_STATUS_MSK_COMMON_LOONGSON (DMA_STATUS_NIS_TX_LOONGSON | \ 54 DMA_STATUS_NIS_RX_LOONGSON | \ 55 DMA_STATUS_AIS_TX_LOONGSON | \ 56 DMA_STATUS_AIS_RX_LOONGSON | \ 57 DMA_STATUS_FBI_TX_LOONGSON | \ 58 DMA_STATUS_FBI_RX_LOONGSON) 59 60#define DMA_STATUS_MSK_RX_LOONGSON (DMA_STATUS_ERI | DMA_STATUS_RWT | \ 61 DMA_STATUS_RPS | DMA_STATUS_RU | \ 62 DMA_STATUS_RI | DMA_STATUS_OVF | \ 63 DMA_STATUS_MSK_COMMON_LOONGSON) 64 65#define DMA_STATUS_MSK_TX_LOONGSON (DMA_STATUS_ETI | DMA_STATUS_UNF | \ 66 DMA_STATUS_TJT | DMA_STATUS_TU | \ 67 DMA_STATUS_TPS | DMA_STATUS_TI | \ 68 DMA_STATUS_MSK_COMMON_LOONGSON) 69 70#define PCI_DEVICE_ID_LOONGSON_GMAC1 0x7a03 71#define PCI_DEVICE_ID_LOONGSON_GMAC2 0x7a23 72#define PCI_DEVICE_ID_LOONGSON_GNET 0x7a13 73#define DWMAC_CORE_MULTICHAN_V1 0x10 /* Loongson custom ID 0x10 */ 74#define DWMAC_CORE_MULTICHAN_V2 0x12 /* Loongson custom ID 0x12 */ 75 76struct loongson_data { 77 u32 multichan; 78 u32 loongson_id; 79 struct device *dev; 80}; 81 82struct stmmac_pci_info { 83 int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat); 84}; 85 86static void loongson_default_data(struct pci_dev *pdev, 87 struct plat_stmmacenet_data *plat) 88{ 89 struct loongson_data *ld = plat->bsp_priv; 90 91 /* Get bus_id, this can be overwritten later */ 92 plat->bus_id = pci_dev_id(pdev); 93 94 /* clk_csr_i = 100-150MHz & MDC = clk_csr_i/62 */ 95 plat->clk_csr = STMMAC_CSR_100_150M; 96 plat->core_type = DWMAC_CORE_GMAC; 97 plat->force_sf_dma_mode = 1; 98 99 /* Increase the default value for multicast hash bins */ 100 plat->multicast_filter_bins = 256; 101 102 plat->clk_ref_rate = 125000000; 103 plat->clk_ptp_rate = 125000000; 104 105 plat->dma_cfg->pbl = 32; 106 plat->dma_cfg->pblx8 = true; 107 108 switch (ld->loongson_id) { 109 case DWMAC_CORE_MULTICHAN_V1: 110 ld->multichan = 1; 111 plat->rx_queues_to_use = 8; 112 plat->tx_queues_to_use = 8; 113 114 /* Only channel 0 supports checksum, 115 * so turn off checksum to enable multiple channels. 116 */ 117 for (int i = 1; i < 8; i++) 118 plat->tx_queues_cfg[i].coe_unsupported = 1; 119 120 break; 121 case DWMAC_CORE_MULTICHAN_V2: 122 ld->multichan = 1; 123 plat->rx_queues_to_use = 4; 124 plat->tx_queues_to_use = 4; 125 break; 126 default: 127 ld->multichan = 0; 128 break; 129 } 130} 131 132static int loongson_gmac_data(struct pci_dev *pdev, 133 struct plat_stmmacenet_data *plat) 134{ 135 loongson_default_data(pdev, plat); 136 137 plat->phy_interface = PHY_INTERFACE_MODE_RGMII_ID; 138 139 return 0; 140} 141 142static struct stmmac_pci_info loongson_gmac_pci_info = { 143 .setup = loongson_gmac_data, 144}; 145 146static void loongson_gnet_fix_speed(void *priv, int speed, unsigned int mode) 147{ 148 struct loongson_data *ld = (struct loongson_data *)priv; 149 struct net_device *ndev = dev_get_drvdata(ld->dev); 150 struct stmmac_priv *ptr = netdev_priv(ndev); 151 152 /* The integrated PHY has a weird problem with switching from the low 153 * speeds to 1000Mbps mode. The speedup procedure requires the PHY-link 154 * re-negotiation. 155 */ 156 if (speed == SPEED_1000) { 157 if (readl(ptr->ioaddr + MAC_CTRL_REG) & 158 GMAC_CONTROL_PS) 159 /* Word around hardware bug, restart autoneg */ 160 phy_restart_aneg(ndev->phydev); 161 } 162} 163 164static int loongson_gnet_data(struct pci_dev *pdev, 165 struct plat_stmmacenet_data *plat) 166{ 167 loongson_default_data(pdev, plat); 168 169 plat->phy_interface = PHY_INTERFACE_MODE_GMII; 170 plat->mdio_bus_data->phy_mask = ~(u32)BIT(2); 171 plat->fix_mac_speed = loongson_gnet_fix_speed; 172 173 return 0; 174} 175 176static struct stmmac_pci_info loongson_gnet_pci_info = { 177 .setup = loongson_gnet_data, 178}; 179 180static void loongson_dwmac_dma_init_channel(struct stmmac_priv *priv, 181 void __iomem *ioaddr, 182 struct stmmac_dma_cfg *dma_cfg, 183 u32 chan) 184{ 185 int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; 186 int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; 187 u32 value; 188 189 value = readl(ioaddr + DMA_CHAN_BUS_MODE(chan)); 190 191 if (dma_cfg->pblx8) 192 value |= DMA_BUS_MODE_MAXPBL; 193 194 value |= DMA_BUS_MODE_USP; 195 value &= ~(DMA_BUS_MODE_PBL_MASK | DMA_BUS_MODE_RPBL_MASK); 196 value |= (txpbl << DMA_BUS_MODE_PBL_SHIFT); 197 value |= (rxpbl << DMA_BUS_MODE_RPBL_SHIFT); 198 199 /* Set the Fixed burst mode */ 200 if (dma_cfg->fixed_burst) 201 value |= DMA_BUS_MODE_FB; 202 203 /* Mixed Burst has no effect when fb is set */ 204 if (dma_cfg->mixed_burst) 205 value |= DMA_BUS_MODE_MB; 206 207 if (dma_cfg->atds) 208 value |= DMA_BUS_MODE_ATDS; 209 210 if (dma_cfg->aal) 211 value |= DMA_BUS_MODE_AAL; 212 213 writel(value, ioaddr + DMA_CHAN_BUS_MODE(chan)); 214 215 /* Mask interrupts by writing to CSR7 */ 216 writel(DMA_INTR_DEFAULT_MASK_LOONGSON, ioaddr + 217 DMA_CHAN_INTR_ENA(chan)); 218} 219 220static int loongson_dwmac_dma_interrupt(struct stmmac_priv *priv, 221 void __iomem *ioaddr, 222 struct stmmac_extra_stats *x, 223 u32 chan, u32 dir) 224{ 225 struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); 226 u32 abnor_intr_status; 227 u32 nor_intr_status; 228 u32 fb_intr_status; 229 u32 intr_status; 230 int ret = 0; 231 232 /* read the status register (CSR5) */ 233 intr_status = readl(ioaddr + DMA_CHAN_STATUS(chan)); 234 235 if (dir == DMA_DIR_RX) 236 intr_status &= DMA_STATUS_MSK_RX_LOONGSON; 237 else if (dir == DMA_DIR_TX) 238 intr_status &= DMA_STATUS_MSK_TX_LOONGSON; 239 240 nor_intr_status = intr_status & (DMA_STATUS_NIS_TX_LOONGSON | 241 DMA_STATUS_NIS_RX_LOONGSON); 242 abnor_intr_status = intr_status & (DMA_STATUS_AIS_TX_LOONGSON | 243 DMA_STATUS_AIS_RX_LOONGSON); 244 fb_intr_status = intr_status & (DMA_STATUS_FBI_TX_LOONGSON | 245 DMA_STATUS_FBI_RX_LOONGSON); 246 247 /* ABNORMAL interrupts */ 248 if (unlikely(abnor_intr_status)) { 249 if (unlikely(intr_status & DMA_STATUS_UNF)) { 250 ret = tx_hard_error_bump_tc; 251 x->tx_undeflow_irq++; 252 } 253 if (unlikely(intr_status & DMA_STATUS_TJT)) 254 x->tx_jabber_irq++; 255 if (unlikely(intr_status & DMA_STATUS_OVF)) 256 x->rx_overflow_irq++; 257 if (unlikely(intr_status & DMA_STATUS_RU)) 258 x->rx_buf_unav_irq++; 259 if (unlikely(intr_status & DMA_STATUS_RPS)) 260 x->rx_process_stopped_irq++; 261 if (unlikely(intr_status & DMA_STATUS_RWT)) 262 x->rx_watchdog_irq++; 263 if (unlikely(intr_status & DMA_STATUS_ETI)) 264 x->tx_early_irq++; 265 if (unlikely(intr_status & DMA_STATUS_TPS)) { 266 x->tx_process_stopped_irq++; 267 ret = tx_hard_error; 268 } 269 if (unlikely(fb_intr_status)) { 270 x->fatal_bus_error_irq++; 271 ret = tx_hard_error; 272 } 273 } 274 /* TX/RX NORMAL interrupts */ 275 if (likely(nor_intr_status)) { 276 if (likely(intr_status & DMA_STATUS_RI)) { 277 u32 value = readl(ioaddr + DMA_INTR_ENA); 278 /* to schedule NAPI on real RIE event. */ 279 if (likely(value & DMA_INTR_ENA_RIE)) { 280 u64_stats_update_begin(&stats->syncp); 281 u64_stats_inc(&stats->rx_normal_irq_n[chan]); 282 u64_stats_update_end(&stats->syncp); 283 ret |= handle_rx; 284 } 285 } 286 if (likely(intr_status & DMA_STATUS_TI)) { 287 u64_stats_update_begin(&stats->syncp); 288 u64_stats_inc(&stats->tx_normal_irq_n[chan]); 289 u64_stats_update_end(&stats->syncp); 290 ret |= handle_tx; 291 } 292 if (unlikely(intr_status & DMA_STATUS_ERI)) 293 x->rx_early_irq++; 294 } 295 /* Optional hardware blocks, interrupts should be disabled */ 296 if (unlikely(intr_status & 297 (DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI))) 298 pr_warn("%s: unexpected status %08x\n", __func__, intr_status); 299 300 /* Clear the interrupt by writing a logic 1 to the CSR5[19-0] */ 301 writel((intr_status & 0x7ffff), ioaddr + DMA_CHAN_STATUS(chan)); 302 303 return ret; 304} 305 306static int loongson_dwmac_setup(void *apriv, struct mac_device_info *mac) 307{ 308 struct stmmac_priv *priv = apriv; 309 struct stmmac_dma_ops *dma; 310 struct loongson_data *ld; 311 struct pci_dev *pdev; 312 313 ld = priv->plat->bsp_priv; 314 pdev = to_pci_dev(priv->device); 315 316 dma = devm_kzalloc(priv->device, sizeof(*dma), GFP_KERNEL); 317 if (!dma) 318 return -ENOMEM; 319 320 /* The Loongson GMAC and GNET devices are based on the DW GMAC 321 * v3.50a and v3.73a IP-cores. But the HW designers have changed 322 * the GMAC_VERSION.SNPSVER field to the custom 0x10/0x12 value 323 * on the network controllers with the multi-channels feature 324 * available to emphasize the differences: multiple DMA-channels, 325 * AV feature and GMAC_INT_STATUS CSR flags layout. Get back the 326 * original value so the correct HW-interface would be selected. 327 */ 328 if (ld->multichan) { 329 priv->synopsys_id = DWMAC_CORE_3_70; 330 *dma = dwmac1000_dma_ops; 331 dma->init_chan = loongson_dwmac_dma_init_channel; 332 dma->dma_interrupt = loongson_dwmac_dma_interrupt; 333 mac->dma = dma; 334 } 335 336 priv->dev->priv_flags |= IFF_UNICAST_FLT; 337 338 /* Pre-initialize the respective "mac" fields as it's done in 339 * dwmac1000_setup() 340 */ 341 mac->pcsr = priv->ioaddr; 342 mac->multicast_filter_bins = priv->plat->multicast_filter_bins; 343 mac->unicast_filter_entries = priv->plat->unicast_filter_entries; 344 mac->mcast_bits_log2 = 0; 345 346 if (mac->multicast_filter_bins) 347 mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins); 348 349 /* Loongson GMAC doesn't support the flow control. Loongson GNET 350 * without multi-channel doesn't support the half-duplex link mode. 351 */ 352 if (pdev->device != PCI_DEVICE_ID_LOONGSON_GNET) { 353 mac->link.caps = MAC_10 | MAC_100 | MAC_1000; 354 } else { 355 if (ld->multichan) 356 mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | 357 MAC_10 | MAC_100 | MAC_1000; 358 else 359 mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | 360 MAC_10FD | MAC_100FD | MAC_1000FD; 361 } 362 363 mac->link.duplex = GMAC_CONTROL_DM; 364 mac->link.speed10 = GMAC_CONTROL_PS; 365 mac->link.speed100 = GMAC_CONTROL_PS | GMAC_CONTROL_FES; 366 mac->link.speed1000 = 0; 367 mac->link.speed_mask = GMAC_CONTROL_PS | GMAC_CONTROL_FES; 368 mac->mii.addr = GMAC_MII_ADDR; 369 mac->mii.data = GMAC_MII_DATA; 370 mac->mii.addr_shift = 11; 371 mac->mii.addr_mask = 0x0000F800; 372 mac->mii.reg_shift = 6; 373 mac->mii.reg_mask = 0x000007C0; 374 mac->mii.clk_csr_shift = 2; 375 mac->mii.clk_csr_mask = GENMASK(5, 2); 376 377 return 0; 378} 379 380static int loongson_dwmac_msi_config(struct pci_dev *pdev, 381 struct plat_stmmacenet_data *plat, 382 struct stmmac_resources *res) 383{ 384 int i, ch_num, ret, vecs; 385 386 ch_num = min(plat->tx_queues_to_use, plat->rx_queues_to_use); 387 388 vecs = roundup_pow_of_two(ch_num * 2 + 1); 389 ret = pci_alloc_irq_vectors(pdev, vecs, vecs, PCI_IRQ_MSI); 390 if (ret < 0) { 391 dev_warn(&pdev->dev, "Failed to allocate MSI IRQs\n"); 392 return ret; 393 } 394 395 res->irq = pci_irq_vector(pdev, 0); 396 397 for (i = 0; i < ch_num; i++) { 398 res->rx_irq[ch_num - 1 - i] = pci_irq_vector(pdev, 1 + i * 2); 399 } 400 401 for (i = 0; i < ch_num; i++) { 402 res->tx_irq[ch_num - 1 - i] = pci_irq_vector(pdev, 2 + i * 2); 403 } 404 405 plat->flags |= STMMAC_FLAG_MULTI_MSI_EN; 406 407 return 0; 408} 409 410static void loongson_dwmac_msi_clear(struct pci_dev *pdev) 411{ 412 pci_free_irq_vectors(pdev); 413} 414 415static int loongson_dwmac_dt_config(struct pci_dev *pdev, 416 struct plat_stmmacenet_data *plat, 417 struct stmmac_resources *res) 418{ 419 struct device_node *np = dev_of_node(&pdev->dev); 420 int ret; 421 422 plat->mdio_node = of_get_child_by_name(np, "mdio"); 423 if (plat->mdio_node) { 424 dev_info(&pdev->dev, "Found MDIO subnode\n"); 425 plat->mdio_bus_data->needs_reset = true; 426 } 427 428 ret = of_alias_get_id(np, "ethernet"); 429 if (ret >= 0) 430 plat->bus_id = ret; 431 432 res->irq = of_irq_get_byname(np, "macirq"); 433 if (res->irq < 0) { 434 dev_err(&pdev->dev, "IRQ macirq not found\n"); 435 ret = -ENODEV; 436 goto err_put_node; 437 } 438 439 res->wol_irq = of_irq_get_byname(np, "eth_wake_irq"); 440 if (res->wol_irq < 0) { 441 dev_info(&pdev->dev, 442 "IRQ eth_wake_irq not found, using macirq\n"); 443 res->wol_irq = res->irq; 444 } 445 446 ret = device_get_phy_mode(&pdev->dev); 447 if (ret < 0) { 448 dev_err(&pdev->dev, "phy_mode not found\n"); 449 ret = -ENODEV; 450 goto err_put_node; 451 } 452 453 plat->phy_interface = ret; 454 455 return 0; 456 457err_put_node: 458 of_node_put(plat->mdio_node); 459 460 return ret; 461} 462 463static void loongson_dwmac_dt_clear(struct pci_dev *pdev, 464 struct plat_stmmacenet_data *plat) 465{ 466 of_node_put(plat->mdio_node); 467} 468 469static int loongson_dwmac_acpi_config(struct pci_dev *pdev, 470 struct plat_stmmacenet_data *plat, 471 struct stmmac_resources *res) 472{ 473 if (!pdev->irq) 474 return -EINVAL; 475 476 res->irq = pdev->irq; 477 478 return 0; 479} 480 481/* Loongson's DWMAC device may take nearly two seconds to complete DMA reset */ 482static int loongson_dwmac_fix_reset(struct stmmac_priv *priv, void __iomem *ioaddr) 483{ 484 u32 value = readl(ioaddr + DMA_BUS_MODE); 485 486 if (value & DMA_BUS_MODE_SFT_RESET) { 487 netdev_err(priv->dev, "the PHY clock is missing\n"); 488 return -EINVAL; 489 } 490 491 value |= DMA_BUS_MODE_SFT_RESET; 492 writel(value, ioaddr + DMA_BUS_MODE); 493 494 return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value, 495 !(value & DMA_BUS_MODE_SFT_RESET), 496 10000, 2000000); 497} 498 499static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id *id) 500{ 501 struct plat_stmmacenet_data *plat; 502 struct stmmac_resources res = {}; 503 struct stmmac_pci_info *info; 504 struct loongson_data *ld; 505 int ret; 506 507 plat = stmmac_plat_dat_alloc(&pdev->dev); 508 if (!plat) 509 return -ENOMEM; 510 511 plat->mdio_bus_data = devm_kzalloc(&pdev->dev, 512 sizeof(*plat->mdio_bus_data), 513 GFP_KERNEL); 514 if (!plat->mdio_bus_data) 515 return -ENOMEM; 516 517 plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); 518 if (!plat->dma_cfg) 519 return -ENOMEM; 520 521 ld = devm_kzalloc(&pdev->dev, sizeof(*ld), GFP_KERNEL); 522 if (!ld) 523 return -ENOMEM; 524 525 /* Enable pci device */ 526 ret = pci_enable_device(pdev); 527 if (ret) { 528 dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n", __func__); 529 return ret; 530 } 531 532 pci_set_master(pdev); 533 534 /* Get the base address of device */ 535 res.addr = pcim_iomap_region(pdev, 0, DRIVER_NAME); 536 ret = PTR_ERR_OR_ZERO(res.addr); 537 if (ret) 538 goto err_disable_device; 539 540 plat->bsp_priv = ld; 541 plat->mac_setup = loongson_dwmac_setup; 542 plat->fix_soc_reset = loongson_dwmac_fix_reset; 543 plat->suspend = stmmac_pci_plat_suspend; 544 plat->resume = stmmac_pci_plat_resume; 545 ld->dev = &pdev->dev; 546 ld->loongson_id = readl(res.addr + GMAC_VERSION) & 0xff; 547 548 info = (struct stmmac_pci_info *)id->driver_data; 549 ret = info->setup(pdev, plat); 550 if (ret) 551 goto err_disable_device; 552 553 plat->tx_fifo_size = SZ_16K * plat->tx_queues_to_use; 554 plat->rx_fifo_size = SZ_16K * plat->rx_queues_to_use; 555 556 if (dev_of_node(&pdev->dev)) 557 ret = loongson_dwmac_dt_config(pdev, plat, &res); 558 else 559 ret = loongson_dwmac_acpi_config(pdev, plat, &res); 560 if (ret) 561 goto err_disable_device; 562 563 /* Use the common MAC IRQ if per-channel MSIs allocation failed */ 564 if (ld->multichan) 565 loongson_dwmac_msi_config(pdev, plat, &res); 566 567 ret = stmmac_dvr_probe(&pdev->dev, plat, &res); 568 if (ret) 569 goto err_plat_clear; 570 571 return 0; 572 573err_plat_clear: 574 if (dev_of_node(&pdev->dev)) 575 loongson_dwmac_dt_clear(pdev, plat); 576 if (ld->multichan) 577 loongson_dwmac_msi_clear(pdev); 578err_disable_device: 579 pci_disable_device(pdev); 580 return ret; 581} 582 583static void loongson_dwmac_remove(struct pci_dev *pdev) 584{ 585 struct net_device *ndev = dev_get_drvdata(&pdev->dev); 586 struct stmmac_priv *priv = netdev_priv(ndev); 587 struct loongson_data *ld; 588 589 ld = priv->plat->bsp_priv; 590 stmmac_dvr_remove(&pdev->dev); 591 592 if (dev_of_node(&pdev->dev)) 593 loongson_dwmac_dt_clear(pdev, priv->plat); 594 595 if (ld->multichan) 596 loongson_dwmac_msi_clear(pdev); 597 598 pci_disable_device(pdev); 599} 600 601static const struct pci_device_id loongson_dwmac_id_table[] = { 602 { PCI_DEVICE_DATA(LOONGSON, GMAC1, &loongson_gmac_pci_info) }, 603 { PCI_DEVICE_DATA(LOONGSON, GMAC2, &loongson_gmac_pci_info) }, 604 { PCI_DEVICE_DATA(LOONGSON, GNET, &loongson_gnet_pci_info) }, 605 {} 606}; 607MODULE_DEVICE_TABLE(pci, loongson_dwmac_id_table); 608 609static struct pci_driver loongson_dwmac_driver = { 610 .name = DRIVER_NAME, 611 .id_table = loongson_dwmac_id_table, 612 .probe = loongson_dwmac_probe, 613 .remove = loongson_dwmac_remove, 614 .driver = { 615 .pm = &stmmac_simple_pm_ops, 616 }, 617}; 618 619module_pci_driver(loongson_dwmac_driver); 620 621MODULE_DESCRIPTION("Loongson DWMAC PCI driver"); 622MODULE_AUTHOR("Qing Zhang <zhangqing@loongson.cn>"); 623MODULE_AUTHOR("Yanteng Si <siyanteng@loongson.cn>"); 624MODULE_LICENSE("GPL v2");
~/
Read-only file preview.