⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 acacia.c

📁 IDT RC32438 on-chip ethernet controller
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * ######################################################################## * *  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  SOFTWARE  IS PROVIDED   ``AS	IS'' AND   ANY	EXPRESS OR IMPLIED *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN *  NO	EVENT  SHALL   THE AUTHOR  BE	 LIABLE FOR ANY   DIRECT, INDIRECT, *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS	OR SERVICES; LOSS OF *  USE, DATA,	OR PROFITS; OR	BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *  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. * * ########################################################################************************************************************************************** acacia.c* driver for the IDT RC32438 on-chip ethernet controller.*************************************************************************************Author: MontaVista Software, Inc.stevel@mvista.com or source@mvista.comAuthor: Integrated Device Technology, Inc.Maruthanayagam: 	Ported from banyan.c cgg:                    	Modified code.*************************************************************************************Haofeng Kou			6/7/03Using the IOD COD and DMA chaining mode to handle the tansmitIn order to prevent the DMA Race Condition, make software access NDPTR only for tansmit.Using the IOF COF and NDPTR of DMA chaining to handle the Receive.In order to prevent the DMA Race Condition, use NDPTR trigger DMA chaining onceoverwrite happen for receive.Modify acacia_restart() to make it recover Bridge mode setting after reset.Adjust the dev_kfree_skb_any() and dev_kfree_skb_irq() usage.Adjust the netif_wake_queue() and netif_start_queue() usage.Add in code to handle the case once there is no memory in system(only for special situations).*************************************************************************************Haofeng Kou			6/16/03Add in code to handle the TX Underflow.Trigger the waiting queue in the TX ISR (7/23/03)Fix the bug happen for the wu-ftp server (9/15/03)*************************************************************************************P. Sadik  Oct 10, 2003'Management Clock Prescaler Divisor' is no longer determined at compile time, butis caculated at run time, based on idt_cpu_freq.*************************************************************************************Haofeng Kou			10/27/03Change the ISR to use Tasklet for the TX and RX.Add in the DMA Abort code and use DMA Abort instead of the DMA halt. (DMA abort finished faster than DMA halt, so replace the halt code by abort)Optimize the interrupt disable/enable actions to reduce the number of interrupt.Improve the CPU usage.*************************************************************************************P. Sadik Nov 14, 2003For Acacia ZC part, the receive handler is modified such that, the DMA haltcondition is checked after the taskelet has finished handling all descriptors.If the H bit is set, then the DMA is restarted. With this fix, the overflowinterrupt need not be taken. **********************************************************************************Haofeng Kou			11/18/03Merge together the ZA and ZC code, auto-switch between the ZA and ZC.Update all the debug features(ASSERT, DBG, ERR, INFO and WARN).Add in the loadable driver module support.Add the MII-PHY LXT973 duplex mode auto-negotiation support.(Not ebable at this time)*************************************************************************************rkt                            4/29/04Added Fast-Switching feature.acacia_rx_tasklet routine modified to handle race conditions in the driver**************************************************************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/ctype.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/proc_fs.h>#include <linux/in.h>#include <linux/slab.h> #include <linux/string.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <asm/bootinfo.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/pgtable.h>#include <asm/segment.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/atomic.h>#ifdef CONFIG_NET_FASTROUTE#include <linux/if_arp.h>#include <net/ip.h>#endif#include "acacia.h"#include  <asm/rc32438/rc32438.h>#define STATION_ADDRESS_HIGH(dev) (((dev)->dev_addr[0] << 8) | \			           ((dev)->dev_addr[1]))#define STATION_ADDRESS_LOW(dev)  (((dev)->dev_addr[2] << 24) | \				   ((dev)->dev_addr[3] << 16) | \				   ((dev)->dev_addr[4] << 8)  | \				   ((dev)->dev_addr[5]))#define MII_CLOCK 1250000 				/* no more than 2.5MHz */static char mac0[7] = { 0x08, 0x00, 0x06, 0x05, 0x40, 0x01 }; static char mac1[7] = { 0x08, 0x00, 0x06, 0x05, 0x50, 0x01 }; static struct idt32438_boarddata *idt32438_boardConfig=NULL;MODULE_PARM(mac0, "c7");MODULE_PARM(mac1, "c7");MODULE_PARM_DESC(mac0, "MAC address for RC32438 ethernet0");MODULE_PARM_DESC(mac1, "MAC address for RC32438 ethernet1");static struct acacia_if_t {	struct net_device *dev;	char* mac_str;	u32 iobase;	int rx_dma_irq;	int tx_dma_irq;	int rx_ovr_irq;  	int tx_und_irq;			} acacia_iflist[] = 	{	 	{	NULL, mac0, ETH1_PhysicalAddress,	 		ETH1_DMA_RX_IRQ, ETH1_DMA_TX_IRQ, ETH1_RX_OVR_IRQ, ETH1_TX_UND_IRQ},		{	NULL, mac1, ETH0_PhysicalAddress,	 		ETH0_DMA_RX_IRQ, ETH0_DMA_TX_IRQ, ETH0_RX_OVR_IRQ, ETH0_TX_UND_IRQ},		{NULL, 0, 0}	};#if 0static int parse_mac_addr(struct net_device *dev, char* macstr){	int i, j;	unsigned char result, value;			for (i=0; i<6; i++) 	{		result = 0;		if (i != 5 && *(macstr+2) != ':') 		{			ERR(__FILE__ "invalid mac address format: %d %c\n",			    i, *(macstr+2));			return -EINVAL;		}						for (j=0; j<2; j++) 		{			if (isxdigit(*macstr) && (value = isdigit(*macstr) ? *macstr-'0' : 			     toupper(*macstr)-'A'+10) < 16) 			{				result = result*16 + value;				macstr++;			} 			else 			{				ERR(__FILE__ "invalid mac address "				    "character: %c\n", *macstr);				return -EINVAL;			}		}						macstr++; 		dev->dev_addr[i] = result;	}			return 0;}#endifstatic inline void acacia_abort_tx(struct net_device *dev){	struct acacia_local *lp = (struct acacia_local *)dev->priv;	rc32438_abort_dma(dev, lp->tx_dma_regs);	}static inline void acacia_abort_rx(struct net_device *dev){	struct acacia_local *lp = (struct acacia_local *)dev->priv;	rc32438_abort_dma(dev, lp->rx_dma_regs);	}static inline void acacia_halt_tx(struct net_device *dev){	struct acacia_local *lp = (struct acacia_local *)dev->priv;		if (rc32438_halt_dma(lp->tx_dma_regs))		ERR(__FUNCTION__ ": timeout!\n");}static inline void acacia_halt_rx(struct net_device *dev){	struct acacia_local *lp = (struct acacia_local *)dev->priv;	if (rc32438_halt_dma(lp->rx_dma_regs))		ERR(__FUNCTION__ ": timeout!\n");}static inline void acacia_start_tx(struct acacia_local *lp,  volatile DMAD_t td){	rc32438_start_dma(lp->tx_dma_regs, virt_to_phys(td));}static inline void acacia_start_rx(struct acacia_local *lp, volatile DMAD_t rd){	rc32438_start_dma(lp->rx_dma_regs, virt_to_phys(rd));}static inline void acacia_chain_tx(struct acacia_local *lp, volatile DMAD_t td){	rc32438_chain_dma(lp->tx_dma_regs, virt_to_phys(td));}static inline void acacia_chain_rx(struct acacia_local *lp, volatile DMAD_t rd){	rc32438_chain_dma(lp->rx_dma_regs, virt_to_phys(rd));}#ifdef ACACIA_PROC_DEBUGstatic int acacia_read_proc(char *buf, char **start, off_t fpos,			    int length, int *eof, void *data){	struct net_device *dev = (struct net_device *)data;	struct acacia_local *lp = (struct acacia_local *)dev->priv;	int len = 0;	/* print out header */	len += sprintf(buf + len, "\n\tRC32438 Ethernet Debug\n\n");	len += sprintf (buf + len,			"DMA halt count      = %10d, total pkt cnt = %10d\n",			lp->dma_halt_cnt, lp->halt_tx_count);	len += sprintf (buf + len,			"DMA run count       = %10d, total pkt cnt = %10d\n",			lp->dma_run_cnt, lp->run_tx_count);	len += sprintf (buf + len,			"DMA race count      = %10d, total pkt cnt = %10d\n",			lp->dma_race_cnt, lp->race_tx_count);	len += sprintf (buf + len,			"DMA collision count = %10d, total pkt cnt = %10d\n",			lp->dma_collide_cnt, lp->collide_tx_count);	if (fpos >= len) 	{		*start = buf;		*eof = 1;		return 0;	}	*start = buf + fpos;		if ((len -= fpos) > length)		return length;		*eof = 1;		return len;}#endif#ifdef CONFIG_NET_FASTROUTE	static int acacia_eth_accept_fastpath(struct net_device *dev, struct dst_entry *dst){	struct net_device *odev = dst->dev;	if ((dst->ops->protocol != __constant_htons(ETH_P_IP))	    || (odev->type != ARPHRD_ETHER)	    || !odev->accept_fastpath) {		return (-1);	}	return (0);}#endifint acacia_get_board_config(void){    int dataFound;    char *bd_config;    /*     * Find start of Board Configuration data, using heuristics:     * Search back from the (aliased) end of flash by 0x1000 bytes     * at a time until we find the string "PB32", which marks the     * start of Board Configuration.  Give up if we've searched     * more than 500KB.     */    dataFound = 0;    for (bd_config = (char *)0xbffff000;         bd_config > (char *)0xbff80000;         bd_config -= 0x1000)    {        if ( *(int *)bd_config == IDT32438_BD_MAGIC) {            dataFound = 1;            break;        }    }    if (!dataFound) {        printk("Could not find Board Configuration Data\n");                bd_config = NULL;    }    idt32438_boardConfig = (struct idt32438_boarddata *) bd_config;    return(dataFound);}/* * Fetch a pointer to an ethernet's MAC address * in the Board Configuration data (in flash). */char *acacia_enet_mac_address_get(int MACUnit){        /* XXX: Hack for poorly configured boards.         *      Cannot setup bridging properly (brctl) when both enet         *      interfaces share the same MAC address.         *         */#ifdef CONFIG_ASK_MULT_MAC_HACK    static u8  enet0Mac[6] = {0x00, 0x0d, 0x0b, 0x13, 0x6b, 0x16};    static u8  enet1Mac[6] = {0x00, 0x0d, 0x0b, 0x13, 0x6b, 0x17};#endif    if (MACUnit == 0) {#ifndef CONFIG_ASK_MULT_MAC_HACK        return idt32438_boardConfig->enet0Mac;#else                return enet0Mac;#endif    }    if (MACUnit == 1) {#ifndef CONFIG_ASK_MULT_MAC_HACK        return idt32438_boardConfig->enet1Mac;#else                return enet1Mac;#endif    }    printk("Invalid ethernet MAC unit number (%d)!\n", MACUnit);    return NULL;}/* * Restart the ACACIA ethernet controller.  */static int acacia_restart(struct net_device *dev){	struct acacia_local *lp = (struct acacia_local *)dev->priv;		/*	 * Disable interrupts	 */	disable_irq(lp->rx_irq);	disable_irq(lp->tx_irq);#ifdef	RC32438_REVISION_ZA	disable_irq(lp->ovr_irq);#endif		disable_irq(lp->und_irq);	/* Mask F E bit in Tx DMA */	local_writel(local_readl(&lp->tx_dma_regs->dmasm) | DMASM_f_m | DMASM_e_m, &lp->tx_dma_regs->dmasm);	/* Mask D H E bit in Rx DMA */	local_writel(local_readl(&lp->rx_dma_regs->dmasm) | DMASM_d_m | DMASM_h_m | DMASM_e_m, &lp->rx_dma_regs->dmasm);	acacia_init(dev);	acacia_multicast_list(dev);	enable_irq(lp->und_irq);#ifdef	RC32438_REVISION_ZA	enable_irq(lp->ovr_irq);#endif	enable_irq(lp->tx_irq);	enable_irq(lp->rx_irq);	return 0;}int acacia_init_module(void){	int i, retval=0;	for (i = 0; acacia_iflist[i].iobase; i++) 	{ 		retval |= acacia_probe(i);	} 	return retval;}static int acacia_probe(int port_num){	struct acacia_local *lp = NULL;	struct acacia_if_t *bif = &acacia_iflist[port_num];	struct net_device *dev = NULL;	int i, retval;	char *addr;		request_region(acacia_iflist[port_num].iobase, 0x24C, "ACACIA");			/* Allocate a new 'dev' if needed */	dev = init_etherdev(0, sizeof(struct acacia_local));	bif->dev = dev;		INFO("RC32438 ethernet%d found at 0x%08x\n", 		port_num, acacia_iflist[port_num].iobase);	/* Fill in the 'dev' fields. */	dev->base_addr = bif->iobase;	/* just use the rx dma irq */	dev->irq = bif->rx_dma_irq; 	if (acacia_get_board_config()) {		/* Board Config data exists. Get it */	        addr = acacia_enet_mac_address_get(port_num);		/* if MAC is bogus in config data, skip */        	if((*(u32 *)addr == 0xffffffff) && (*(u16 *)(addr+4)==0xffff)){			printk("ERROR: Bogus MAC config Data\n");			printk("Using Default MAC address \n");			addr = bif->mac_str;        	}    	}	else {		printk("Using Default MAC address \n");		addr = bif->mac_str;	}    	memcpy(dev->dev_addr, addr, dev->addr_len );	INFO("HW Address ");	for (i = 0; i < 6; i++) 	{		printk("%2.2x", dev->dev_addr[i]);		if (i<5)			printk(":");	}	printk("\n");    	INFO("Rx IRQ %d, Tx IRQ %d\n", bif->rx_dma_irq, bif->tx_dma_irq);    	/* Initialize the device structure. */	if (dev->priv == NULL) 	{		lp = (struct acacia_local *)kmalloc(sizeof(*lp), GFP_KERNEL);		memset(lp, 0, sizeof(struct acacia_local));	} 	else 	{		lp = (struct acacia_local *)dev->priv;	}	dev->priv = lp;	lp->rx_irq = bif->rx_dma_irq;	lp->tx_irq = bif->tx_dma_irq;	lp->ovr_irq = bif->rx_ovr_irq;	lp->und_irq = bif->tx_und_irq;	lp->eth_regs = ioremap_nocache(bif->iobase,				       sizeof(*lp->eth_regs));	if (!lp->eth_regs) 	{		ERR("Can't remap eth registers\n");		retval = -ENXIO;		goto probe_err_out;	}	if (port_num == 0)	{	    	lp->rx_dma_regs =			ioremap_nocache(DMA0_PhysicalAddress + 4 * DMA_CHAN_OFFSET,	sizeof(struct DMA_Chan_s));	     	if (!lp->rx_dma_regs) 		{			ERR("Can't remap Rx DMA registers\n");			retval = -ENXIO;			goto probe_err_out;		}			    	lp->tx_dma_regs =			ioremap_nocache(DMA0_PhysicalAddress  + 5 * DMA_CHAN_OFFSET,				sizeof(struct DMA_Chan_s));	     	if (!lp->tx_dma_regs) 		{			ERR("Can't remap Tx DMA registers\n");			retval = -ENXIO;			goto probe_err_out;		}#ifdef ACACIA_PROC_DEBUG	lp->ps = create_proc_read_entry ("net/rc32438_0", 0, NULL,					 acacia_read_proc, dev);#endif		}     	else if (port_num == 1)	{		lp->rx_dma_regs =			ioremap_nocache(DMA0_PhysicalAddress  + 2 * DMA_CHAN_OFFSET,				sizeof(struct DMA_Chan_s));	     	if (!lp->rx_dma_regs) 		{			ERR("Can't remap Rx DMA registers\n");			retval = -ENXIO;			goto probe_err_out;	     	}			    	lp->tx_dma_regs =		ioremap_nocache(DMA0_PhysicalAddress  + 3 * DMA_CHAN_OFFSET,				sizeof(struct DMA_Chan_s));	     	if (!lp->tx_dma_regs) 		{			ERR("Can't remap Tx DMA registers\n");			retval = -ENXIO;			goto probe_err_out;	     	}#ifdef ACACIA_PROC_DEBUG	lp->ps = create_proc_read_entry ("net/rc32438_1", 0, NULL,					 acacia_read_proc, dev);#endif	        	}	lp->td_ring = (DMAD_t)kmalloc(TD_RING_SIZE + RD_RING_SIZE, GFP_KERNEL);	if (!lp->td_ring) 	{		ERR("Can't allocate descriptors\n");		retval = -ENOMEM;		goto probe_err_out;	}	dma_cache_inv((unsigned long)(lp->td_ring),TD_RING_SIZE + RD_RING_SIZE);

⌨️ 快捷键说明

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