📄 8139too.c
字号:
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 rtl8139_open (struct net_device *dev){ struct rtl8139_private *tp = dev->priv; int retval;#ifdef RTL8139_DEBUG void *ioaddr = tp->mmio_addr;#endif DPRINTK ("ENTER\n"); retval = request_irq (dev->irq, rtl8139_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; tp->twistie = 1; rtl8139_init_ring (dev); rtl8139_hw_start (dev); DPRINTK ("%s: rtl8139_open() ioaddr %#lx IRQ %d" " GP Pins %2.2x %s-duplex.\n", dev->name, pci_resource_start (tp->pci_dev, 1), dev->irq, RTL_R8 (MediaStatus), tp->full_duplex ? "full" : "half"); tp->thr_pid = kernel_thread (rtl8139_thread, dev, CLONE_FS | CLONE_FILES); if (tp->thr_pid < 0) printk (KERN_WARNING "%s: unable to start kernel thread\n", dev->name); DPRINTK ("EXIT, returning 0\n"); return 0;}/* Start the hardware at open or resume. */static void rtl8139_hw_start (struct net_device *dev){ struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; u32 i; u8 tmp; DPRINTK ("ENTER\n"); /* Soft reset the chip. */ RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) | CmdReset); udelay (100); /* Check that the chip has finished the reset. */ for (i = 1000; i > 0; i--) if ((RTL_R8 (ChipCmd) & CmdReset) == 0) break; /* Restore our idea of the MAC address. */ RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); /* Must enable Tx/Rx before setting transfer thresholds! */ RTL_W8_F (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) | CmdRxEnb | CmdTxEnb); i = rtl8139_rx_config | (RTL_R32 (RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); RTL_W32_F (RxConfig, i); /* Check this value: the documentation for IFG contradicts ifself. */ RTL_W32 (TxConfig, (TX_DMA_BURST << TxDMAShift)); /* unlock Config[01234] and BMCR register writes */ RTL_W8_F (Cfg9346, Cfg9346_Unlock); udelay (10); tp->cur_rx = 0; if (tp->chipset >= CH_8139A) { tmp = RTL_R8 (Config1) & Config1Clear; tmp |= Cfg1_Driver_Load; tmp |= (tp->chipset == CH_8139B) ? 3 : 1; /* Enable PM/VPD */ RTL_W8_F (Config1, tmp); } else { u8 foo = RTL_R8 (Config1) & Config1Clear; RTL_W8 (Config1, tp->full_duplex ? (foo|0x60) : (foo|0x20)); } if (tp->chipset >= CH_8139B) { tmp = RTL_R8 (Config4) & ~(1<<2); /* chip will clear Rx FIFO overflow automatically */ tmp |= (1<<7); RTL_W8 (Config4, tmp); /* disable magic packet scanning, which is enabled * when PM is enabled above (Config1) */ RTL_W8 (Config3, RTL_R8 (Config3) & ~(1<<5)); } /* Lock Config[01234] and BMCR register writes */ RTL_W8_F (Cfg9346, Cfg9346_Lock); udelay (10); /* init Rx ring buffer DMA address */ RTL_W32_F (RxBuf, tp->rx_ring_dma); /* init Tx buffer DMA addresses */ for (i = 0; i < NUM_TX_DESC; i++) RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs)); RTL_W32_F (RxMissed, 0); rtl8139_set_rx_mode (dev); /* no early-rx interrupts */ RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear); /* make sure RxTx has started */ RTL_W8_F (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) | CmdRxEnb | CmdTxEnb); /* Enable all known interrupts by setting the interrupt mask. */ RTL_W16_F (IntrMask, rtl8139_intr_mask); netif_start_queue (dev); DPRINTK ("EXIT\n");}/* Initialize the Rx and Tx rings, along with various 'dev' bits. */static void rtl8139_init_ring (struct net_device *dev){ struct rtl8139_private *tp = dev->priv; int i; DPRINTK ("ENTER\n"); tp->cur_rx = 0; tp->cur_tx = 0; 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");}/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */static int next_tick = 3 * HZ;#ifndef CONFIG_8139TOO_TUNE_TWISTERstatic inline void rtl8139_tune_twister (struct net_device *dev, struct rtl8139_private *tp) {}#elsestatic void rtl8139_tune_twister (struct net_device *dev, struct rtl8139_private *tp){ int linkcase; void *ioaddr = tp->mmio_addr; DPRINTK ("ENTER\n"); /* This is a complicated state machine to configure the "twister" for impedance/echos based on the cable length. All of this is magic and undocumented. */ switch (tp->twistie) { case 1: if (RTL_R16 (CSCR) & CSCR_LinkOKBit) { /* We have link beat, let us tune the twister. */ RTL_W16 (CSCR, CSCR_LinkDownOffCmd); tp->twistie = 2; /* Change to state 2. */ next_tick = HZ / 10; } else { /* Just put in some reasonable defaults for when beat returns. */ RTL_W16 (CSCR, CSCR_LinkDownCmd); RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */ RTL_W32 (PARA78, PARA78_default); RTL_W32 (PARA7c, PARA7c_default); tp->twistie = 0; /* Bail from future actions. */ } break; case 2: /* Read how long it took to hear the echo. */ linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits; if (linkcase == 0x7000) tp->twist_row = 3; else if (linkcase == 0x3000) tp->twist_row = 2; else if (linkcase == 0x1000) tp->twist_row = 1; else tp->twist_row = 0; tp->twist_col = 0; tp->twistie = 3; /* Change to state 2. */ next_tick = HZ / 10; break; case 3: /* Put out four tuning parameters, one per 100msec. */ if (tp->twist_col == 0) RTL_W16 (FIFOTMS, 0); RTL_W32 (PARA7c, param[(int) tp->twist_row] [(int) tp->twist_col]); next_tick = HZ / 10; if (++tp->twist_col >= 4) { /* For short cables we are done. For long cables (row == 3) check for mistune. */ tp->twistie = (tp->twist_row == 3) ? 4 : 0; } break; case 4: /* Special case for long cables: check for mistune. */ if ((RTL_R16 (CSCR) & CSCR_LinkStatusBits) == 0x7000) { tp->twistie = 0; break; } else { RTL_W32 (PARA7c, 0xfb38de03); tp->twistie = 5; next_tick = HZ / 10; } break; case 5: /* Retune for shorter cable (column 2). */ RTL_W32 (FIFOTMS, 0x20); RTL_W32 (PARA78, PARA78_default); RTL_W32 (PARA7c, PARA7c_default); RTL_W32 (FIFOTMS, 0x00); tp->twist_row = 2; tp->twist_col = 0; tp->twistie = 3; next_tick = HZ / 10; break; default: /* do nothing */ break; } DPRINTK ("EXIT\n");}#endif /* CONFIG_8139TOO_TUNE_TWISTER */static inline void rtl8139_thread_iter (struct net_device *dev, struct rtl8139_private *tp, void *ioaddr){ int mii_reg5; mii_reg5 = mdio_read (dev, tp->phys[0], 5); if (!tp->duplex_lock && mii_reg5 != 0xffff) { int duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 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_reg5); RTL_W8 (Cfg9346, Cfg9346_Unlock); RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20); RTL_W8 (Cfg9346, Cfg9346_Lock); } } next_tick = HZ * 60; rtl8139_tune_twister (dev, tp); DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n", dev->name, RTL_R16 (NWayLPAR)); DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x" " RxStatus %4.4lx.\n", dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus), RTL_R32 (RxEarlyStatus)); DPRINTK ("%s: Chip config %2.2x %2.2x.\n", dev->name, RTL_R8 (Config0), RTL_R8 (Config1));}static int rtl8139_thread (void *data){ struct net_device *dev = data; struct rtl8139_private *tp = dev->priv; unsigned long timeout; daemonize (); spin_lock_irq(¤t->sigmask_lock); sigemptyset(¤t->blocked); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); strncpy (current->comm, dev->name, sizeof(current->comm) - 1); current->comm[sizeof(current->comm) - 1] = '\0'; while (1) { timeout = next_tick; do { timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout); } while (!signal_pending (current) && (timeout > 0)); if (signal_pending (current)) break; rtnl_lock (); rtl8139_thread_iter (dev, tp, tp->mmio_addr); rtnl_unlock (); } up_and_exit (&tp->thr_exited, 0);}static void rtl8139_tx_clear (struct rtl8139_private *tp){ int i; tp->cur_tx = 0; 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 rtl8139_tx_timeout (struct net_device *dev){ struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int i; u8 tmp8; DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x " "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd), RTL_R16 (IntrStatus), RTL_R8 (MediaStatus)); /* disable Tx ASAP, if not already */ tmp8 = RTL_R8 (ChipCmd); if (tmp8 & CmdTxEnb) RTL_W8 (ChipCmd, tmp8 & ~CmdTxEnb); /* Disable interrupts by clearing the interrupt mask. */ RTL_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, tp->cur_tx, 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, RTL_R32 (TxStatus0 + (i * 4)), i == tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : ""); /* Stop a shared interrupt from scavenging while we are. */ spin_lock_irq (&tp->lock); rtl8139_tx_clear (tp); spin_unlock_irq (&tp->lock); /* ...and finally, reset everything */ rtl8139_hw_start (dev);}static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev){ struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int entry; /* Calculate the next Tx descriptor entry. */ entry = 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; if ((long) skb->data & 3) { /* Must use alignment buffer. */ /* tp->tx_info[entry].mapping = 0; */ memcpy (tp->tx_buf[entry], skb->data, skb->len); RTL_W32 (TxAddr0 + (entry * 4), tp->tx_bufs_dma + (tp->tx_buf[entry] - tp->tx_bufs)); } else { tp->tx_info[entry].mapping = pci_map_single (tp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); RTL_W32 (TxAddr0 + (entry * 4), tp->tx_info[entry].mapping); } /* Note: the chip doesn't have auto-pad! */ RTL_W32 (TxStatus0 + (entry * sizeof (u32)), tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); dev->trans_start = jiffies; tp->cur_tx++; mb(); if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) 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 rtl8139_tx_interrupt (struct net_device *dev, struct rtl8139_private *tp, void *ioaddr){ unsigned int dirty_tx, tx_left; assert (dev != NULL); assert (tp != NULL); assert (ioaddr != NULL); dirty_tx = tp->dirty_tx; tx_left = tp->cur_tx - dirty_tx; while (tx_left > 0) { int entry = dirty_tx % NUM_TX_DESC; int txstatus; txstatus = RTL_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++; RTL_W32 (TxConfig, TxClearAbt | (TX_DMA_BURST << TxDMAShift)); } if (txstatus & TxCarrierLost) tp->stats.tx_carrier_errors++; if (txstatus & TxOutOfWindow) tp->stats.tx_window_errors++;#ifdef ETHER_STATS if ((txstatus & 0x0f000000) == 0x0f000000) tp->stats.collisions16++;#endif } 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++; tx_left--; }#ifndef RTL8139_NDEBUG if (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, tp->cur_tx); dirty_tx += NUM_TX_DESC; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -