📄 1022.ethernet.patch
字号:
diff -Naur --exclude=CVS --exclude='*.o' --exclude='*.a' --exclude='*.so' --exclude='*.elf' --exclude=System.map --exclude=Makefile.d --exclude='*log' --exclude='*log2' --exclude='*~' --exclude='.*~' --exclude='.#*' --exclude='*.bak' --exclude='*.orig' --exclude='*.rej' --exclude='core.[0-9]*' --exclude=.depend --exclude='.*.o.flags' --exclude='*.gz' --exclude=.depend --exclude='.*.o.flags' --exclude='*.gz' --exclude=vmlinux --exclude=vmlinux.bin --exclude=yamon-02.06-SIGMADESIGNS-01_el.bin linuxmips-2.4.30.ref/drivers/net/Config.in linuxmips-2.4.30/drivers/net/Config.in--- linuxmips-2.4.30.ref/drivers/net/Config.in 2007-06-11 10:36:00.000000000 -0700+++ linuxmips-2.4.30/drivers/net/Config.in 2007-06-11 10:40:45.000000000 -0700@@ -24,6 +24,12 @@ comment 'Ethernet (10 or 100Mbit)' bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET if [ "$CONFIG_NET_ETHERNET" = "y" ]; then+ if [ "$CONFIG_TANGO2" = "y" ]; then+ if [ "$CONFIG_TANGO2_ES4" = "y" -o "$CONFIG_TANGO2_ES6" = "y" ]; then+ tristate ' SMP863x Builtin Ethernet support' CONFIG_EM86XX_ENET+ tristate ' SMP863x Builtin Ethernet support (new driver)' CONFIG_EM86XX_ENET_NEW+ fi+ fi if [ "$CONFIG_ARM" = "y" ]; then dep_bool ' ARM EBSA110 AM79C961A support' CONFIG_ARM_AM79C961A $CONFIG_ARCH_EBSA110 tristate ' Cirrus Logic CS8900A support' CONFIG_ARM_CIRRUSdiff -Naur --exclude=CVS --exclude='*.o' --exclude='*.a' --exclude='*.so' --exclude='*.elf' --exclude=System.map --exclude=Makefile.d --exclude='*log' --exclude='*log2' --exclude='*~' --exclude='.*~' --exclude='.#*' --exclude='*.bak' --exclude='*.orig' --exclude='*.rej' --exclude='core.[0-9]*' --exclude=.depend --exclude='.*.o.flags' --exclude='*.gz' --exclude=.depend --exclude='.*.o.flags' --exclude='*.gz' --exclude=vmlinux --exclude=vmlinux.bin --exclude=yamon-02.06-SIGMADESIGNS-01_el.bin linuxmips-2.4.30.ref/drivers/net/em86xx_enet.c linuxmips-2.4.30/drivers/net/em86xx_enet.c--- linuxmips-2.4.30.ref/drivers/net/em86xx_enet.c 1969-12-31 16:00:00.000000000 -0800+++ linuxmips-2.4.30/drivers/net/em86xx_enet.c 2007-06-11 10:40:45.000000000 -0700@@ -0,0 +1,1112 @@+/*+ * New driver for SMP863x builtin Ethernet mac+ *+ * This driver uses NAPI and generic linux MII support.+ *+ * Tx path limits the number of interrupt by reclaiming sent buffer in+ * a timer. In case the tx starts to go faster, it will switch to+ * interrupt mode.+ *+ * Note that OOM condition is not handled correctly, and can leave the+ * rx path in bad shape. down/up the interface should make it work+ * again though. But anyway, it's not likely to happen.+ *+ * Copyright (C) 2005 Maxime Bizon <mbizon@freebox.fr>+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.+ */++#include <linux/config.h>+#include <linux/kernel.h>+#include <linux/netdevice.h>+#include <linux/etherdevice.h>+#include <linux/spinlock.h>+#include <linux/interrupt.h>+#include <linux/signal.h>+#include <linux/sched.h>+#include <linux/module.h>+#include <linux/compatmac.h>+#include <linux/mm.h>+#include <linux/init.h>+#include <linux/timer.h>+#include <linux/mii.h>+#include <linux/ethtool.h>+#include <linux/crc32.h>+#include <linux/delay.h>++#include "em86xx_enet.h"++#define PFX "em86xx_enet: "++static struct net_device *gdev;+static int gphy_id = -1;++/*+ * mdio read/write callback, can run from userspace or timer+ */+static __inline int enet_mdio_read(struct net_device *dev, int phy_id,+ int location)+{+ int val;++ while (enet_readl(ENET_MAC_MIIAR) & MIIAR_BUSY);+ enet_writel(ENET_MAC_MIIAR, MIIAR_ADDR(phy_id) | MIIAR_REG(location));+ udelay(1);+ while (enet_readl(ENET_MAC_MIIAR) & MIIAR_BUSY);+ val = enet_readl(ENET_MAC_MIIDR);++ return val;+}++static void enet_mdio_write(struct net_device *dev, int phy_id,+ int location, int val)+{+ enet_writel(ENET_MAC_MIIDR, val);+ enet_writel(ENET_MAC_MIIAR,+ MIIAR_ADDR(phy_id) | MIIAR_REG(location) | MIIAR_WRITE);+ udelay(1);+ while (enet_readl(ENET_MAC_MIIAR) & MIIAR_BUSY);+}++/*+ * enable/disable interrupt helpers+ * need proper locks since we will call them from any context+ */+static __inline void enet_disable_interrupts(struct em86xx_enet_priv *priv,+ int rx_only)+{+ unsigned long flags, val;++ spin_lock_irqsave(&priv->ier_lock, flags);+ if (rx_only) {+ val = enet_readl(ENET_DMA_IER);+ val &= ~IER_R;+ enet_writel(ENET_DMA_IER, val);+ } else+ enet_writel(ENET_DMA_IER, 0);+ spin_unlock_irqrestore(&priv->ier_lock, flags);+}++static __inline void enet_enable_interrupts(struct em86xx_enet_priv *priv,+ int rx_only)+{+ unsigned long flags, val;++ spin_lock_irqsave(&priv->ier_lock, flags);+ if (rx_only) {+ val = enet_readl(ENET_DMA_IER);+ val |= IER_R;+ enet_writel(ENET_DMA_IER, val);+ } else+ enet_writel(ENET_DMA_IER, IER_NIS | IER_R | IER_T);+ spin_unlock_irqrestore(&priv->ier_lock, flags);+}+++/*+ * rx poll func, called by network core+ */+static int enet_poll(struct net_device *dev, int *budget)+{+ struct em86xx_enet_priv *priv;+ int limit, received;++ priv = netdev_priv(dev);++ /* calculate how many rx packet we are allowed to fetch */+ limit = *budget;+ if (*budget > dev->quota)+ limit = dev->quota;+ received = 0;++ /* process no more than "limit" done rx */+ do {+ volatile struct enet_rx_desc *rx;+ struct sk_buff *skb;+ uint32_t rdes0_cache;+ unsigned int len;++ rx = &priv->rx_descs[priv->last_rx_desc];++ /* we need multiple read on this volatile, avoid+ * memory access at each time */+ rdes0_cache = rx->rdes0;+ if (rdes0_cache & RDES0_OWN) {+ break;+ }++ if (limit <= 0)+ break;+ --limit;++ if ((skb = priv->rx_skbs[priv->last_rx_desc]) != NULL) {++ /* we don't handle multipacket frame */+ if (!(rdes0_cache & RDES0_FIRST) ||+ !(rdes0_cache & RDES0_LAST)) {+ /* we don't handle multipacket frame */+ priv->stats.rx_errors++;+ priv->stats.rx_length_errors++;+ goto rearm;+ }++ /* check for CRC */+ if (rdes0_cache & RDES0_CRC) {+ priv->stats.rx_errors++;+ priv->stats.rx_crc_errors++;+ goto rearm;+ }++ /* sanity check on len field */+ len = RDES0_FRAME_LEN(rdes0_cache);+ if (rdes0_cache & (RDES0_TOO_LONG | RDES0_TRUNC) ||+ len > RX_BUF_SIZE) {+ priv->stats.rx_errors++;+ priv->stats.rx_length_errors++;+ goto rearm;+ }++ /* check remaining error */+ if (rdes0_cache & (RDES0_ERR_SUM | RDES0_COLLISION |+ RDES0_WATCHDOG_TMOUT |+ RDES0_MII_ERROR)) {+ priv->stats.rx_errors++;+ goto rearm;+ }++ /* ok, seems valid, adjust skb proto and len+ * and give it to kernel */+ skb->dev = dev;+ skb_put(skb, len);+ skb->protocol = eth_type_trans(skb, dev);+ netif_receive_skb(skb);++ priv->stats.rx_packets++;+ priv->stats.rx_bytes += len;+ dev->last_rx = jiffies;+ priv->rx_skbs[priv->last_rx_desc] = NULL;+ /* we will realloc an skb for this slot */+ }++ skb = dev_alloc_skb(RX_BUF_SIZE + SKB_RESERVE_SIZE);+ if (unlikely(!skb))+ break;++ skb_reserve(skb, SKB_RESERVE_SIZE);+ rx->rdes2 = PHYSADDR(skb->data);+ dma_cache_inv((unsigned long)skb->data, RX_BUF_SIZE);+ priv->rx_skbs[priv->last_rx_desc] = skb;++rearm:+ /* rearm descriptor */+ wmb();+ rx->rdes0 = RDES0_OWN;+ priv->last_rx_desc++;+ priv->last_rx_desc &= (ENET_RX_DESC_COUNT - 1);+ received++;++ } while (1);++ dev->quota -= received;+ *budget -= received;++ if (limit <= 0) {+ /* breaked, but there is still work to do */+ return 1;+ }++ netif_rx_complete(dev);+ enet_enable_interrupts(priv, 1);++ return 0;+}++/*+ * tx reclaim func. Called by timer or tx done tasklet to reclaim sent+ * buffers.+ */+static void enet_tx_reclaim(unsigned long data)+{+ struct net_device *dev;+ struct em86xx_enet_priv *priv;+ volatile struct enet_tx_desc *tx;++ dev = (struct net_device *)data;+ priv = netdev_priv(dev);++ spin_lock(&priv->tx_lock);++ while (priv->free_tx_desc_count < ENET_TX_DESC_COUNT) {+ uint32_t tdes0_cache;+ struct sk_buff *skb;++ tx = &priv->tx_descs[priv->dirty_tx_desc];++ tdes0_cache = tx->tdes0;+ if (tdes0_cache & TDES0_OWN)+ break;++ skb = priv->tx_skbs[priv->dirty_tx_desc];+ priv->stats.tx_packets++;++ /* check for transmission errors and update stats+ * accordingly */+ if (tdes0_cache & (TDES0_ERR_SUM | TDES0_CARRIER_LOST |+ TDES0_NO_CARRIER | TDES0_LATE_COLLISION |+ TDES0_EXC_COLLISION | TDES0_HEARTBEAT |+ TDES0_EXC_DEFERAL | TDES0_UNDERFLOW)) {+ priv->stats.tx_errors++;+ } else {+ priv->stats.tx_bytes += skb->len;+ }++ dev_kfree_skb(skb);+ priv->tx_skbs[priv->dirty_tx_desc] = NULL;+ priv->dirty_tx_desc++;+ priv->dirty_tx_desc %= ENET_TX_DESC_COUNT;+ priv->free_tx_desc_count++;+ }++ if (priv->free_tx_desc_count != 0 && netif_queue_stopped(dev))+ netif_wake_queue(dev);++ spin_unlock(&priv->tx_lock);+}++/*+ * tx done timer callback, just call tx_done and reschedule timer+ */+static void enet_tx_reclaim_timer(unsigned long data)+{+ struct net_device *dev;+ struct em86xx_enet_priv *priv;++ dev = (struct net_device *)data;+ priv = netdev_priv(dev);+ enet_tx_reclaim(data);++ priv->tx_reclaim_timer.expires = jiffies + TX_RECLAIM_TIMER_FREQ;+ add_timer(&priv->tx_reclaim_timer);+}+++/*+ * tx request callback+ */+static int enet_xmit(struct sk_buff *skb, struct net_device *dev)+{+ struct em86xx_enet_priv *priv;+ volatile struct enet_tx_desc *tx;+ unsigned long tdes1_cache;++ spin_lock(&priv->tx_lock);++ priv = netdev_priv(dev);+ tx = &priv->tx_descs[priv->next_tx_desc];++ /* make sure the next free tx desc is available */+ if (unlikely(priv->free_tx_desc_count == 0)) {+ /* no, this should not happen since queue is stopped+ * before we run out of tx desc */+ printk(KERN_WARNING PFX "no free tx desc to handle pkt\n");+ dev_kfree_skb(skb);+ netif_stop_queue(dev);+ spin_unlock(&priv->tx_lock);+ return 0;+ }++ /* fill the tx desc with this skb address */+ tdes1_cache = (TDES1_FIRST | TDES1_LAST);+ if (priv->next_tx_desc == ENET_TX_DESC_COUNT - 1)+ tdes1_cache |= TDES1_TER;++ /* if we start to run low on free tx desc, then enable tx+ * interrupt to reclaim them faster */+ if (priv->free_tx_desc_count == ENET_TX_DESC_LOW) {+ tdes1_cache |= (TDES1_ENABLE_ISR);+ }+ tdes1_cache |= TDES1_TBS1(skb->len);++ tx->tdes1 = tdes1_cache;+ tx->tdes2 = PHYSADDR(skb->data);+ dma_cache_wback((unsigned long)skb->data, skb->len);++ /* keep a pointer to it for later and give it to dma */+ priv->tx_skbs[priv->next_tx_desc] = skb;+ wmb();+ tx->tdes0 = TDES0_OWN;++ /* kick tx dma in case it was suspended */+ wmb();+ enet_writel(ENET_DMA_TPDR, 0x1);++ priv->next_tx_desc++;+ priv->next_tx_desc %= ENET_TX_DESC_COUNT;++ /* if next tx descriptor is not clean, then we have to stop+ * queue */+ if (unlikely(--priv->free_tx_desc_count == 0))+ netif_stop_queue(dev);++ spin_unlock(&priv->tx_lock);++ return 0;+}++/*+ * our irq handler, just ack it and schedule the right tasklet to+ * handle this+ */+static irqreturn_t enet_isr(int irq, void *dev_id, struct pt_regs *regs)+{+ struct net_device *dev;+ struct em86xx_enet_priv *priv;+ unsigned long val;++ dev = (struct net_device *)dev_id;+ priv = netdev_priv(dev);++ /* fetch status & ack them */+ val = enet_readl(ENET_DMA_SR);+ enet_writel(ENET_DMA_SR, val);++ /* handle interrupt */+ if (val & SR_NIS) {+ if (val & SR_T) {+ tasklet_schedule(&priv->tx_reclaim_tasklet);+ }++ if (val & SR_R) {+ if (netif_rx_schedule_prep(dev)) {+ /* disable rx interrupt */+ enet_disable_interrupts(priv, 1);++ /* ack any interrupt that may have+ * arrived between last ack to avoid+ * reentering */+ enet_writel(ENET_DMA_SR, SR_NIS | SR_R);+ __netif_rx_schedule(dev);+ }+ }+ }++ return IRQ_HANDLED;+}++/*+ * start/stop dma engine+ */+static __inline void enet_start_dma(struct em86xx_enet_priv *priv)+{+ /* send start command to rx & tx dma */+ enet_writel(ENET_DMA_CR, CR_SF | CR_SR | CR_ST);+}++static __inline void enet_stop_dma(struct em86xx_enet_priv *priv)+{+ unsigned long val;++ /* send stop command to rx & tx dma */+ enet_writel(ENET_DMA_CR, 0);++ /* wait for them to reach stopped state, should not be long */+ do {+ udelay(1);+ val = enet_readl(ENET_DMA_SR);+ if ((val & SR_TPS) && (val & SR_RPS))+ break;+ } while (1);+}++/*+ * reconfigure mac for new link state+ */+static void enet_link_reconfigure(struct net_device *dev)+{+ struct em86xx_enet_priv *priv;+ unsigned long val;++ priv = netdev_priv(dev);++ if (dev->flags & IFF_UP)+ enet_stop_dma(priv);++ /* reflect duplex status in dma register */+ spin_lock(&priv->maccr_lock);+ val = enet_readl(ENET_MAC_MACCR);+ if (priv->mii.full_duplex)+ val |= MACCR_F;+ else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -