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

📄 ibm_ocp_enet.c

📁 linux-2.4.29操作系统的源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * ibm_ocp_enet.c * * Ethernet driver for the built in ethernet on the IBM 4xx PowerPC * processors. *  * (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org> * * Based on original work by * *      Armin Kuster <akuster@mvista.com> * 	Johnnie Peters <jpeters@mvista.com> * * 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. * * TODO *       - Check for races in the "remove" code path *       - Add some Power Management to the MAC and the PHY *       - Audit remaining of non-rewritten code (--BenH) *       - Cleanup message display using msglevel mecanism * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <asm/processor.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/ocp.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/crc32.h>#include "ibm_ocp_enet.h"//#define MDIO_DEBUG(fmt) printk fmt#define MDIO_DEBUG(fmt)//#define LINK_DEBUG(fmt) printk fmt#define LINK_DEBUG(fmt)//#define PKT_DEBUG(fmt) printk fmt#define PKT_DEBUG(fmt)#define DRV_NAME        "emac"#define DRV_VERSION     "2.0"#define DRV_AUTHOR      "Benjamin Herrenschmidt <benh@kernel.crashing.org>"#define DRV_DESC        "IBM OCP EMAC Ethernet driver"MODULE_AUTHOR(DRV_AUTHOR);MODULE_DESCRIPTION(DRV_DESC);MODULE_LICENSE("GPL");static int skb_res = SKB_RES;MODULE_PARM(skb_res, "i");MODULE_PARM_DESC(skb_res, "Amount of data to reserve on skb buffs\n"		 "The 405 handles a misaligned IP header fine but\n"		 "this can help if you are routing to a tunnel or a\n"		 "device that needs aligned data. 0..2");#define ZMII_PRIV(ocpdev) ((struct ibm_ocp_zmii*)ocp_get_drvdata(ocpdev))static unsigned int zmii_enable[][4] = {	{ZMII_SMII0, ZMII_RMII0, ZMII_MII0,	 ~(ZMII_MDI1 | ZMII_MDI2 | ZMII_MDI3)},	{ZMII_SMII1, ZMII_RMII1, ZMII_MII1,	 ~(ZMII_MDI0 | ZMII_MDI2 | ZMII_MDI3)},	{ZMII_SMII2, ZMII_RMII2, ZMII_MII2,	 ~(ZMII_MDI0 | ZMII_MDI1 | ZMII_MDI3)},	{ZMII_SMII3, ZMII_RMII3, ZMII_MII3, ~(ZMII_MDI0 | ZMII_MDI1 | ZMII_MDI2)}};static unsigned int mdi_enable[] =    { ZMII_MDI0, ZMII_MDI1, ZMII_MDI2, ZMII_MDI3 };static unsigned int zmii_speed = 0x0;static unsigned int zmii_speed100[] = { ZMII_MII0_100MB, ZMII_MII1_100MB };/* Since multiple EMACs share MDIO lines in various ways, we need * to avoid re-using the same PHY ID in cases where the arch didn't * setup precise emac_phy_map entries */static u32 busy_phy_map = 0;static struct net_device_stats *emac_stats(struct net_device *dev){	struct ocp_enet_private *fep = dev->priv;	return &fep->stats;}static intemac_init_zmii(struct ocp_device *ocpdev, int mode){	struct ibm_ocp_zmii *zmii = ZMII_PRIV(ocpdev);	struct zmii_regs *zmiip;	const char *mode_name[] = { "SMII", "RMII", "MII" };		if (zmii){		/* We have already initialized ZMII device,		   so just increment refcount and return */			zmii->users++;		return 0;		   	}		zmii = kmalloc(sizeof(struct ibm_ocp_zmii), GFP_KERNEL);	if (zmii == NULL) {		printk(KERN_ERR "zmii%d: Out of memory allocating ZMII structure!\n",			ocpdev->def->index);		return -ENOMEM;	}	memset(zmii, 0, sizeof(*zmii));			zmiip = (struct zmii_regs *)ioremap(ocpdev->def->paddr, sizeof(*zmiip));	if (zmiip == NULL){		printk(KERN_ERR "zmii%d: Cannot ioremap bridge registers!\n",			ocpdev->def->index);				kfree(zmii);		return -ENOMEM;		}			if (mode == ZMII_AUTO) {		if (zmiip->fer & (ZMII_MII0 | ZMII_MII1 | 				  ZMII_MII2 | ZMII_MII3))			mode = MII;		if (zmiip->fer & (ZMII_RMII0 | ZMII_RMII1 |				  ZMII_RMII2 | ZMII_RMII3))			mode = RMII;		if (zmiip->fer & (ZMII_SMII0 | ZMII_SMII1 |				  ZMII_SMII2 | ZMII_SMII3))			mode = SMII;		/* Failsafe: ZMII_AUTO is invalid index into the arrays,		   so force SMII if all else fails. */		if (mode == ZMII_AUTO)			mode = SMII;	}		zmii->base = zmiip;	zmii->mode = mode;	zmii->users++;	ocp_set_drvdata(ocpdev, zmii);	printk(KERN_NOTICE "zmii%d: bridge in %s mode\n", ocpdev->def->index,		mode_name[mode]);	return 0;}static voidemac_enable_zmii_port(struct ocp_device *ocpdev, int input){	u32 mask;	struct ibm_ocp_zmii *zmii = ZMII_PRIV(ocpdev);	mask  = in_be32(&zmii->base->fer);	mask &= zmii_enable[input][MDI];	/* turn all non enabled MDI's off */	mask |= zmii_enable[input][zmii->mode] | mdi_enable[input];	out_be32(&zmii->base->fer, mask);}static voidemac_zmii_port_speed(struct ocp_device *ocpdev, int input, int speed){	struct ibm_ocp_zmii *zmii = ZMII_PRIV(ocpdev);	if (speed == 100)		zmii_speed |= zmii_speed100[input];	else		zmii_speed &= ~zmii_speed100[input];	out_be32(&zmii->base->ssr, zmii_speed);}static voidemac_fini_zmii(struct ocp_device *ocpdev){	struct ibm_ocp_zmii *zmii = ZMII_PRIV(ocpdev);	BUG_ON(!zmii || zmii->users == 0);	if (!--zmii->users){		ocp_set_drvdata(ocpdev, NULL);		iounmap((void*)zmii->base);		kfree(zmii);	}}intemac_phy_read(struct net_device *dev, int mii_id, int reg){	register int i;	uint32_t stacr;	struct ocp_enet_private *fep = dev->priv;	volatile emac_t *emacp = fep->emacp;	MDIO_DEBUG(("%s: phy_read, id: 0x%x, reg: 0x%x\n", dev->name, mii_id, reg));	/* Enable proper ZMII port */	if (fep->zmii_dev)		emac_enable_zmii_port(fep->zmii_dev, fep->zmii_input);	/* Use the EMAC that has the MDIO port */	if (fep->mdio_dev) {		dev = fep->mdio_dev;		fep = dev->priv;	}		/* Wait for data transfer complete bit */	for (i = 0; i < OCP_RESET_DELAY; ++i) {		if (emacp->em0stacr & EMAC_STACR_OC)			break;		udelay(MDIO_DELAY);	/* changed to 2 with new scheme -armin */	}	if ((emacp->em0stacr & EMAC_STACR_OC) == 0) {		printk(KERN_WARNING "%s: PHY read timeout #1!\n", dev->name);		return -1;	}	/* Clear the speed bits and make a read request to the PHY */	stacr = ((EMAC_STACR_READ | (reg & 0x1f)) & ~EMAC_STACR_CLK_100MHZ);	stacr |= ((mii_id & 0x1F) << 5);	out_be32(&emacp->em0stacr, stacr);	stacr = in_be32(&emacp->em0stacr);	/* Wait for data transfer complete bit */	for (i = 0; i < OCP_RESET_DELAY; ++i) {		if ((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC)			break;		udelay(MDIO_DELAY);	}	if ((stacr & EMAC_STACR_OC) == 0) {		printk(KERN_WARNING "%s: PHY read timeout #2!\n", dev->name);		return -1;	}	/* Check for a read error */	if (stacr & EMAC_STACR_PHYE) {	        MDIO_DEBUG(("OCP MDIO PHY error !\n"));		return -1;	}   	MDIO_DEBUG((" -> 0x%x\n", stacr >> 16));	return (stacr >> 16);}voidemac_phy_write(struct net_device *dev, int mii_id, int reg, int data){	register int i = 0;	uint32_t stacr;	struct ocp_enet_private *fep = dev->priv;	volatile emac_t *emacp = fep->emacp;	MDIO_DEBUG(("%s phy_write, id: 0x%x, reg: 0x%x, data: 0x%x\n",            dev->name, mii_id, reg, data));	/* Enable proper ZMII port */	if (fep->zmii_dev)		emac_enable_zmii_port(fep->zmii_dev, fep->zmii_input);	/* Use the EMAC that has the MDIO port */	if (fep->mdio_dev) {		dev = fep->mdio_dev;		fep = dev->priv;	}	/* Wait for data transfer complete bit */	for (i = 0; i < OCP_RESET_DELAY; ++i) {		if (emacp->em0stacr & EMAC_STACR_OC)			break;		udelay(MDIO_DELAY);	/* changed to 2 with new scheme -armin */	}	if ((emacp->em0stacr & EMAC_STACR_OC) == 0) {		printk(KERN_WARNING "%s: PHY write timeout #2!\n", dev->name);		return;	}	/* Clear the speed bits and make a read request to the PHY */	stacr = ((EMAC_STACR_WRITE | (reg & 0x1f)) & ~EMAC_STACR_CLK_100MHZ);	stacr |= ((mii_id & 0x1f) << 5) | ((data & 0xffff) << 16);	out_be32(&emacp->em0stacr, stacr);	/* Wait for data transfer complete bit */	for (i = 0; i < OCP_RESET_DELAY; ++i) {		if ((stacr = emacp->em0stacr) & EMAC_STACR_OC)			break;		udelay(MDIO_DELAY);	}	if ((emacp->em0stacr & EMAC_STACR_OC) == 0)		printk(KERN_WARNING "%s: PHY write timeout #2!\n", dev->name);	/* Check for a write error */	if ((stacr & EMAC_STACR_PHYE) != 0) {		MDIO_DEBUG(("OCP MDIO PHY error !\n"));        }}static voidemac_wakeup_irq(int irq, void *param, struct pt_regs *regs){	struct net_device *dev = param;	/* On Linux the 405 ethernet will always be active if configured	 * in.  This interrupt should never occur.	 */	printk(KERN_INFO "%s: WakeUp interrupt !\n", dev->name);}static voidemac_txeob_dev(void *param, u32 chanmask){	struct net_device *dev = param;	struct ocp_enet_private *fep = dev->priv;        unsigned long flags;        spin_lock_irqsave(&fep->lock, flags);	PKT_DEBUG(("emac_txeob_dev() entry, tx_cnt: %d\n", fep->tx_cnt));	while (fep->tx_cnt &&	       !(fep->tx_desc[fep->ack_slot].ctrl & MAL_TX_CTRL_READY)) {		/* Tell the system the transmit completed. */		dev_kfree_skb_irq(fep->tx_skb[fep->ack_slot]);		if (fep->tx_desc[fep->ack_slot].ctrl &		    (EMAC_TX_ST_EC | EMAC_TX_ST_MC | EMAC_TX_ST_SC))			fep->stats.collisions++;		fep->tx_skb[fep->ack_slot] = (struct sk_buff *) NULL;		if (++fep->ack_slot == NUM_TX_BUFF)			fep->ack_slot = 0;		fep->tx_cnt--;	}	if (fep->tx_cnt < NUM_TX_BUFF)       		netif_wake_queue(dev);	PKT_DEBUG(("emac_txeob_dev() exit, tx_cnt: %d\n", fep->tx_cnt));	        spin_unlock_irqrestore(&fep->lock, flags);}/*  Fill/Re-fill the rx chain with valid ctrl/ptrs.  This function will fill from rx_slot up to the parm end.  So to completely fill the chain pre-set rx_slot to 0 and  pass in an end of 0. */static voidemac_rx_fill(struct net_device *dev, int end){	int i;	struct ocp_enet_private *fep = dev->priv;	unsigned char *ptr;	i = fep->rx_slot;	do {		if (fep->rx_skb[i] != NULL) {			/*We will trust the skb is still in a good state */			ptr = (char *) virt_to_phys(fep->rx_skb[i]->data);		} else {			/* We don't want the 16 bytes skb_reserve done by dev_alloc_skb,			 * it breaks our cache line alignement. However, we still allocate			 * +16 so that we end up allocating the exact same size as			 * dev_alloc_skb() would do.			 * Also, because of the skb_res, the max DMA size we give to EMAC			 * is slighly wrong, causing it to potentially DMA 2 more bytes                         * from a broken/oversized packet. These 16 bytes will take care                         * that we don't walk on somebody else toes with that.			 */			fep->rx_skb[i] =			    alloc_skb(DESC_RX_BUF_SIZE + 16, GFP_ATOMIC);			if (fep->rx_skb[i] == NULL) {				/* Keep rx_slot here, the next time clean/fill is called				 * we will try again before the MAL wraps back here				 * If the MAL tries to use this descriptor with				 * the EMPTY bit off it will cause the				 * rxde interrupt.  That is where we will				 * try again to allocate an sk_buff.				 */				break;			}			if (skb_res)				skb_reserve(fep->rx_skb[i], skb_res);			/* We must NOT consistent_sync the cache line right after the			 * buffer, so we must crop our sync size to account for the			 * reserved space			 */			consistent_sync((void *) fep->rx_skb[i]->					data, (DESC_RX_BUF_SIZE-skb_res),					PCI_DMA_FROMDEVICE);			ptr = (char *) virt_to_phys(fep->rx_skb[i]->data);		}		fep->rx_desc[i].ctrl = MAL_RX_CTRL_EMPTY | MAL_RX_CTRL_INTR |	/*could be smarter about this to avoid ints at high loads */		    (i == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);		fep->rx_desc[i].data_ptr = ptr;		/*		   * 440GP uses the previously reserved bits in the		   * data_len to encode the upper 4-bits of the buffer		   * physical address (ERPN). Initialize these.		 */		fep->rx_desc[i].data_len = 0;	} while ((i = (i + 1) % NUM_RX_BUFF) != end);	fep->rx_slot = i;}static void emac_rx_clean(struct net_device *dev, int call_rx_fill){	int i;	int error, frame_length;	struct ocp_enet_private *fep = dev->priv;	unsigned short ctrl;	int slots_walked = 0;	i = fep->rx_slot;	PKT_DEBUG(("emac_rx_clean() entry, call_rx_fill: %d, rx_slot: %d\n", call_rx_fill, fep->rx_slot));	do {		if (fep->rx_skb[i] == NULL)			goto skip;	/*we have already handled the packet but haved failed to alloc */		/* 		   since rx_desc is in uncached mem we don't keep reading it directly 		   we pull out a local copy of ctrl and do the checks on the copy.		 */		ctrl = fep->rx_desc[i].ctrl;		if (ctrl & MAL_RX_CTRL_EMPTY)			break;	/*we don't have any more ready packets */		if (ctrl & EMAC_BAD_RX_PACKET) {			fep->stats.rx_errors++;			fep->stats.rx_dropped++;

⌨️ 快捷键说明

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