sundance.c
来自「linux 内核源代码」· C语言 代码 · 共 1,781 行 · 第 1/4 页
C
1,781 行
udelay(100); }}static int __devinit sundance_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent){ struct net_device *dev; struct netdev_private *np; static int card_idx; int chip_idx = ent->driver_data; int irq; int i; void __iomem *ioaddr; u16 mii_ctl; void *ring_space; dma_addr_t ring_dma;#ifdef USE_IO_OPS int bar = 0;#else int bar = 1;#endif int phy, phy_end, phy_idx = 0; DECLARE_MAC_BUF(mac);/* when built into the kernel, we only print version if device is found */#ifndef MODULE static int printed_version; if (!printed_version++) printk(version);#endif if (pci_enable_device(pdev)) return -EIO; pci_set_master(pdev); irq = pdev->irq; dev = alloc_etherdev(sizeof(*np)); if (!dev) return -ENOMEM; SET_NETDEV_DEV(dev, &pdev->dev); if (pci_request_regions(pdev, DRV_NAME)) goto err_out_netdev; ioaddr = pci_iomap(pdev, bar, netdev_io_size); if (!ioaddr) goto err_out_res; for (i = 0; i < 3; i++) ((__le16 *)dev->dev_addr)[i] = cpu_to_le16(eeprom_read(ioaddr, i + EEPROM_SA_OFFSET)); memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); dev->base_addr = (unsigned long)ioaddr; dev->irq = irq; np = netdev_priv(dev); np->base = ioaddr; np->pci_dev = pdev; np->chip_id = chip_idx; np->msg_enable = (1 << debug) - 1; spin_lock_init(&np->lock); tasklet_init(&np->rx_tasklet, rx_poll, (unsigned long)dev); tasklet_init(&np->tx_tasklet, tx_poll, (unsigned long)dev); ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma); if (!ring_space) goto err_out_cleardev; np->tx_ring = (struct netdev_desc *)ring_space; np->tx_ring_dma = ring_dma; ring_space = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &ring_dma); if (!ring_space) goto err_out_unmap_tx; np->rx_ring = (struct netdev_desc *)ring_space; np->rx_ring_dma = ring_dma; np->mii_if.dev = dev; np->mii_if.mdio_read = mdio_read; np->mii_if.mdio_write = mdio_write; np->mii_if.phy_id_mask = 0x1f; np->mii_if.reg_num_mask = 0x1f; /* The chip-specific entries in the device structure. */ dev->open = &netdev_open; dev->hard_start_xmit = &start_tx; dev->stop = &netdev_close; dev->get_stats = &get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &netdev_ioctl; SET_ETHTOOL_OPS(dev, ðtool_ops); dev->tx_timeout = &tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->change_mtu = &change_mtu; pci_set_drvdata(pdev, dev); i = register_netdev(dev); if (i) goto err_out_unmap_rx; printk(KERN_INFO "%s: %s at %p, %s, IRQ %d.\n", dev->name, pci_id_tbl[chip_idx].name, ioaddr, print_mac(mac, dev->dev_addr), irq); np->phys[0] = 1; /* Default setting */ np->mii_preamble_required++; /* * It seems some phys doesn't deal well with address 0 being accessed * first */ if (sundance_pci_tbl[np->chip_id].device == 0x0200) { phy = 0; phy_end = 31; } else { phy = 1; phy_end = 32; /* wraps to zero, due to 'phy & 0x1f' */ } for (; phy <= phy_end && phy_idx < MII_CNT; phy++) { int phyx = phy & 0x1f; int mii_status = mdio_read(dev, phyx, MII_BMSR); if (mii_status != 0xffff && mii_status != 0x0000) { np->phys[phy_idx++] = phyx; np->mii_if.advertising = mdio_read(dev, phyx, MII_ADVERTISE); if ((mii_status & 0x0040) == 0) np->mii_preamble_required++; printk(KERN_INFO "%s: MII PHY found at address %d, status " "0x%4.4x advertising %4.4x.\n", dev->name, phyx, mii_status, np->mii_if.advertising); } } np->mii_preamble_required--; if (phy_idx == 0) { printk(KERN_INFO "%s: No MII transceiver found, aborting. ASIC status %x\n", dev->name, ioread32(ioaddr + ASICCtrl)); goto err_out_unregister; } np->mii_if.phy_id = np->phys[0]; /* Parse override configuration */ np->an_enable = 1; if (card_idx < MAX_UNITS) { if (media[card_idx] != NULL) { np->an_enable = 0; if (strcmp (media[card_idx], "100mbps_fd") == 0 || strcmp (media[card_idx], "4") == 0) { np->speed = 100; np->mii_if.full_duplex = 1; } else if (strcmp (media[card_idx], "100mbps_hd") == 0 || strcmp (media[card_idx], "3") == 0) { np->speed = 100; np->mii_if.full_duplex = 0; } else if (strcmp (media[card_idx], "10mbps_fd") == 0 || strcmp (media[card_idx], "2") == 0) { np->speed = 10; np->mii_if.full_duplex = 1; } else if (strcmp (media[card_idx], "10mbps_hd") == 0 || strcmp (media[card_idx], "1") == 0) { np->speed = 10; np->mii_if.full_duplex = 0; } else { np->an_enable = 1; } } if (flowctrl == 1) np->flowctrl = 1; } /* Fibre PHY? */ if (ioread32 (ioaddr + ASICCtrl) & 0x80) { /* Default 100Mbps Full */ if (np->an_enable) { np->speed = 100; np->mii_if.full_duplex = 1; np->an_enable = 0; } } /* Reset PHY */ mdio_write (dev, np->phys[0], MII_BMCR, BMCR_RESET); mdelay (300); /* If flow control enabled, we need to advertise it.*/ if (np->flowctrl) mdio_write (dev, np->phys[0], MII_ADVERTISE, np->mii_if.advertising | 0x0400); mdio_write (dev, np->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART); /* Force media type */ if (!np->an_enable) { mii_ctl = 0; mii_ctl |= (np->speed == 100) ? BMCR_SPEED100 : 0; mii_ctl |= (np->mii_if.full_duplex) ? BMCR_FULLDPLX : 0; mdio_write (dev, np->phys[0], MII_BMCR, mii_ctl); printk (KERN_INFO "Override speed=%d, %s duplex\n", np->speed, np->mii_if.full_duplex ? "Full" : "Half"); } /* Perhaps move the reset here? */ /* Reset the chip to erase previous misconfiguration. */ if (netif_msg_hw(np)) printk("ASIC Control is %x.\n", ioread32(ioaddr + ASICCtrl)); sundance_reset(dev, 0x00ff << 16); if (netif_msg_hw(np)) printk("ASIC Control is now %x.\n", ioread32(ioaddr + ASICCtrl)); card_idx++; return 0;err_out_unregister: unregister_netdev(dev);err_out_unmap_rx: pci_free_consistent(pdev, RX_TOTAL_SIZE, np->rx_ring, np->rx_ring_dma);err_out_unmap_tx: pci_free_consistent(pdev, TX_TOTAL_SIZE, np->tx_ring, np->tx_ring_dma);err_out_cleardev: pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, ioaddr);err_out_res: pci_release_regions(pdev);err_out_netdev: free_netdev (dev); return -ENODEV;}static int change_mtu(struct net_device *dev, int new_mtu){ if ((new_mtu < 68) || (new_mtu > 8191)) /* Set by RxDMAFrameLen */ return -EINVAL; if (netif_running(dev)) return -EBUSY; dev->mtu = new_mtu; return 0;}#define eeprom_delay(ee_addr) ioread32(ee_addr)/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */static int __devinit eeprom_read(void __iomem *ioaddr, int location){ int boguscnt = 10000; /* Typical 1900 ticks. */ iowrite16(0x0200 | (location & 0xff), ioaddr + EECtrl); do { eeprom_delay(ioaddr + EECtrl); if (! (ioread16(ioaddr + EECtrl) & 0x8000)) { return ioread16(ioaddr + EEData); } } while (--boguscnt > 0); return 0;}/* MII transceiver control section. Read and write the MII registers using software-generated serial MDIO protocol. See the MII specifications or DP83840A data sheet for details. The maximum data clock rate is 2.5 Mhz. The minimum timing is usually met by back-to-back 33Mhz PCI cycles. */#define mdio_delay() ioread8(mdio_addr)enum mii_reg_bits { MDIO_ShiftClk=0x0001, MDIO_Data=0x0002, MDIO_EnbOutput=0x0004,};#define MDIO_EnbIn (0)#define MDIO_WRITE0 (MDIO_EnbOutput)#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput)/* Generate the preamble required for initial synchronization and a few older transceivers. */static void mdio_sync(void __iomem *mdio_addr){ int bits = 32; /* Establish sync by sending at least 32 logic ones. */ while (--bits >= 0) { iowrite8(MDIO_WRITE1, mdio_addr); mdio_delay(); iowrite8(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); mdio_delay(); }}static int mdio_read(struct net_device *dev, int phy_id, int location){ struct netdev_private *np = netdev_priv(dev); void __iomem *mdio_addr = np->base + MIICtrl; int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; int i, retval = 0; if (np->mii_preamble_required) mdio_sync(mdio_addr); /* Shift the read command bits out. */ for (i = 15; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; iowrite8(dataval, mdio_addr); mdio_delay(); iowrite8(dataval | MDIO_ShiftClk, mdio_addr); mdio_delay(); } /* Read the two transition, 16 data, and wire-idle bits. */ for (i = 19; i > 0; i--) { iowrite8(MDIO_EnbIn, mdio_addr); mdio_delay(); retval = (retval << 1) | ((ioread8(mdio_addr) & MDIO_Data) ? 1 : 0); iowrite8(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); mdio_delay(); } return (retval>>1) & 0xffff;}static void mdio_write(struct net_device *dev, int phy_id, int location, int value){ struct netdev_private *np = netdev_priv(dev); void __iomem *mdio_addr = np->base + MIICtrl; int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; int i; if (np->mii_preamble_required) mdio_sync(mdio_addr); /* Shift the command bits out. */ for (i = 31; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; iowrite8(dataval, mdio_addr); mdio_delay(); iowrite8(dataval | MDIO_ShiftClk, mdio_addr); mdio_delay(); } /* Clear out extra bits. */ for (i = 2; i > 0; i--) { iowrite8(MDIO_EnbIn, mdio_addr); mdio_delay(); iowrite8(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); mdio_delay(); } return;}static int netdev_open(struct net_device *dev){ struct netdev_private *np = netdev_priv(dev); void __iomem *ioaddr = np->base; unsigned long flags; int i; /* Do we need to reset the chip??? */ i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev); if (i) return i; if (netif_msg_ifup(np)) printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", dev->name, dev->irq); init_ring(dev); iowrite32(np->rx_ring_dma, ioaddr + RxListPtr); /* The Tx list pointer is written as packets are queued. */ /* Initialize other registers. */ __set_mac_addr(dev);#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) iowrite16(dev->mtu + 18, ioaddr + MaxFrameSize);#else iowrite16(dev->mtu + 14, ioaddr + MaxFrameSize);#endif if (dev->mtu > 2047) iowrite32(ioread32(ioaddr + ASICCtrl) | 0x0C, ioaddr + ASICCtrl); /* Configure the PCI bus bursts and FIFO thresholds. */ if (dev->if_port == 0) dev->if_port = np->default_port; spin_lock_init(&np->mcastlock); set_rx_mode(dev); iowrite16(0, ioaddr + IntrEnable); iowrite16(0, ioaddr + DownCounter); /* Set the chip to poll every N*320nsec. */ iowrite8(100, ioaddr + RxDMAPollPeriod); iowrite8(127, ioaddr + TxDMAPollPeriod); /* Fix DFE-580TX packet drop issue */ if (np->pci_dev->revision >= 0x14) iowrite8(0x01, ioaddr + DebugCtrl1); netif_start_queue(dev); spin_lock_irqsave(&np->lock, flags); reset_tx(dev); spin_unlock_irqrestore(&np->lock, flags); iowrite16 (StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1); if (netif_msg_ifup(np)) printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x " "MAC Control %x, %4.4x %4.4x.\n", dev->name, ioread32(ioaddr + RxStatus), ioread8(ioaddr + TxStatus), ioread32(ioaddr + MACCtrl0), ioread16(ioaddr + MACCtrl1), ioread16(ioaddr + MACCtrl0)); /* Set the timer to check for link beat. */ init_timer(&np->timer); np->timer.expires = jiffies + 3*HZ; np->timer.data = (unsigned long)dev; np->timer.function = &netdev_timer; /* timer handler */ add_timer(&np->timer); /* Enable interrupts by setting the interrupt mask. */ iowrite16(DEFAULT_INTR, ioaddr + IntrEnable); return 0;}static void check_duplex(struct net_device *dev){ struct netdev_private *np = netdev_priv(dev); void __iomem *ioaddr = np->base; int mii_lpa = mdio_read(dev, np->phys[0], MII_LPA); int negotiated = mii_lpa & np->mii_if.advertising; int duplex; /* Force media */ if (!np->an_enable || mii_lpa == 0xffff) { if (np->mii_if.full_duplex) iowrite16 (ioread16 (ioaddr + MACCtrl0) | EnbFullDuplex, ioaddr + MACCtrl0); return; } /* Autonegotiation */ duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; if (np->mii_if.full_duplex != duplex) { np->mii_if.full_duplex = duplex; if (netif_msg_link(np)) printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d " "negotiated capability %4.4x.\n", dev->name, duplex ? "full" : "half", np->phys[0], negotiated); iowrite16(ioread16(ioaddr + MACCtrl0) | duplex ? 0x20 : 0, ioaddr + MACCtrl0); }}static void netdev_timer(unsigned long data){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?