📄 pci-skeleton.c
字号:
DPRINTK ("ENTER\n"); if (phy_id > 31) { /* Really a 8139. Use internal registers. */ DPRINTK ("EXIT after directly using 8139 internal regs\n"); return location < 8 && mii_2_8139_map[location] ? readw (tp->mmio_addr + mii_2_8139_map[location]) : 0; } mdio_sync (mdio_addr); /* Shift the read command bits out. */ for (i = 15; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; writeb (MDIO_DIR | dataval, mdio_addr); mdio_delay (); writeb (MDIO_DIR | dataval | MDIO_CLK, mdio_addr); mdio_delay (); } /* Read the two transition, 16 data, and wire-idle bits. */ for (i = 19; i > 0; i--) { writeb (0, mdio_addr); mdio_delay (); retval = (retval << 1) | ((readb (mdio_addr) & MDIO_DATA_IN) ? 1 : 0); writeb (MDIO_CLK, mdio_addr); mdio_delay (); } DPRINTK ("EXIT, returning %d\n", (retval >> 1) & 0xffff); return (retval >> 1) & 0xffff;}static void mdio_write (struct net_device *dev, int phy_id, int location, int value){ struct netdrv_private *tp = dev->priv; void *mdio_addr = tp->mmio_addr + Config4; int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; int i; DPRINTK ("ENTER\n"); if (phy_id > 31) { /* Really a 8139. Use internal registers. */ if (location < 8 && mii_2_8139_map[location]) { writew (value, tp->mmio_addr + mii_2_8139_map[location]); readw (tp->mmio_addr + mii_2_8139_map[location]); } DPRINTK ("EXIT after directly using 8139 internal regs\n"); return; } 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; writeb (dataval, mdio_addr); mdio_delay (); writeb (dataval | MDIO_CLK, mdio_addr); mdio_delay (); } /* Clear out extra bits. */ for (i = 2; i > 0; i--) { writeb (0, mdio_addr); mdio_delay (); writeb (MDIO_CLK, mdio_addr); mdio_delay (); } DPRINTK ("EXIT\n");}static int netdrv_open (struct net_device *dev){ struct netdrv_private *tp = dev->priv; int retval;#ifdef NETDRV_DEBUG void *ioaddr = tp->mmio_addr;#endif DPRINTK ("ENTER\n"); retval = request_irq (dev->irq, netdrv_interrupt, SA_SHIRQ, dev->name, dev); if (retval) { DPRINTK ("EXIT, returning %d\n", retval); return retval; } tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN, &tp->tx_bufs_dma); tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN, &tp->rx_ring_dma); if (tp->tx_bufs == NULL || tp->rx_ring == NULL) { free_irq(dev->irq, dev); if (tp->tx_bufs) pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, tp->tx_bufs, tp->tx_bufs_dma); if (tp->rx_ring) pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, tp->rx_ring, tp->rx_ring_dma); DPRINTK ("EXIT, returning -ENOMEM\n"); return -ENOMEM; } tp->full_duplex = tp->duplex_lock; tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; netdrv_init_ring (dev); netdrv_hw_start (dev); DPRINTK ("%s: netdrv_open() ioaddr %#lx IRQ %d" " GP Pins %2.2x %s-duplex.\n", dev->name, pci_resource_start (tp->pci_dev, 1), dev->irq, NETDRV_R8 (MediaStatus), tp->full_duplex ? "full" : "half"); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ init_timer (&tp->timer); tp->timer.expires = jiffies + 3 * HZ; tp->timer.data = (unsigned long) dev; tp->timer.function = &netdrv_timer; add_timer (&tp->timer); DPRINTK ("EXIT, returning 0\n"); return 0;}/* Start the hardware at open or resume. */static void netdrv_hw_start (struct net_device *dev){ struct netdrv_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; u32 i; DPRINTK ("ENTER\n"); /* Soft reset the chip. */ NETDRV_W8 (ChipCmd, (NETDRV_R8 (ChipCmd) & ChipCmdClear) | CmdReset); udelay (100); /* Check that the chip has finished the reset. */ for (i = 1000; i > 0; i--) if ((NETDRV_R8 (ChipCmd) & CmdReset) == 0) break; /* Restore our idea of the MAC address. */ NETDRV_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); NETDRV_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); /* Must enable Tx/Rx before setting transfer thresholds! */ NETDRV_W8_F (ChipCmd, (NETDRV_R8 (ChipCmd) & ChipCmdClear) | CmdRxEnb | CmdTxEnb); i = netdrv_rx_config | (NETDRV_R32 (RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); NETDRV_W32_F (RxConfig, i); /* Check this value: the documentation for IFG contradicts ifself. */ NETDRV_W32 (TxConfig, (TX_DMA_BURST << TxDMAShift)); /* unlock Config[01234] and BMCR register writes */ NETDRV_W8_F (Cfg9346, Cfg9346_Unlock); udelay (10); tp->cur_rx = 0; /* Lock Config[01234] and BMCR register writes */ NETDRV_W8_F (Cfg9346, Cfg9346_Lock); udelay (10); /* init Rx ring buffer DMA address */ NETDRV_W32_F (RxBuf, tp->rx_ring_dma); /* init Tx buffer DMA addresses */ for (i = 0; i < NUM_TX_DESC; i++) NETDRV_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs)); NETDRV_W32_F (RxMissed, 0); netdrv_set_rx_mode (dev); /* no early-rx interrupts */ NETDRV_W16 (MultiIntr, NETDRV_R16 (MultiIntr) & MultiIntrClear); /* make sure RxTx has started */ NETDRV_W8_F (ChipCmd, (NETDRV_R8 (ChipCmd) & ChipCmdClear) | CmdRxEnb | CmdTxEnb); /* Enable all known interrupts by setting the interrupt mask. */ NETDRV_W16_F (IntrMask, netdrv_intr_mask); netif_start_queue (dev); DPRINTK ("EXIT\n");}/* Initialize the Rx and Tx rings, along with various 'dev' bits. */static void netdrv_init_ring (struct net_device *dev){ struct netdrv_private *tp = dev->priv; int i; DPRINTK ("ENTER\n"); tp->cur_rx = 0; atomic_set (&tp->cur_tx, 0); atomic_set (&tp->dirty_tx, 0); for (i = 0; i < NUM_TX_DESC; i++) { tp->tx_info[i].skb = NULL; tp->tx_info[i].mapping = 0; tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE]; } DPRINTK ("EXIT\n");}static void netdrv_timer (unsigned long data){ struct net_device *dev = (struct net_device *) data; struct netdrv_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int next_tick = 60 * HZ; int mii_lpa; mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); if (!tp->duplex_lock && mii_lpa != 0xffff) { int duplex = (mii_lpa & LPA_100FULL) || (mii_lpa & 0x01C0) == 0x0040; if (tp->full_duplex != duplex) { tp->full_duplex = duplex; printk (KERN_INFO "%s: Setting %s-duplex based on MII #%d link" " partner ability of %4.4x.\n", dev->name, tp->full_duplex ? "full" : "half", tp->phys[0], mii_lpa); NETDRV_W8 (Cfg9346, Cfg9346_Unlock); NETDRV_W8 (Config1, tp->full_duplex ? 0x60 : 0x20); NETDRV_W8 (Cfg9346, Cfg9346_Lock); } } DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n", dev->name, NETDRV_R16 (NWayLPAR)); DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x" " RxStatus %4.4x.\n", dev->name, NETDRV_R16 (IntrMask), NETDRV_R16 (IntrStatus), NETDRV_R32 (RxEarlyStatus)); DPRINTK ("%s: Chip config %2.2x %2.2x.\n", dev->name, NETDRV_R8 (Config0), NETDRV_R8 (Config1)); tp->timer.expires = jiffies + next_tick; add_timer (&tp->timer);}static void netdrv_tx_clear (struct netdrv_private *tp){ int i; atomic_set (&tp->cur_tx, 0); atomic_set (&tp->dirty_tx, 0); /* Dump the unsent Tx packets. */ for (i = 0; i < NUM_TX_DESC; i++) { struct ring_info *rp = &tp->tx_info[i]; if (rp->mapping != 0) { pci_unmap_single (tp->pci_dev, rp->mapping, rp->skb->len, PCI_DMA_TODEVICE); rp->mapping = 0; } if (rp->skb) { dev_kfree_skb (rp->skb); rp->skb = NULL; tp->stats.tx_dropped++; } }}static void netdrv_tx_timeout (struct net_device *dev){ struct netdrv_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int i; u8 tmp8; unsigned long flags; DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x " "media %2.2x.\n", dev->name, NETDRV_R8 (ChipCmd), NETDRV_R16 (IntrStatus), NETDRV_R8 (MediaStatus)); /* disable Tx ASAP, if not already */ tmp8 = NETDRV_R8 (ChipCmd); if (tmp8 & CmdTxEnb) NETDRV_W8 (ChipCmd, tmp8 & ~CmdTxEnb); /* Disable interrupts by clearing the interrupt mask. */ NETDRV_W16 (IntrMask, 0x0000); /* Emit info to figure out what went wrong. */ printk (KERN_DEBUG "%s: Tx queue start entry %d dirty entry %d.\n", dev->name, atomic_read (&tp->cur_tx), atomic_read (&tp->dirty_tx)); for (i = 0; i < NUM_TX_DESC; i++) printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n", dev->name, i, NETDRV_R32 (TxStatus0 + (i * 4)), i == atomic_read (&tp->dirty_tx) % NUM_TX_DESC ? " (queue head)" : ""); /* Stop a shared interrupt from scavenging while we are. */ spin_lock_irqsave (&tp->lock, flags); netdrv_tx_clear (tp); spin_unlock_irqrestore (&tp->lock, flags); /* ...and finally, reset everything */ netdrv_hw_start (dev); netif_wake_queue (dev);}static int netdrv_start_xmit (struct sk_buff *skb, struct net_device *dev){ struct netdrv_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int entry; /* Calculate the next Tx descriptor entry. */ entry = atomic_read (&tp->cur_tx) % NUM_TX_DESC; assert (tp->tx_info[entry].skb == NULL); assert (tp->tx_info[entry].mapping == 0); tp->tx_info[entry].skb = skb; /* tp->tx_info[entry].mapping = 0; */ memcpy (tp->tx_buf[entry], skb->data, skb->len); /* Note: the chip doesn't have auto-pad! */ NETDRV_W32 (TxStatus0 + (entry * sizeof(u32)), tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); dev->trans_start = jiffies; atomic_inc (&tp->cur_tx); if ((atomic_read (&tp->cur_tx) - atomic_read (&tp->dirty_tx)) >= NUM_TX_DESC) netif_stop_queue (dev); DPRINTK ("%s: Queued Tx packet at %p size %u to slot %d.\n", dev->name, skb->data, skb->len, entry); return 0;}static void netdrv_tx_interrupt (struct net_device *dev, struct netdrv_private *tp, void *ioaddr){ int cur_tx, dirty_tx, tx_left; assert (dev != NULL); assert (tp != NULL); assert (ioaddr != NULL); dirty_tx = atomic_read (&tp->dirty_tx); cur_tx = atomic_read (&tp->cur_tx); tx_left = cur_tx - dirty_tx; while (tx_left > 0) { int entry = dirty_tx % NUM_TX_DESC; int txstatus; txstatus = NETDRV_R32 (TxStatus0 + (entry * sizeof (u32))); if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) break; /* It still hasn't been Txed */ /* Note: TxCarrierLost is always asserted at 100mbps. */ if (txstatus & (TxOutOfWindow | TxAborted)) { /* There was an major error, log it. */ DPRINTK ("%s: Transmit error, Tx status %8.8x.\n", dev->name, txstatus); tp->stats.tx_errors++; if (txstatus & TxAborted) { tp->stats.tx_aborted_errors++; NETDRV_W32 (TxConfig, TxClearAbt | (TX_DMA_BURST << TxDMAShift)); } if (txstatus & TxCarrierLost) tp->stats.tx_carrier_errors++; if (txstatus & TxOutOfWindow) tp->stats.tx_window_errors++; } else { if (txstatus & TxUnderrun) { /* Add 64 to the Tx FIFO threshold. */ if (tp->tx_flag < 0x00300000) tp->tx_flag += 0x00020000; tp->stats.tx_fifo_errors++; } tp->stats.collisions += (txstatus >> 24) & 15; tp->stats.tx_bytes += txstatus & 0x7ff; tp->stats.tx_packets++; } /* Free the original skb. */ if (tp->tx_info[entry].mapping != 0) { pci_unmap_single(tp->pci_dev, tp->tx_info[entry].mapping, tp->tx_info[entry].skb->len, PCI_DMA_TODEVICE); tp->tx_info[entry].mapping = 0; } dev_kfree_skb_irq (tp->tx_info[entry].skb); tp->tx_info[entry].skb = NULL; dirty_tx++; if (dirty_tx < 0) { /* handle signed int overflow */ atomic_sub (cur_tx, &tp->cur_tx); /* XXX racy? */ dirty_tx = cur_tx - tx_left + 1; } if (netif_queue_stopped (dev)) netif_wake_queue (dev); cur_tx = atomic_read (&tp->cur_tx); tx_left = cur_tx - dirty_tx; }#ifndef NETDRV_NDEBUG if (atomic_read (&tp->cur_tx) - dirty_tx > NUM_TX_DESC) { printk (KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d.\n", dev->name, dirty_tx, atomic_read (&tp->cur_tx)); dirty_tx += NUM_TX_DESC; }#endif /* NETDRV_NDEBUG */ atomic_set (&tp->dirty_tx, dirty_tx);}/* TODO: clean this up! Rx reset need not be this intensive */static void netdrv_rx_err (u32 rx_status, struct net_device *dev, struct netdrv_private *tp, void *ioaddr){ u8 tmp8; int tmp_work = 1000; DPRINTK ("%s: Ethernet frame had errors, status %8.8x.\n", dev->name, rx_status); if (rx_status & RxTooLong) { DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", dev->name, rx_status); /* A.C.: The chip hangs here. */ } tp->stats.rx_errors++; if (rx_status & (RxBadSymbol | RxBadAlign)) tp->stats.rx_frame_errors++; if (rx_status & (RxRunt | RxTooLong)) tp->stats.rx_length_errors++; if (rx_status & RxCRCErr) tp->stats.rx_crc_errors++; /* Reset the receiver, based on RealTek recommendation. (Bug?) */ tp->cur_rx = 0; /* disable receive */ tmp8 = NETDRV_R8 (ChipCmd) & ChipCmdClear; NETDRV_W8_F (ChipCmd, tmp8 | CmdTxEnb); /* A.C.: Reset the multicast list. */ netdrv_set_rx_mode (dev); /* XXX potentially temporary hack to * restart hung receiver */ while (--tmp_work > 0) { tmp8 = NETDRV_R8 (ChipCmd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -