📄 8139too.c
字号:
enum TwisterParamVals { PARA78_default = 0x78fa8388, PARA7c_default = 0xcb38de43, /* param[0][3] */ PARA7c_xxx = 0xcb38de43,};static const unsigned long param[4][4] = { {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}};static void rtl8139_tune_twister (struct net_device *dev, struct rtl8139_private *tp){ int linkcase; void __iomem *ioaddr = tp->mmio_addr; /* 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; }}#endif /* CONFIG_8139TOO_TUNE_TWISTER */static inline void rtl8139_thread_iter (struct net_device *dev, struct rtl8139_private *tp, void __iomem *ioaddr){ int mii_lpa; mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); if (!tp->mii.force_media && mii_lpa != 0xffff) { int duplex = (mii_lpa & LPA_100FULL) || (mii_lpa & 0x01C0) == 0x0040; if (tp->mii.full_duplex != duplex) { tp->mii.full_duplex = duplex; if (mii_lpa) { printk (KERN_INFO "%s: Setting %s-duplex based on MII #%d link" " partner ability of %4.4x.\n", dev->name, tp->mii.full_duplex ? "full" : "half", tp->phys[0], mii_lpa); } else { printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n", dev->name); }#if 0 RTL_W8 (Cfg9346, Cfg9346_Unlock); RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); RTL_W8 (Cfg9346, Cfg9346_Lock);#endif } } 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\n", dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus)); DPRINTK ("%s: Chip config %2.2x %2.2x.\n", dev->name, RTL_R8 (Config0), RTL_R8 (Config1));}static void rtl8139_thread (struct work_struct *work){ struct rtl8139_private *tp = container_of(work, struct rtl8139_private, thread.work); struct net_device *dev = tp->mii.dev; unsigned long thr_delay = next_tick; rtnl_lock(); if (!netif_running(dev)) goto out_unlock; if (tp->watchdog_fired) { tp->watchdog_fired = 0; rtl8139_tx_timeout_task(work); } else rtl8139_thread_iter(dev, tp, tp->mmio_addr); if (tp->have_thread) schedule_delayed_work(&tp->thread, thr_delay);out_unlock: rtnl_unlock ();}static void rtl8139_start_thread(struct rtl8139_private *tp){ tp->twistie = 0; if (tp->chipset == CH_8139_K) tp->twistie = 1; else if (tp->drv_flags & HAS_LNK_CHNG) return; tp->have_thread = 1; tp->watchdog_fired = 0; schedule_delayed_work(&tp->thread, next_tick);}static inline void rtl8139_tx_clear (struct rtl8139_private *tp){ tp->cur_tx = 0; tp->dirty_tx = 0; /* XXX account for unsent Tx packets in tp->stats.tx_dropped */}static void rtl8139_tx_timeout_task (struct work_struct *work){ struct rtl8139_private *tp = container_of(work, struct rtl8139_private, thread.work); struct net_device *dev = tp->mii.dev; void __iomem *ioaddr = tp->mmio_addr; int i; u8 tmp8; printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x " "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd), RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus)); /* Emit info to figure out what went wrong. */ printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\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)" : ""); tp->xstats.tx_timeouts++; /* disable Tx ASAP, if not already */ tmp8 = RTL_R8 (ChipCmd); if (tmp8 & CmdTxEnb) RTL_W8 (ChipCmd, CmdRxEnb); spin_lock_bh(&tp->rx_lock); /* Disable interrupts by clearing the interrupt mask. */ RTL_W16 (IntrMask, 0x0000); /* 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 */ if (netif_running(dev)) { rtl8139_hw_start (dev); netif_wake_queue (dev); } spin_unlock_bh(&tp->rx_lock);}static void rtl8139_tx_timeout (struct net_device *dev){ struct rtl8139_private *tp = netdev_priv(dev); tp->watchdog_fired = 1; if (!tp->have_thread) { INIT_DELAYED_WORK(&tp->thread, rtl8139_thread); schedule_delayed_work(&tp->thread, next_tick); }}static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev){ struct rtl8139_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; unsigned int entry; unsigned int len = skb->len; unsigned long flags; /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % NUM_TX_DESC; /* Note: the chip doesn't have auto-pad! */ if (likely(len < TX_BUF_SIZE)) { if (len < ETH_ZLEN) memset(tp->tx_buf[entry], 0, ETH_ZLEN); skb_copy_and_csum_dev(skb, tp->tx_buf[entry]); dev_kfree_skb(skb); } else { dev_kfree_skb(skb); tp->stats.tx_dropped++; return 0; } spin_lock_irqsave(&tp->lock, flags); RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); dev->trans_start = jiffies; tp->cur_tx++; wmb(); if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) netif_stop_queue (dev); spin_unlock_irqrestore(&tp->lock, flags); if (netif_msg_tx_queued(tp)) printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n", dev->name, len, entry); return 0;}static void rtl8139_tx_interrupt (struct net_device *dev, struct rtl8139_private *tp, void __iomem *ioaddr){ unsigned long dirty_tx, tx_left; assert (dev != 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. */ if (netif_msg_tx_err(tp)) printk(KERN_DEBUG "%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); RTL_W16 (IntrStatus, TxErr); wmb(); } 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++; } 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, %ld vs. %ld.\n", dev->name, dirty_tx, tp->cur_tx); dirty_tx += NUM_TX_DESC; }#endif /* RTL8139_NDEBUG */ /* only wake the queue if we did work, and the queue is stopped */ if (tp->dirty_tx != dirty_tx) { tp->dirty_tx = dirty_tx; mb(); netif_wake_queue (dev); }}/* TODO: clean this up! Rx reset need not be this intensive */static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, struct rtl8139_private *tp, void __iomem *ioaddr){ u8 tmp8;#ifdef CONFIG_8139_OLD_RX_RESET int tmp_work;#endif if (netif_msg_rx_err (tp)) printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n", dev->name, rx_status); tp->stats.rx_errors++; if (!(rx_status & RxStatusOK)) { if (rx_status & RxTooLong) { DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", dev->name, rx_status); /* A.C.: The chip hangs here. */ } 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++; } else { tp->xstats.rx_lost_in_ring++; }#ifndef CONFIG_8139_OLD_RX_RESET tmp8 = RTL_R8 (ChipCmd); RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb); RTL_W8 (ChipCmd, tmp8); RTL_W32 (RxConfig, tp->rx_config); tp->cur_rx = 0;#else /* Reset the receiver, based on RealTek recommendation. (Bug?) */ /* disable receive */ RTL_W8_F (ChipCmd, CmdTxEnb); tmp_work = 200; while (--tmp_work > 0) { udelay(1); tmp8 = RTL_R8 (ChipCmd); if (!(tmp8 & CmdRxEnb)) break; } if (tmp_work <= 0) printk (KERN_WARNING PFX "rx stop wait too long\n"); /* restart receive */ tmp_work = 200; while (--tmp_work > 0) { RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb); udelay(1); tmp8 = RTL_R8 (ChipCmd); if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) break; } if (tmp_work <= 0) printk (KERN_WARNING PFX "tx/rx enable wait too long\n"); /* and reinitialize all rx related registers */ RTL_W8_F (Cfg9346, Cfg9346_Unlock); /* Must enable Tx/Rx before setting transfer thresholds! */ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; RTL_W32 (RxConfig, tp->rx_config); tp->cur_rx = 0; DPRINTK("init buffer addresses\n"); /* Lock Config[01234] and BMCR register writes */ RTL_W8 (Cfg9346, Cfg9346_Lock); /* init Rx ring buffer DMA address */ RTL_W32_F (RxBuf, tp->rx_ring_dma); /* A.C.: Reset the multicast list. */ __set_rx_mode (dev);#endif}#if RX_BUF_IDX == 3static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring, u32 offset, unsigned int size){ u32 left = RX_BUF_LEN - offset; if (size > left) { skb_copy_to_linear_data(skb, ring + offset, left); skb_copy_to_linear_data_offset(skb, left, ring, size - left); } else skb_copy_to_linear_data(skb, ring + offset, size);}#endifstatic void rtl8139_isr_ack(struct rtl8139_private *tp){ void __iomem *ioaddr = tp->mmio_addr; u16 status; status = RTL_R16 (IntrStatus) & RxAckBits; /* Clear out errors and receive interrupts */ if (likely(status != 0)) { if (unlikely(status & (RxFIFOOver | RxOverflow))) { tp->stats.rx_errors++; if (status & RxFIFOOver) tp->stats.rx_fifo_errors++; } RTL_W16_F (IntrStatus, RxAckBits); }}static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, int budget){ void __iomem *ioaddr = tp->mmio_addr; int received = 0; unsigned char *rx_ring = tp->rx_ring; unsigned int cur_rx = tp->cur_rx; unsigned int rx_size = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -