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

📄 epic100.c

📁 powerpc内核mpc8241linux系统下net驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
	ep->force_fd = duplex;	ep->default_port = option;	if (ep->default_port)		ep->medialock = 1;	/* The Epic-specific entries in the device structure. */	dev->open = &epic_open;	dev->hard_start_xmit = &epic_start_xmit;	dev->stop = &epic_close;	dev->get_stats = &epic_get_stats;	dev->set_multicast_list = &set_rx_mode;	dev->do_ioctl = &mii_ioctl;	return dev;}/* Serial EEPROM section. *//*  EEPROM_Ctrl bits. */#define EE_SHIFT_CLK	0x04	/* EEPROM shift clock. */#define EE_CS			0x02	/* EEPROM chip select. */#define EE_DATA_WRITE	0x08	/* EEPROM chip data in. */#define EE_WRITE_0		0x01#define EE_WRITE_1		0x09#define EE_DATA_READ	0x10	/* EEPROM chip data out. */#define EE_ENB			(0x0001 | EE_CS)/* Delay between EEPROM clock transitions.   No extra delay is needed with 33Mhz PCI, but 66Mhz is untested. */#ifdef _LINUX_DELAY_H#define eeprom_delay(nanosec)	udelay(1)#else#define eeprom_delay(nanosec)	do { ; } while (0)#endif/* The EEPROM commands include the alway-set leading bit. */#define EE_WRITE_CMD	(5 << 6)#define EE_READ64_CMD	(6 << 6)#define EE_READ256_CMD	(6 << 8)#define EE_ERASE_CMD	(7 << 6)static int read_eeprom(long ioaddr, int location){	int i;	int retval = 0;	long ee_addr = ioaddr + EECTL;	int read_cmd = location |		(inl(ee_addr) & 0x40) ? EE_READ64_CMD : EE_READ256_CMD;	outl(EE_ENB & ~EE_CS, ee_addr);	outl(EE_ENB, ee_addr);	/* Shift the read command bits out. */	for (i = 12; i >= 0; i--) {		short dataval = (read_cmd & (1 << i)) ? EE_WRITE_1 : EE_WRITE_0;		outl(EE_ENB | dataval, ee_addr);		eeprom_delay(100);		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);		eeprom_delay(150);	}	outl(EE_ENB, ee_addr);	for (i = 16; i > 0; i--) {		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);		eeprom_delay(100);		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);		outl(EE_ENB, ee_addr);		eeprom_delay(100);	}	/* Terminate the EEPROM access. */	outl(EE_ENB & ~EE_CS, ee_addr);	return retval;}#define MII_READOP		1#define MII_WRITEOP		2static int mdio_read(long ioaddr, int phy_id, int location){	int i;	outl((phy_id << 9) | (location << 4) | MII_READOP, ioaddr + MIICtrl);	/* Typical operation takes < 50 ticks. */	for (i = 4000; i > 0; i--)		if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0)			return inw(ioaddr + MIIData);	return 0xffff;}static void mdio_write(long ioaddr, int phy_id, int location, int value){	int i;	outw(value, ioaddr + MIIData);	outl((phy_id << 9) | (location << 4) | MII_WRITEOP, ioaddr + MIICtrl);	for (i = 10000; i > 0; i--) {		if ((inl(ioaddr + MIICtrl) & MII_WRITEOP) == 0)			break;	}	return;}static intepic_open(struct device *dev){	struct epic_private *ep = (struct epic_private *)dev->priv;	long ioaddr = dev->base_addr;	int i;	int mii_reg5;	ep->full_duplex = ep->force_fd;	/* Soft reset the chip. */	outl(0x4001, ioaddr + GENCTL);	if (request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, "SMC EPIC/100", dev))		return -EAGAIN;	MOD_INC_USE_COUNT;	epic_init_ring(dev);	outl(0x4000, ioaddr + GENCTL);	/* This next magic! line by Ken Yamaguchi.. ?? */	outl(0x0008, ioaddr + TEST1);	/* Pull the chip out of low-power mode, enable interrupts, and set for	   PCI read multiple.  The MIIcfg setting and strange write order are	   required by the details of which bits are reset and the transceiver	   wiring on the Ositech CardBus card.	*/	outl(0x12, ioaddr + MIICfg);	if (ep->chip_id == 6)		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);#if defined(__powerpc__) || defined(__sparc__)		/* Big endian */	outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);#else	outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);#endif	for (i = 0; i < 3; i++)		outl(((u16*)dev->dev_addr)[i], ioaddr + LAN0 + i*4);	outl(TX_FIFO_THRESH, ioaddr + TxThresh);	mii_reg5 = mdio_read(ioaddr, ep->phys[0], 5);	if (mii_reg5 != 0xffff) {		if ((mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040)			ep->full_duplex = 1;		else if (! (mii_reg5 & 0x4000))			mdio_write(ioaddr, ep->phys[0], 0, 0x1200);		if (epic_debug > 1)			printk(KERN_INFO "%s: Setting %s-duplex based on MII xcvr %d"				   " register read of %4.4x.\n", dev->name,				   ep->full_duplex ? "full" : "half", ep->phys[0], mii_reg5);	}	outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);	outl(virt_to_bus(ep->rx_ring), ioaddr + PRxCDAR);	outl(virt_to_bus(ep->tx_ring), ioaddr + PTxCDAR);	/* Start the chip's Rx process. */	set_rx_mode(dev);	outl(0x000A, ioaddr + COMMAND);	dev->tbusy = 0;	dev->interrupt = 0;	dev->start = 1;	/* Enable interrupts by setting the interrupt mask. */	outl((ep->chip_id == 6 ? PCIBusErr175 : PCIBusErr170)		 | CntFull | TxUnderrun | TxDone		 | RxError | RxOverflow | RxFull | RxHeader | RxDone,		 ioaddr + INTMASK);	if (epic_debug > 1)		printk(KERN_DEBUG "%s: epic_open() ioaddr %lx IRQ %d status %4.4x "			   "%s-duplex.\n",			   dev->name, ioaddr, dev->irq, inl(ioaddr + GENCTL),			   ep->full_duplex ? "full" : "half");	/* Set the timer to switch to check for link beat and perhaps switch	   to an alternate media type. */	init_timer(&ep->timer);	ep->timer.expires = RUN_AT((24*HZ)/10);			/* 2.4 sec. */	ep->timer.data = (unsigned long)dev;	ep->timer.function = &epic_timer;				/* timer handler */	add_timer(&ep->timer);	return 0;}/* Reset the chip to recover from a PCI transaction error.   This may occur at interrupt time. */static void epic_pause(struct device *dev){	long ioaddr = dev->base_addr;	struct epic_private *ep = (struct epic_private *)dev->priv;	/* Disable interrupts by clearing the interrupt mask. */	outl(0x00000000, ioaddr + INTMASK);	/* Stop the chip's Tx and Rx DMA processes. */	outw(0x0061, ioaddr + COMMAND);	/* Update the error counts. */	if (inw(ioaddr + COMMAND) != 0xffff) {		ep->stats.rx_missed_errors += inb(ioaddr + MPCNT);		ep->stats.rx_frame_errors += inb(ioaddr + ALICNT);		ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT);	}	/* Remove the packets on the Rx queue. */	epic_rx(dev);}static void epic_restart(struct device *dev){	long ioaddr = dev->base_addr;	struct epic_private *ep = (struct epic_private *)dev->priv;	int i;	printk(KERN_DEBUG "%s: Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n",		   dev->name, ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx);	/* Soft reset the chip. */	outl(0x0001, ioaddr + GENCTL);	udelay(1);	/* Duplicate code from epic_open(). */	outl(0x0008, ioaddr + TEST1);#if defined(__powerpc__)		/* Big endian */	outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);#else	outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);#endif	outl(0x12, ioaddr + MIICfg);	if (ep->chip_id == 6)		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);	for (i = 0; i < 3; i++)		outl(((u16*)dev->dev_addr)[i], ioaddr + LAN0 + i*4);	outl(TX_FIFO_THRESH, ioaddr + TxThresh);	outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);	outl(virt_to_bus(&ep->rx_ring[ep->cur_rx%RX_RING_SIZE]), ioaddr + PRxCDAR);	outl(virt_to_bus(&ep->tx_ring[ep->dirty_tx%TX_RING_SIZE]),		 ioaddr + PTxCDAR);	/* Start the chip's Rx process. */	set_rx_mode(dev);	outl(0x000A, ioaddr + COMMAND);	/* Enable interrupts by setting the interrupt mask. */	outl((ep->chip_id == 6 ? PCIBusErr175 : PCIBusErr170)		 | CntFull | TxUnderrun | TxDone		 | RxError | RxOverflow | RxFull | RxHeader | RxDone,		 ioaddr + INTMASK);	printk(KERN_DEBUG "%s: epic_restart() done, cmd status %4.4x, ctl %4.4x"		   " interrupt %4.4x.\n",			   dev->name, inl(ioaddr + COMMAND), inl(ioaddr + GENCTL),		   inl(ioaddr + INTSTAT));	return;}static void epic_timer(unsigned long data){	struct device *dev = (struct device *)data;	struct epic_private *ep = (struct epic_private *)dev->priv;	long ioaddr = dev->base_addr;	int next_tick = 0;	int mii_reg5 = mdio_read(ioaddr, ep->phys[0], 5);	if (epic_debug > 3) {		printk(KERN_DEBUG "%s: Media selection tick, Tx status %8.8x.\n",			   dev->name, inl(ioaddr + TxSTAT));		printk(KERN_DEBUG "%s: Other registers are IntMask %4.4x "			   "IntStatus %4.4x RxStatus %4.4x.\n",			   dev->name, inl(ioaddr + INTMASK), inl(ioaddr + INTSTAT),			   inl(ioaddr + RxSTAT));	}	if (! ep->force_fd  &&  mii_reg5 != 0xffff) {		int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040;		if (ep->full_duplex != duplex) {			ep->full_duplex = duplex;			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"				   " partner capability of %4.4x.\n", dev->name,				   ep->full_duplex ? "full" : "half", ep->phys[0], mii_reg5);			outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);		}		next_tick = 60*HZ;	}	if (next_tick) {		ep->timer.expires = RUN_AT(next_tick);		add_timer(&ep->timer);	}}static void epic_tx_timeout(struct device *dev){	struct epic_private *ep = (struct epic_private *)dev->priv;	long ioaddr = dev->base_addr;	if (epic_debug > 0) {		printk(KERN_WARNING "%s: Transmit timeout using MII device, "			   "Tx status %4.4x.\n",			   dev->name, inw(ioaddr + TxSTAT));		if (epic_debug > 1) {			printk(KERN_DEBUG "%s: Tx indices: dirty_tx %d, cur_tx %d.\n",			 dev->name, ep->dirty_tx, ep->cur_tx);		}	}	if (inw(ioaddr + TxSTAT) & 0x10) {		/* Tx FIFO underflow. */		ep->stats.tx_fifo_errors++;		/* Restart the transmit process. */		outl(0x0080, ioaddr + COMMAND);	}	/* Perhaps stop and restart the chip's Tx processes . */	/* Trigger a transmit demand. */	outl(0x0004, dev->base_addr + COMMAND);	dev->trans_start = jiffies;	ep->stats.tx_errors++;	return;}/* Initialize the Rx and Tx rings, along with various 'dev' bits. */static voidepic_init_ring(struct device *dev){	struct epic_private *ep = (struct epic_private *)dev->priv;	int i;	ep->tx_full = 0;	ep->cur_rx = ep->cur_tx = 0;	ep->dirty_rx = ep->dirty_tx = 0;	for (i = 0; i < RX_RING_SIZE; i++) {		ep->rx_ring[i].status = 0x8000;		/* Owned by Epic chip */		ep->rx_ring[i].buflength = PKT_BUF_SZ;		{			/* Note the receive buffer must be longword aligned.			   dev_alloc_skb() provides 16 byte alignment.  But do *not*			   use skb_reserve() to align the IP header! */			struct sk_buff *skb;			skb = dev_alloc_skb(PKT_BUF_SZ);			ep->rx_skbuff[i] = skb;			if (skb == NULL)				break;			/* Bad news!  */			skb->dev = dev;			/* Mark as being used by this device. */			skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */			ep->rx_ring[i].bufaddr = virt_to_bus(skb->tail);		}		ep->rx_ring[i].next = virt_to_bus(&ep->rx_ring[i+1]);	}	/* Mark the last entry as wrapping the ring. */	ep->rx_ring[i-1].next = virt_to_bus(&ep->rx_ring[0]);	/* The Tx buffer descriptor is filled in as needed, but we	   do need to clear the ownership bit. */	for (i = 0; i < TX_RING_SIZE; i++) {		ep->tx_skbuff[i] = 0;		ep->tx_ring[i].status = 0x0000;		ep->tx_ring[i].next = virt_to_bus(&ep->tx_ring[i+1]);	}	ep->tx_ring[i-1].next = virt_to_bus(&ep->tx_ring[0]);}static intepic_start_xmit(struct sk_buff *skb, struct device *dev){	struct epic_private *ep = (struct epic_private *)dev->priv;	int entry;	u32 flag;	/* Block a timer-based transmit from overlapping.  This could better be	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {		if (jiffies - dev->trans_start < TX_TIMEOUT)			return 1;		epic_tx_timeout(dev);		return 1;	}	/* Caution: the write order is important here, set the base address	   with the "ownership" bits last. */	/* Calculate the next Tx descriptor entry. */	entry = ep->cur_tx % TX_RING_SIZE;	ep->tx_skbuff[entry] = skb;	ep->tx_ring[entry].txlength = (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN);	ep->tx_ring[entry].bufaddr = virt_to_bus(skb->data);	ep->tx_ring[entry].buflength = skb->len;	/* tx_bytes counting -- Nolan Leake */		ep->stats.tx_bytes += ep->tx_ring[entry].txlength;		if (ep->cur_tx - ep->dirty_tx < TX_RING_SIZE/2) {/* Typical path */	  flag = 0x10; /* No interrupt */	  clear_bit(0, (void*)&dev->tbusy);	} else if (ep->cur_tx - ep->dirty_tx == TX_RING_SIZE/2) {	  flag = 0x14; /* Tx-done intr. */	  clear_bit(0, (void*)&dev->tbusy);	} else if (ep->cur_tx - ep->dirty_tx < TX_RING_SIZE - 2) {	  flag = 0x10; /* No Tx-done intr. */	  clear_bit(0, (void*)&dev->tbusy);	} else {	  /* Leave room for two additional entries. */	  flag = 0x14; /* Tx-done intr. */	  ep->tx_full = 1;	}	ep->tx_ring[entry].control = flag;	ep->tx_ring[entry].status = 0x8000;	/* Pass ownership to the chip. */	ep->cur_tx++;	/* Trigger an immediate transmit demand. */	outl(0x0004, dev->base_addr + COMMAND);	dev->trans_start = jiffies;	if (epic_debug > 4)		printk(KERN_DEBUG "%s: Queued Tx packet size %d to slot %d, "			   "flag %2.2x Tx status %8.8x.\n",			   dev->name, (int)skb->len, entry, flag,			   inl(dev->base_addr + TxSTAT));	return 0;}/* The interrupt handler does all of the Rx thread work and cleans up   after the Tx thread. */static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs){	struct device *dev = (struct device *)dev_instance;	struct epic_private *ep = (struct epic_private *)dev->priv;	long ioaddr = dev->base_addr;	int status, boguscnt = max_interrupt_work;#if defined(__i386__)	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */	if (test_and_set_bit(0, (void*)&dev->interrupt)) {		printk(KERN_ERR "%s: SMP simultaneous entry of an interrupt handler.\n",			   dev->name);		dev->interrupt = 0;	/* Avoid halting machine. */		return;	}#else	if (dev->interrupt) {		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);		return;	}	dev->interrupt = 1;#endif	do {		status = inl(ioaddr + INTSTAT);		/* Acknowledge all of the current interrupt sources ASAP. */		outl(status & 0x00007fff, ioaddr + INTSTAT);		if (epic_debug > 4)			printk("%s: interrupt  interrupt=%#8.8x new intstat=%#8.8x.\n",				   dev->name, status, inl(ioaddr + INTSTAT));		if ((status & IntrSummary) == 0)			break;		if (status & (RxDone | RxStarted | RxEarlyWarn))			epic_rx(dev);		if (status & (TxEmpty | TxDone)) {			int dirty_tx;			for (dirty_tx = ep->dirty_tx; dirty_tx < ep->cur_tx; dirty_tx++) {				int entry = dirty_tx % TX_RING_SIZE;				int txstatus = ep->tx_ring[entry].status;				if (txstatus < 0)					break;			/* It still hasn't been Txed */				if ( ! (txstatus & 0x0001)) {					/* There was an major error, log it. */

⌨️ 快捷键说明

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