pcnet32.c
来自「linux 内核源代码」· C语言 代码 · 共 2,361 行 · 第 1/5 页
C
2,361 行
netif_wake_queue(dev); } spin_unlock_irqrestore(&lp->lock, flags); if (work_done < budget) { spin_lock_irqsave(&lp->lock, flags); __netif_rx_complete(dev, napi); /* clear interrupt masks */ val = lp->a.read_csr(ioaddr, CSR3); val &= 0x00ff; lp->a.write_csr(ioaddr, CSR3, val); /* Set interrupt enable. */ lp->a.write_csr(ioaddr, CSR0, CSR0_INTEN); mmiowb(); spin_unlock_irqrestore(&lp->lock, flags); } return work_done;}#endif#define PCNET32_REGS_PER_PHY 32#define PCNET32_MAX_PHYS 32static int pcnet32_get_regs_len(struct net_device *dev){ struct pcnet32_private *lp = netdev_priv(dev); int j = lp->phycount * PCNET32_REGS_PER_PHY; return ((PCNET32_NUM_REGS + j) * sizeof(u16));}static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *ptr){ int i, csr0; u16 *buff = ptr; struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; unsigned long flags; spin_lock_irqsave(&lp->lock, flags); csr0 = a->read_csr(ioaddr, CSR0); if (!(csr0 & CSR0_STOP)) /* If not stopped */ pcnet32_suspend(dev, &flags, 1); /* read address PROM */ for (i = 0; i < 16; i += 2) *buff++ = inw(ioaddr + i); /* read control and status registers */ for (i = 0; i < 90; i++) { *buff++ = a->read_csr(ioaddr, i); } *buff++ = a->read_csr(ioaddr, 112); *buff++ = a->read_csr(ioaddr, 114); /* read bus configuration registers */ for (i = 0; i < 30; i++) { *buff++ = a->read_bcr(ioaddr, i); } *buff++ = 0; /* skip bcr30 so as not to hang 79C976 */ for (i = 31; i < 36; i++) { *buff++ = a->read_bcr(ioaddr, i); } /* read mii phy registers */ if (lp->mii) { int j; for (j = 0; j < PCNET32_MAX_PHYS; j++) { if (lp->phymask & (1 << j)) { for (i = 0; i < PCNET32_REGS_PER_PHY; i++) { lp->a.write_bcr(ioaddr, 33, (j << 5) | i); *buff++ = lp->a.read_bcr(ioaddr, 34); } } } } if (!(csr0 & CSR0_STOP)) { /* If not stopped */ int csr5; /* clear SUSPEND (SPND) - CSR5 bit 0 */ csr5 = a->read_csr(ioaddr, CSR5); a->write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND)); } spin_unlock_irqrestore(&lp->lock, flags);}static const struct ethtool_ops pcnet32_ethtool_ops = { .get_settings = pcnet32_get_settings, .set_settings = pcnet32_set_settings, .get_drvinfo = pcnet32_get_drvinfo, .get_msglevel = pcnet32_get_msglevel, .set_msglevel = pcnet32_set_msglevel, .nway_reset = pcnet32_nway_reset, .get_link = pcnet32_get_link, .get_ringparam = pcnet32_get_ringparam, .set_ringparam = pcnet32_set_ringparam, .get_strings = pcnet32_get_strings, .self_test = pcnet32_ethtool_test, .phys_id = pcnet32_phys_id, .get_regs_len = pcnet32_get_regs_len, .get_regs = pcnet32_get_regs, .get_sset_count = pcnet32_get_sset_count,};/* only probes for non-PCI devices, the rest are handled by * pci_register_driver via pcnet32_probe_pci */static void __devinit pcnet32_probe_vlbus(unsigned int *pcnet32_portlist){ unsigned int *port, ioaddr; /* search for PCnet32 VLB cards at known addresses */ for (port = pcnet32_portlist; (ioaddr = *port); port++) { if (request_region (ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_vlbus")) { /* check if there is really a pcnet chip on that ioaddr */ if ((inb(ioaddr + 14) == 0x57) && (inb(ioaddr + 15) == 0x57)) { pcnet32_probe1(ioaddr, 0, NULL); } else { release_region(ioaddr, PCNET32_TOTAL_SIZE); } } }}static int __devinitpcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent){ unsigned long ioaddr; int err; err = pci_enable_device(pdev); if (err < 0) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "failed to enable device -- err=%d\n", err); return err; } pci_set_master(pdev); ioaddr = pci_resource_start(pdev, 0); if (!ioaddr) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "card has no PCI IO resources, aborting\n"); return -ENODEV; } if (!pci_dma_supported(pdev, PCNET32_DMA_MASK)) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "architecture does not support 32bit PCI busmaster DMA\n"); return -ENODEV; } if (request_region(ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_pci") == NULL) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "io address range already allocated\n"); return -EBUSY; } err = pcnet32_probe1(ioaddr, 1, pdev); if (err < 0) { pci_disable_device(pdev); } return err;}/* pcnet32_probe1 * Called from both pcnet32_probe_vlbus and pcnet_probe_pci. * pdev will be NULL when called from pcnet32_probe_vlbus. */static int __devinitpcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev){ struct pcnet32_private *lp; int i, media; int fdx, mii, fset, dxsuflo; int chip_version; char *chipname; struct net_device *dev; struct pcnet32_access *a = NULL; u8 promaddr[6]; int ret = -ENODEV; /* reset the chip */ pcnet32_wio_reset(ioaddr); /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */ if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && pcnet32_wio_check(ioaddr)) { a = &pcnet32_wio; } else { pcnet32_dwio_reset(ioaddr); if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { a = &pcnet32_dwio; } else goto err_release_region; } chip_version = a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr, 89) << 16); if ((pcnet32_debug & NETIF_MSG_PROBE) && (pcnet32_debug & NETIF_MSG_HW)) printk(KERN_INFO " PCnet chip version is %#x.\n", chip_version); if ((chip_version & 0xfff) != 0x003) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_INFO PFX "Unsupported chip version.\n"); goto err_release_region; } /* initialize variables */ fdx = mii = fset = dxsuflo = 0; chip_version = (chip_version >> 12) & 0xffff; switch (chip_version) { case 0x2420: chipname = "PCnet/PCI 79C970"; /* PCI */ break; case 0x2430: if (shared) chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */ else chipname = "PCnet/32 79C965"; /* 486/VL bus */ break; case 0x2621: chipname = "PCnet/PCI II 79C970A"; /* PCI */ fdx = 1; break; case 0x2623: chipname = "PCnet/FAST 79C971"; /* PCI */ fdx = 1; mii = 1; fset = 1; break; case 0x2624: chipname = "PCnet/FAST+ 79C972"; /* PCI */ fdx = 1; mii = 1; fset = 1; break; case 0x2625: chipname = "PCnet/FAST III 79C973"; /* PCI */ fdx = 1; mii = 1; break; case 0x2626: chipname = "PCnet/Home 79C978"; /* PCI */ fdx = 1; /* * This is based on specs published at www.amd.com. This section * assumes that a card with a 79C978 wants to go into standard * ethernet mode. The 79C978 can also go into 1Mb HomePNA mode, * and the module option homepna=1 can select this instead. */ media = a->read_bcr(ioaddr, 49); media &= ~3; /* default to 10Mb ethernet */ if (cards_found < MAX_UNITS && homepna[cards_found]) media |= 1; /* switch to home wiring mode */ if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_DEBUG PFX "media set to %sMbit mode.\n", (media & 1) ? "1" : "10"); a->write_bcr(ioaddr, 49, media); break; case 0x2627: chipname = "PCnet/FAST III 79C975"; /* PCI */ fdx = 1; mii = 1; break; case 0x2628: chipname = "PCnet/PRO 79C976"; fdx = 1; mii = 1; break; default: if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_INFO PFX "PCnet version %#x, no PCnet32 chip.\n", chip_version); goto err_release_region; } /* * On selected chips turn on the BCR18:NOUFLO bit. This stops transmit * starting until the packet is loaded. Strike one for reliability, lose * one for latency - although on PCI this isnt a big loss. Older chips * have FIFO's smaller than a packet, so you can't do this. * Turn on BCR18:BurstRdEn and BCR18:BurstWrEn. */ if (fset) { a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0860)); a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00); dxsuflo = 1; } dev = alloc_etherdev(sizeof(*lp)); if (!dev) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "Memory allocation failed.\n"); ret = -ENOMEM; goto err_release_region; } SET_NETDEV_DEV(dev, &pdev->dev); if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_INFO PFX "%s at %#3lx,", chipname, ioaddr); /* In most chips, after a chip reset, the ethernet address is read from the * station address PROM at the base address and programmed into the * "Physical Address Registers" CSR12-14. * As a precautionary measure, we read the PROM values and complain if * they disagree with the CSRs. If they miscompare, and the PROM addr * is valid, then the PROM addr is used. */ for (i = 0; i < 3; i++) { unsigned int val; val = a->read_csr(ioaddr, i + 12) & 0x0ffff; /* There may be endianness issues here. */ dev->dev_addr[2 * i] = val & 0x0ff; dev->dev_addr[2 * i + 1] = (val >> 8) & 0x0ff; } /* read PROM address and compare with CSR address */ for (i = 0; i < 6; i++) promaddr[i] = inb(ioaddr + i); if (memcmp(promaddr, dev->dev_addr, 6) || !is_valid_ether_addr(dev->dev_addr)) { if (is_valid_ether_addr(promaddr)) { if (pcnet32_debug & NETIF_MSG_PROBE) { printk(" warning: CSR address invalid,\n"); printk(KERN_INFO " using instead PROM address of"); } memcpy(dev->dev_addr, promaddr, 6); } } memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */ if (!is_valid_ether_addr(dev->perm_addr)) memset(dev->dev_addr, 0, sizeof(dev->dev_addr)); if (pcnet32_debug & NETIF_MSG_PROBE) { for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i]); /* Version 0x2623 and 0x2624 */ if (((chip_version + 1) & 0xfffe) == 0x2624) { i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */ printk("\n" KERN_INFO " tx_start_pt(0x%04x):", i); switch (i >> 10) { case 0: printk(" 20 bytes,"); break; case 1: printk(" 64 bytes,"); break; case 2: printk(" 128 bytes,"); break; case 3: printk("~220 bytes,"); break; } i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */ printk(" BCR18(%x):", i & 0xffff); if (i & (1 << 5)) printk("BurstWrEn "); if (i & (1 << 6)) printk("BurstRdEn "); if (i & (1 << 7)) printk("DWordIO "); if (i & (1 << 11)) printk("NoUFlow "); i = a->read_bcr(ioaddr, 25); printk("\n" KERN_INFO " SRAMSIZE=0x%04x,", i << 8); i = a->read_bcr(ioaddr, 26); printk(" SRAM_BND=0x%04x,", i << 8); i = a->read_bcr(ioaddr, 27); if (i & (1 << 14)) printk("LowLatRx"); } } dev->base_addr = ioaddr; lp = netdev_priv(dev); /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */ if ((lp->init_block = pci_alloc_consistent(pdev, sizeof(*lp->init_block), &lp->init_dma_addr)) == NULL) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "Consistent memory allocation failed.\n"); ret = -ENOMEM; goto err_free_netdev; } lp->pci_dev = pdev; lp->dev = dev; spin_lock_init(&lp->lock); SET_NETDEV_DEV(dev, &pdev->dev); lp->name = chipname; lp->shared_irq = shared; lp->tx_ring_size = TX_RING_SIZE; /* default tx ring size */ lp->rx_ring_size = RX_RING_SIZE; /* default rx ring size */ lp->tx_mod_mask = lp->tx_ring_size - 1; lp->rx_mod_mask = lp->rx_ring_size - 1; lp->tx_len_bits = (PCNET32_LOG_TX_BUFFERS << 12); lp->rx_len_bits = (PCNET32_LOG_RX_BUFFERS << 4); lp->mii_if.full_duplex = fdx; lp->mii_if.phy_id_mask = 0x1f; lp->mii_if.reg_num_mask = 0x1f; lp->dxsuflo = dxsuflo; lp->mii = mii; lp->chip_version = chip_version; lp->msg_enable = pcnet32_debug; if ((cards_found >= MAX_UNITS) || (options[cards_found] > sizeof(options_mapping))) lp->options = PCNET32_PORT_ASEL; else lp->options = options_mapping[options[cards_found]]; lp->mii_if.dev = dev; lp->mii_if.mdio_read = mdio_read; lp->mii_if.mdio_write = mdio_write; /* napi.weight is used in both the napi and non-napi cases */ lp->napi.weight = lp->rx_ring_size / 2;#ifdef CONFIG_PCNET32_NAPI netif_napi_add(dev, &lp->napi, pcnet32_poll, lp->rx_ring_size / 2);#endif if (fdx && !(lp->options & PCNET32_PORT_ASEL) && ((cards_found >= MAX_UNITS) || full_duplex[cards_found])) lp->options |= PCNET32_PORT_FD; if (!a) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "No access methods\n"); ret = -ENODEV; goto err_free_consistent; } lp->a = *a; /* prior to register_netdev, dev->name is not yet correct */ if (pcnet32_alloc_ring(dev, pci_name(lp->pci_dev))) { ret = -ENOMEM; goto err_free_ring; } /* detect special T1/E1 WAN card by checking for MAC address */ if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75) lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI; lp->init_block->mode = cpu_to_le16(0x0003); /* Disable Rx and Tx. */ lp->init_block->tlen_rlen = cpu_to_le16(lp->tx_len_bits | lp->rx_len_bits); for (i = 0; i < 6; i++)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?