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

📄 acenic.c

📁 powerpc内核mpc8241linux系统下net驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * acenic.c: Linux driver for the Alteon AceNIC Gigabit Ethernet card *           and other Tigon based cards. * * Copyright 1998 by Jes Sorensen, <Jes.Sorensen@cern.ch>. * * Thanks to Alteon and 3Com for providing hardware and documentation * enabling me to write this driver. * * A mailing list for discussing the use of this driver has been * setup, please subscribe to the lists if you have any questions * about the driver. Send mail to linux-acenic-help@sunsite.auc.dk to * see how to subscribe. * * 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. * * Additional work by Pete Wyckoff <wyckoff@ca.sandia.gov> for initial * Alpha and trace dump support. */#define PKT_COPY_THRESHOLD 300#include <linux/module.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/pci.h>#include <linux/kernel.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/mm.h>#include <net/sock.h>#include <net/ip.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/byteorder.h>#include <asm/uaccess.h>#include "acenic.h"/* * These must be defined before the firmware is included. */#define MAX_TEXT_LEN	96*1024#define MAX_RODATA_LEN	8*1024#define MAX_DATA_LEN	2*1024#include "acenic_firmware.h"#ifndef PCI_VENDOR_ID_ALTEON#define PCI_VENDOR_ID_ALTEON		0x12ae	#define PCI_DEVICE_ID_ALTEON_ACENIC	0x0001#endif#ifndef PCI_DEVICE_ID_3COM_3C985#define PCI_DEVICE_ID_3COM_3C985	0x0001#endif#ifndef PCI_VENDOR_ID_NETGEAR#define PCI_VENDOR_ID_NETGEAR		0x1385#define PCI_DEVICE_ID_NETGEAR_GA620	0x620a#endif/* * This driver currently supports Tigon I and Tigon II based cards * including the Alteon AceNIC and the 3Com 3C985. The driver should * also work on the NetGear GA620, however I have not been able to * test that myself. * * This card is really neat, it supports receive hardware checksumming * and jumbo frames (up to 9000 bytes) and does a lot of work in the * firmware. Also the programming interface is quite neat, except for * the parts dealing with the i2c eeprom on the card ;-) * * Using jumbo frames: * * To enable jumbo frames, simply specify an mtu between 1500 and 9000 * bytes to ifconfig. Jumbo frames can be enabled or disabled at any time * by running `ifconfig eth<X> mtu <MTU>' with <X> being the Ethernet * interface number and <MTU> being the MTU value. * * Module parameters: * * When compiled as a loadable module, the driver allows for a number * of module parameters to be specified. The driver supports the * following module parameters: * *  trace=<val> - Firmware trace level. This requires special traced *                firmware to replace the firmware supplied with *                the driver - for debugging purposes only. * *  link=<val>  - Link state. Normally you want to use the default link *                parameters set by the driver. This can be used to *                override these in case your switch doesn't negotiate *                the link properly. Valid values are: *         0x0001 - Force half duplex link. *         0x0002 - Do not negotiate line speed with the other end. *         0x0010 - 10Mbit/sec link. *         0x0020 - 100Mbit/sec link. *         0x0040 - 1000Mbit/sec link. *         0x0100 - Do not negotiate flow control. *         0x0200 - Enable RX flow control Y *         0x0400 - Enable TX flow control Y (Tigon II NICs only). *                Default value is 0x0270, ie. enable link+flow *                control negotiation. Negotiating the highest *                possible link speed with RX flow control enabled. * *                When disabling link speed negotiation, only one link *                speed is allowed to be specified! * *  tx_coal_tick=<val> - number of coalescing clock ticks (us) allowed *                to wait for more packets to arive before *                interrupting the host, from the time the first *                packet arrives. * *  rx_coal_tick=<val> - number of coalescing clock ticks (us) allowed *                to wait for more packets to arive in the transmit ring, *                before interrupting the host, after transmitting the *                first packet in the ring. * *  max_tx_desc=<val> - maximum number of transmit descriptors *                (packets) transmitted before interrupting the host. * *  max_rx_desc=<val> - maximum number of receive descriptors *                (packets) received before interrupting the host. * *  tx_ratio=<val> - 7 bit value (0 - 63) specifying the split in 64th *                increments of the NIC's on board memory to be used for *                transmit and receive buffers. For the 1MB NIC app. 800KB *                is available, on the 1/2MB NIC app. 300KB is available. *                68KB will always be available as a minimum for both *                directions. The default value is a 50/50 split. * * If you use more than one NIC, specify the parameters for the * individual NICs with a comma, ie. trace=0,0x00001fff,0 you want to * run tracing on NIC #2 but not on NIC #1 and #3. * * TODO: * * - Proper multicast support. * - NIC dump support. * - More tuning parameters. * * The mini ring is not used under Linux and I am not sure it makes sense * to actually use it. *//* * Default values for tuning parameters */#define DEF_TX_RATIO	31#define DEF_TX_COAL	TICKS_PER_SEC / 500#define DEF_TX_MAX_DESC	7#define DEF_RX_COAL	TICKS_PER_SEC / 10000#define DEF_RX_MAX_DESC	2#define DEF_TRACE	0#define DEF_STAT	2 * TICKS_PER_SECstatic int link[8] = {0, };static int trace[8] = {0, };static int tx_coal_tick[8] = {0, };static int rx_coal_tick[8] = {0, };static int max_tx_desc[8] = {0, };static int max_rx_desc[8] = {0, };static int tx_ratio[8] = {0, };static const char __initdata *version = "acenic.c: v0.32 03/15/99  Jes Sorensen (Jes.Sorensen@cern.ch)\n";static struct device *root_dev = NULL;static int probed __initdata = 0;__initfunc(int acenic_probe (struct device *dev)){	int boards_found = 0;	int version_disp;	struct ace_private *ap;	u8 pci_latency;#if 0	u16 vendor, device;	u8 pci_bus;	u8 pci_dev_fun;	u8 irq;#endif	struct pci_dev *pdev = NULL;	if (probed)		return -ENODEV;	probed ++;	if (!pci_present())		/* is PCI support present? */		return -ENODEV;	version_disp = 0;	while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET<<8, pdev))){		dev = NULL;		if (!((pdev->vendor == PCI_VENDOR_ID_ALTEON) &&		      (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC)) &&		    !((pdev->vendor == PCI_VENDOR_ID_3COM) &&		      (pdev->device == PCI_DEVICE_ID_3COM_3C985)) &&		    !((pdev->vendor == PCI_VENDOR_ID_NETGEAR) &&		      (pdev->device == PCI_DEVICE_ID_NETGEAR_GA620)))			continue;		dev = init_etherdev(dev, sizeof(struct ace_private));		if (dev == NULL){			printk(KERN_ERR "Unable to allocate etherdev "			       "structure!\n");			break;		}		if (!dev->priv)			dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL);		if (!dev->priv)			return -ENOMEM;		ap = dev->priv;		ap->pdev = pdev;		ap->vendor = pdev->vendor;		dev->irq = pdev->irq;#ifdef __SMP__		spin_lock_init(&ap->lock);#endif		dev->open = &ace_open;		dev->hard_start_xmit = &ace_start_xmit;		dev->stop = &ace_close;		dev->get_stats = &ace_get_stats;		dev->set_multicast_list = &ace_set_multicast_list;#if 0		dev->do_ioctl = &ace_ioctl;#endif		dev->set_mac_address = &ace_set_mac_addr;		dev->change_mtu = &ace_change_mtu;		/*		 * Dummy value.		 */		dev->base_addr = 42;		/* display version info if adapter is found */		if (!version_disp)		{			/* set display flag to TRUE so that */			/* we only display this string ONCE */			version_disp = 1;			printk(version);		}		pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command);		pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);		if (pci_latency <= 0x40){			pci_latency = 0x40;			pci_write_config_byte(pdev, PCI_LATENCY_TIMER,					      pci_latency);		}		pci_set_master(pdev);		switch(ap->vendor){		case PCI_VENDOR_ID_ALTEON:			sprintf(ap->name, "AceNIC Gigabit Ethernet");			printk(KERN_INFO "%s: Alteon AceNIC ", dev->name);			break;		case PCI_VENDOR_ID_3COM:			sprintf(ap->name, "3Com 3C985 Gigabit Ethernet");			printk(KERN_INFO "%s: 3Com 3C985 ", dev->name);			break;		case PCI_VENDOR_ID_NETGEAR:			sprintf(ap->name, "NetGear GA620 Gigabit Ethernet");			printk(KERN_INFO "%s: NetGear GA620 ", dev->name);			break;		default:			sprintf(ap->name, "Unknown AceNIC based Gigabit Ethernet");			printk(KERN_INFO "%s: Unknown AceNIC ", dev->name);			break;		}		printk("Gigabit Ethernet at 0x%08lx, irq %i, PCI latency %i "		       "clks\n", pdev->base_address[0], dev->irq, pci_latency);		/*		 * Remap the regs into kernel space.		 */		ap->regs = (struct ace_regs *)ioremap(pdev->base_address[0],						      0x4000);		if (!ap->regs){			printk(KERN_ERR "%s:  Unable to map I/O register, "			       "AceNIC %i will be disabled.\n",			       dev->name, boards_found);			break;		}#ifdef MODULE		if (ace_init(dev, boards_found))			continue;#else		if (ace_init(dev, -1))			continue;#endif		boards_found++;		/*		 * This is bollocks, but we need to tell the net-init		 * code that it shall go for the next device.		 */		dev->base_addr = 0;	}	/*	 * If we're at this point we're going through ace_probe() for	 * the first time.  Return success (0) if we've initialized 1	 * or more boards. Otherwise, return failure (-ENODEV).	 */#ifdef MODULE	return boards_found;#else	if (boards_found > 0)		return 0;	else		return -ENODEV;#endif}#ifdef MODULE#if LINUX_VERSION_CODE > 0x20118MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@cern.ch>");MODULE_DESCRIPTION("AceNIC/3C985 Gigabit Ethernet driver");MODULE_PARM(link, "1-" __MODULE_STRING(8) "i");MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i");MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i");MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i");MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i");MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i");#endifint init_module(void){	int cards;	root_dev = NULL;	cards = acenic_probe(NULL);	return cards ? 0 : -ENODEV;}void cleanup_module(void){	struct ace_private *ap;	struct ace_regs *regs;	struct device *next;	short i;	unsigned long flags;	while (root_dev){		next = ((struct ace_private *)root_dev->priv)->next;		ap = (struct ace_private *)root_dev->priv;		regs = ap->regs;		spin_lock_irqsave(&ap->lock, flags);		writel(readl(&regs->CpuCtrl) | CPU_HALT, &regs->CpuCtrl);		if (ap->version == 2)			writel(readl(&regs->CpuBCtrl) | CPU_HALT,			       &regs->CpuBCtrl);		writel(0, &regs->Mb0Lo);		spin_unlock_irqrestore(&ap->lock, flags);		/*		 * Release the RX buffers.		 */		for (i = 0; i < RX_STD_RING_ENTRIES; i++) {			if (ap->rx_std_skbuff[i]) {				ap->rx_std_ring[i].size = 0;				set_aceaddr_bus(&ap->rx_std_ring[i].addr, 0);				dev_kfree_skb(ap->rx_std_skbuff[i]);			}		}		iounmap(regs);		if(ap->trace_buf)			kfree(ap->trace_buf);		kfree(ap->info);		free_irq(root_dev->irq, root_dev);		unregister_netdev(root_dev);		kfree(root_dev);		root_dev = next;	}}#endif/* * Commands are considered to be slow. */static inline void ace_issue_cmd(struct ace_regs *regs, struct cmd *cmd){	u32 idx;	idx = readl(&regs->CmdPrd);	writel(*(u32 *)(cmd), &regs->CmdRng[idx]);	idx = (idx + 1) % CMD_RING_ENTRIES;	writel(idx, &regs->CmdPrd);}__initfunc(static int ace_init(struct device *dev, int board_idx)){	struct ace_private *ap;	struct ace_regs *regs;	struct ace_info *info;	u32 tig_ver, mac1, mac2, tmp;	unsigned long tmp_ptr, myjif;	short i;	ap = dev->priv;	regs = ap->regs;	/*	 * Don't access any other registes before this point!	 */#ifdef __BIG_ENDIAN	writel(((BYTE_SWAP | WORD_SWAP | CLR_INT) |		((BYTE_SWAP | WORD_SWAP | CLR_INT) << 24)),	       &regs->HostCtrl);#else	writel((CLR_INT | WORD_SWAP | ((CLR_INT | WORD_SWAP) << 24)),	       &regs->HostCtrl);#endif	mb();	/*	 * Stop the NIC CPU and clear pending interrupts	 */	writel(readl(&regs->CpuCtrl) | CPU_HALT, &regs->CpuCtrl);	writel(0, &regs->Mb0Lo);	tig_ver = readl(&regs->HostCtrl) >> 28;	switch(tig_ver){	case 4:		printk(KERN_INFO"  Tigon I (Rev. 4), Firmware: %i.%i.%i, ",		       tigonFwReleaseMajor, tigonFwReleaseMinor,		       tigonFwReleaseFix);		writel(0, &regs->LocalCtrl);		ap->version = 1;		break;	case 6:		printk(KERN_INFO"  Tigon II (Rev. %i), Firmware: %i.%i.%i, ",		       tig_ver, tigon2FwReleaseMajor, tigon2FwReleaseMinor,		       tigon2FwReleaseFix);		writel(readl(&regs->CpuBCtrl) | CPU_HALT, &regs->CpuBCtrl);		writel(SRAM_BANK_512K, &regs->LocalCtrl);		writel(SYNC_SRAM_TIMING, &regs->MiscCfg);		ap->version = 2;		break;	default:		printk(KERN_INFO"  Unsupported Tigon version detected (%i), ",		       tig_ver);		return -ENODEV;	}	/*	 * ModeStat _must_ be set after the SRAM settings as this change	 * seems to corrupt the ModeStat and possible other registers.	 * The SRAM settings survive resets and setting it to the same	 * value a second time works as well. This is what caused the	 * `Firmware not running' problem on the Tigon II.	 */#ifdef __LITTLE_ENDIAN	writel(ACE_BYTE_SWAP_DATA | ACE_WARN | ACE_FATAL |	       ACE_WORD_SWAP | ACE_NO_JUMBO_FRAG, &regs->ModeStat);#else#error "this driver doesn't run on big-endian machines yet!"#endif	mac1 = 0;	for(i = 0; i < 4; i++){		mac1 = mac1 << 8;		mac1 |= read_eeprom_byte(regs, 0x8c+i);	}	mac2 = 0;	for(i = 4; i < 8; i++){		mac2 = mac2 << 8;		mac2 |= read_eeprom_byte(regs, 0x8c+i);	}	writel(mac1, &regs->MacAddrHi);	writel(mac2, &regs->MacAddrLo);	printk("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",	       (mac1 >> 8) & 0xff, mac1 & 0xff, (mac2 >> 24) &0xff,	       (mac2 >> 16) & 0xff, (mac2 >> 8) & 0xff, mac2 & 0xff);	dev->dev_addr[0] = (mac1 >> 8) & 0xff;	dev->dev_addr[1] = mac1 & 0xff;	dev->dev_addr[2] = (mac2 >> 24) & 0xff;	dev->dev_addr[3] = (mac2 >> 16) & 0xff;	dev->dev_addr[4] = (mac2 >> 8) & 0xff;	dev->dev_addr[5] = mac2 & 0xff;	/*	 * Set the max DMA transfer size. Seems that for most systems	 * the performance is better when no MAX parameter is	 * set. However for systems enabling PCI write and invalidate,	 * DMA writes must be set to the L1 cache line size to get	 * optimal performance.	 */	tmp = READ_CMD_MEM | WRITE_CMD_MEM;	if (ap->version == 2){#if 0		/*		 * According to the documentation this enables writes		 * to all PCI regs - NOT good.		 */		tmp |= DMA_WRITE_ALL_ALIGN;#endif		tmp |= MEM_READ_MULTIPLE;		if (ap->pci_command & PCI_COMMAND_INVALIDATE){			switch(L1_CACHE_BYTES){			case 16:				tmp |= DMA_WRITE_MAX_16;				break;			case 32:				tmp |= DMA_WRITE_MAX_32;				break;			case 64:				tmp |= DMA_WRITE_MAX_64;				break;			default:				printk(KERN_INFO "  Cache line size %i not "				       "supported, PCI write and invalidate "				       "disabled\n", L1_CACHE_BYTES);				ap->pci_command &= ~PCI_COMMAND_INVALIDATE;				pci_write_config_word(ap->pdev, PCI_COMMAND,						      ap->pci_command);			}		}	}	writel(tmp, &regs->PciState);	if (request_irq(dev->irq, ace_interrupt, SA_SHIRQ, ap->name, dev)) {		printk(KERN_WARNING "%s: Requested IRQ %d is busy\n",		       dev->name, dev->irq);		return -EAGAIN;	}	/*	 * Initialize the generic info block and the command+event rings	 * and the control blocks for the transmit and receive rings	 * as they need to be setup once and for all.	 */	if (!(info = kmalloc(sizeof(struct ace_info), GFP_KERNEL | GFP_DMA))){		free_irq(dev->irq, dev);		return -EAGAIN;	}	/*	 * Register the device here to be able to catch allocated	 * interrupt handlers in case the firmware doesn't come up.	 */	ap->next = root_dev;	root_dev = dev;	ap->info = info;	memset(info, 0, sizeof(struct ace_info));	ace_load_firmware(dev);	ap->fw_running = 0;	tmp_ptr = virt_to_bus((void *)info);#if (BITS_PER_LONG == 64)	writel(tmp_ptr >> 32, &regs->InfoPtrHi);#else	writel(0, &regs->InfoPtrHi);#endif	writel(tmp_ptr & 0xffffffff, &regs->InfoPtrLo);	memset(ap->evt_ring, 0, EVT_RING_ENTRIES * sizeof(struct event));	set_aceaddr(&info->evt_ctrl.rngptr, ap->evt_ring);	info->evt_ctrl.flags = 0;	set_aceaddr(&info->evt_prd_ptr, &ap->evt_prd);	ap->evt_prd = 0;	writel(0, &regs->EvtCsm);	info->cmd_ctrl.flags = 0;	set_aceaddr_bus(&info->cmd_ctrl.rngptr, (void *)0x100);	info->cmd_ctrl.max_len = 0;	for (i = 0; i < CMD_RING_ENTRIES; i++)		writel(0, &regs->CmdRng[i]);	writel(0, &regs->CmdPrd);	writel(0, &regs->CmdCsm);	set_aceaddr(&info->stats2_ptr, &info->s.stats);	info->rx_std_ctrl.max_len = ACE_STD_MTU + ETH_HLEN + 4;	set_aceaddr(&info->rx_std_ctrl.rngptr, ap->rx_std_ring);	info->rx_std_ctrl.flags = FLG_RX_TCP_UDP_SUM;	memset(ap->rx_std_ring, 0,	       RX_STD_RING_ENTRIES * sizeof(struct rx_desc));	info->rx_jumbo_ctrl.max_len = 0;	set_aceaddr(&info->rx_jumbo_ctrl.rngptr, ap->rx_jumbo_ring);	info->rx_jumbo_ctrl.flags = FLG_RX_TCP_UDP_SUM;

⌨️ 快捷键说明

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