ep93xx_eth.c

来自「linux 内核源代码」· C语言 代码 · 共 917 行 · 第 1/2 页

C
917
字号
/* * EP93xx ethernet network device driver * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> * Dedicated to Marija Kulikova. * * 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. */#include <linux/dma-mapping.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/netdevice.h>#include <linux/mii.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/init.h>#include <linux/moduleparam.h>#include <linux/platform_device.h>#include <linux/delay.h>#include <asm/arch/ep93xx-regs.h>#include <asm/arch/platform.h>#include <asm/io.h>#define DRV_MODULE_NAME		"ep93xx-eth"#define DRV_MODULE_VERSION	"0.1"#define RX_QUEUE_ENTRIES	64#define TX_QUEUE_ENTRIES	8#define MAX_PKT_SIZE		2044#define PKT_BUF_SIZE		2048#define REG_RXCTL		0x0000#define  REG_RXCTL_DEFAULT	0x00073800#define REG_TXCTL		0x0004#define  REG_TXCTL_ENABLE	0x00000001#define REG_MIICMD		0x0010#define  REG_MIICMD_READ	0x00008000#define  REG_MIICMD_WRITE	0x00004000#define REG_MIIDATA		0x0014#define REG_MIISTS		0x0018#define  REG_MIISTS_BUSY	0x00000001#define REG_SELFCTL		0x0020#define  REG_SELFCTL_RESET	0x00000001#define REG_INTEN		0x0024#define  REG_INTEN_TX		0x00000008#define  REG_INTEN_RX		0x00000007#define REG_INTSTSP		0x0028#define  REG_INTSTS_TX		0x00000008#define  REG_INTSTS_RX		0x00000004#define REG_INTSTSC		0x002c#define REG_AFP			0x004c#define REG_INDAD0		0x0050#define REG_INDAD1		0x0051#define REG_INDAD2		0x0052#define REG_INDAD3		0x0053#define REG_INDAD4		0x0054#define REG_INDAD5		0x0055#define REG_GIINTMSK		0x0064#define  REG_GIINTMSK_ENABLE	0x00008000#define REG_BMCTL		0x0080#define  REG_BMCTL_ENABLE_TX	0x00000100#define  REG_BMCTL_ENABLE_RX	0x00000001#define REG_BMSTS		0x0084#define  REG_BMSTS_RX_ACTIVE	0x00000008#define REG_RXDQBADD		0x0090#define REG_RXDQBLEN		0x0094#define REG_RXDCURADD		0x0098#define REG_RXDENQ		0x009c#define REG_RXSTSQBADD		0x00a0#define REG_RXSTSQBLEN		0x00a4#define REG_RXSTSQCURADD	0x00a8#define REG_RXSTSENQ		0x00ac#define REG_TXDQBADD		0x00b0#define REG_TXDQBLEN		0x00b4#define REG_TXDQCURADD		0x00b8#define REG_TXDENQ		0x00bc#define REG_TXSTSQBADD		0x00c0#define REG_TXSTSQBLEN		0x00c4#define REG_TXSTSQCURADD	0x00c8#define REG_MAXFRMLEN		0x00e8struct ep93xx_rdesc{	u32	buf_addr;	u32	rdesc1;};#define RDESC1_NSOF		0x80000000#define RDESC1_BUFFER_INDEX	0x7fff0000#define RDESC1_BUFFER_LENGTH	0x0000ffffstruct ep93xx_rstat{	u32	rstat0;	u32	rstat1;};#define RSTAT0_RFP		0x80000000#define RSTAT0_RWE		0x40000000#define RSTAT0_EOF		0x20000000#define RSTAT0_EOB		0x10000000#define RSTAT0_AM		0x00c00000#define RSTAT0_RX_ERR		0x00200000#define RSTAT0_OE		0x00100000#define RSTAT0_FE		0x00080000#define RSTAT0_RUNT		0x00040000#define RSTAT0_EDATA		0x00020000#define RSTAT0_CRCE		0x00010000#define RSTAT0_CRCI		0x00008000#define RSTAT0_HTI		0x00003f00#define RSTAT1_RFP		0x80000000#define RSTAT1_BUFFER_INDEX	0x7fff0000#define RSTAT1_FRAME_LENGTH	0x0000ffffstruct ep93xx_tdesc{	u32	buf_addr;	u32	tdesc1;};#define TDESC1_EOF		0x80000000#define TDESC1_BUFFER_INDEX	0x7fff0000#define TDESC1_BUFFER_ABORT	0x00008000#define TDESC1_BUFFER_LENGTH	0x00000fffstruct ep93xx_tstat{	u32	tstat0;};#define TSTAT0_TXFP		0x80000000#define TSTAT0_TXWE		0x40000000#define TSTAT0_FA		0x20000000#define TSTAT0_LCRS		0x10000000#define TSTAT0_OW		0x04000000#define TSTAT0_TXU		0x02000000#define TSTAT0_ECOLL		0x01000000#define TSTAT0_NCOLL		0x001f0000#define TSTAT0_BUFFER_INDEX	0x00007fffstruct ep93xx_descs{	struct ep93xx_rdesc	rdesc[RX_QUEUE_ENTRIES];	struct ep93xx_tdesc	tdesc[TX_QUEUE_ENTRIES];	struct ep93xx_rstat	rstat[RX_QUEUE_ENTRIES];	struct ep93xx_tstat	tstat[TX_QUEUE_ENTRIES];};struct ep93xx_priv{	struct resource		*res;	void			*base_addr;	int			irq;	struct ep93xx_descs	*descs;	dma_addr_t		descs_dma_addr;	void			*rx_buf[RX_QUEUE_ENTRIES];	void			*tx_buf[TX_QUEUE_ENTRIES];	spinlock_t		rx_lock;	unsigned int		rx_pointer;	unsigned int		tx_clean_pointer;	unsigned int		tx_pointer;	spinlock_t		tx_pending_lock;	unsigned int		tx_pending;	struct net_device	*dev;	struct napi_struct	napi;	struct net_device_stats	stats;	struct mii_if_info	mii;	u8			mdc_divisor;};#define rdb(ep, off)		__raw_readb((ep)->base_addr + (off))#define rdw(ep, off)		__raw_readw((ep)->base_addr + (off))#define rdl(ep, off)		__raw_readl((ep)->base_addr + (off))#define wrb(ep, off, val)	__raw_writeb((val), (ep)->base_addr + (off))#define wrw(ep, off, val)	__raw_writew((val), (ep)->base_addr + (off))#define wrl(ep, off, val)	__raw_writel((val), (ep)->base_addr + (off))static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg);static struct net_device_stats *ep93xx_get_stats(struct net_device *dev){	struct ep93xx_priv *ep = netdev_priv(dev);	return &(ep->stats);}static int ep93xx_rx(struct net_device *dev, int processed, int budget){	struct ep93xx_priv *ep = netdev_priv(dev);	while (processed < budget) {		int entry;		struct ep93xx_rstat *rstat;		u32 rstat0;		u32 rstat1;		int length;		struct sk_buff *skb;		entry = ep->rx_pointer;		rstat = ep->descs->rstat + entry;		rstat0 = rstat->rstat0;		rstat1 = rstat->rstat1;		if (!(rstat0 & RSTAT0_RFP) || !(rstat1 & RSTAT1_RFP))			break;		rstat->rstat0 = 0;		rstat->rstat1 = 0;		if (!(rstat0 & RSTAT0_EOF))			printk(KERN_CRIT "ep93xx_rx: not end-of-frame "					 " %.8x %.8x\n", rstat0, rstat1);		if (!(rstat0 & RSTAT0_EOB))			printk(KERN_CRIT "ep93xx_rx: not end-of-buffer "					 " %.8x %.8x\n", rstat0, rstat1);		if ((rstat1 & RSTAT1_BUFFER_INDEX) >> 16 != entry)			printk(KERN_CRIT "ep93xx_rx: entry mismatch "					 " %.8x %.8x\n", rstat0, rstat1);		if (!(rstat0 & RSTAT0_RWE)) {			ep->stats.rx_errors++;			if (rstat0 & RSTAT0_OE)				ep->stats.rx_fifo_errors++;			if (rstat0 & RSTAT0_FE)				ep->stats.rx_frame_errors++;			if (rstat0 & (RSTAT0_RUNT | RSTAT0_EDATA))				ep->stats.rx_length_errors++;			if (rstat0 & RSTAT0_CRCE)				ep->stats.rx_crc_errors++;			goto err;		}		length = rstat1 & RSTAT1_FRAME_LENGTH;		if (length > MAX_PKT_SIZE) {			printk(KERN_NOTICE "ep93xx_rx: invalid length "					 " %.8x %.8x\n", rstat0, rstat1);			goto err;		}		/* Strip FCS.  */		if (rstat0 & RSTAT0_CRCI)			length -= 4;		skb = dev_alloc_skb(length + 2);		if (likely(skb != NULL)) {			skb_reserve(skb, 2);			dma_sync_single(NULL, ep->descs->rdesc[entry].buf_addr,						length, DMA_FROM_DEVICE);			skb_copy_to_linear_data(skb, ep->rx_buf[entry], length);			skb_put(skb, length);			skb->protocol = eth_type_trans(skb, dev);			dev->last_rx = jiffies;			netif_receive_skb(skb);			ep->stats.rx_packets++;			ep->stats.rx_bytes += length;		} else {			ep->stats.rx_dropped++;		}err:		ep->rx_pointer = (entry + 1) & (RX_QUEUE_ENTRIES - 1);		processed++;	}	if (processed) {		wrw(ep, REG_RXDENQ, processed);		wrw(ep, REG_RXSTSENQ, processed);	}	return processed;}static int ep93xx_have_more_rx(struct ep93xx_priv *ep){	struct ep93xx_rstat *rstat = ep->descs->rstat + ep->rx_pointer;	return !!((rstat->rstat0 & RSTAT0_RFP) && (rstat->rstat1 & RSTAT1_RFP));}static int ep93xx_poll(struct napi_struct *napi, int budget){	struct ep93xx_priv *ep = container_of(napi, struct ep93xx_priv, napi);	struct net_device *dev = ep->dev;	int rx = 0;poll_some_more:	rx = ep93xx_rx(dev, rx, budget);	if (rx < budget) {		int more = 0;		spin_lock_irq(&ep->rx_lock);		__netif_rx_complete(dev, napi);		wrl(ep, REG_INTEN, REG_INTEN_TX | REG_INTEN_RX);		if (ep93xx_have_more_rx(ep)) {			wrl(ep, REG_INTEN, REG_INTEN_TX);			wrl(ep, REG_INTSTSP, REG_INTSTS_RX);			more = 1;		}		spin_unlock_irq(&ep->rx_lock);		if (more && netif_rx_reschedule(dev, napi))			goto poll_some_more;	}	return rx;}static int ep93xx_xmit(struct sk_buff *skb, struct net_device *dev){	struct ep93xx_priv *ep = netdev_priv(dev);	int entry;	if (unlikely(skb->len > MAX_PKT_SIZE)) {		ep->stats.tx_dropped++;		dev_kfree_skb(skb);		return NETDEV_TX_OK;	}	entry = ep->tx_pointer;	ep->tx_pointer = (ep->tx_pointer + 1) & (TX_QUEUE_ENTRIES - 1);	ep->descs->tdesc[entry].tdesc1 =		TDESC1_EOF | (entry << 16) | (skb->len & 0xfff);	skb_copy_and_csum_dev(skb, ep->tx_buf[entry]);	dma_sync_single(NULL, ep->descs->tdesc[entry].buf_addr,				skb->len, DMA_TO_DEVICE);	dev_kfree_skb(skb);	dev->trans_start = jiffies;	spin_lock_irq(&ep->tx_pending_lock);	ep->tx_pending++;	if (ep->tx_pending == TX_QUEUE_ENTRIES)		netif_stop_queue(dev);	spin_unlock_irq(&ep->tx_pending_lock);	wrl(ep, REG_TXDENQ, 1);	return NETDEV_TX_OK;}static void ep93xx_tx_complete(struct net_device *dev){	struct ep93xx_priv *ep = netdev_priv(dev);	int wake;	wake = 0;	spin_lock(&ep->tx_pending_lock);	while (1) {		int entry;		struct ep93xx_tstat *tstat;		u32 tstat0;		entry = ep->tx_clean_pointer;		tstat = ep->descs->tstat + entry;		tstat0 = tstat->tstat0;		if (!(tstat0 & TSTAT0_TXFP))			break;		tstat->tstat0 = 0;		if (tstat0 & TSTAT0_FA)			printk(KERN_CRIT "ep93xx_tx_complete: frame aborted "					 " %.8x\n", tstat0);		if ((tstat0 & TSTAT0_BUFFER_INDEX) != entry)			printk(KERN_CRIT "ep93xx_tx_complete: entry mismatch "					 " %.8x\n", tstat0);		if (tstat0 & TSTAT0_TXWE) {			int length = ep->descs->tdesc[entry].tdesc1 & 0xfff;			ep->stats.tx_packets++;			ep->stats.tx_bytes += length;		} else {			ep->stats.tx_errors++;		}		if (tstat0 & TSTAT0_OW)			ep->stats.tx_window_errors++;		if (tstat0 & TSTAT0_TXU)			ep->stats.tx_fifo_errors++;		ep->stats.collisions += (tstat0 >> 16) & 0x1f;		ep->tx_clean_pointer = (entry + 1) & (TX_QUEUE_ENTRIES - 1);		if (ep->tx_pending == TX_QUEUE_ENTRIES)			wake = 1;		ep->tx_pending--;	}	spin_unlock(&ep->tx_pending_lock);	if (wake)		netif_wake_queue(dev);}static irqreturn_t ep93xx_irq(int irq, void *dev_id){	struct net_device *dev = dev_id;	struct ep93xx_priv *ep = netdev_priv(dev);	u32 status;	status = rdl(ep, REG_INTSTSC);	if (status == 0)		return IRQ_NONE;	if (status & REG_INTSTS_RX) {		spin_lock(&ep->rx_lock);		if (likely(netif_rx_schedule_prep(dev, &ep->napi))) {			wrl(ep, REG_INTEN, REG_INTEN_TX);			__netif_rx_schedule(dev, &ep->napi);		}		spin_unlock(&ep->rx_lock);	}	if (status & REG_INTSTS_TX)		ep93xx_tx_complete(dev);	return IRQ_HANDLED;}static void ep93xx_free_buffers(struct ep93xx_priv *ep){	int i;	for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {		dma_addr_t d;		d = ep->descs->rdesc[i].buf_addr;		if (d)			dma_unmap_single(NULL, d, PAGE_SIZE, DMA_FROM_DEVICE);		if (ep->rx_buf[i] != NULL)			free_page((unsigned long)ep->rx_buf[i]);	}	for (i = 0; i < TX_QUEUE_ENTRIES; i += 2) {		dma_addr_t d;		d = ep->descs->tdesc[i].buf_addr;		if (d)			dma_unmap_single(NULL, d, PAGE_SIZE, DMA_TO_DEVICE);		if (ep->tx_buf[i] != NULL)			free_page((unsigned long)ep->tx_buf[i]);	}	dma_free_coherent(NULL, sizeof(struct ep93xx_descs), ep->descs,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?