au1000_eth.c

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

C
1,315
字号
/* * * Alchemy Au1x00 ethernet driver * * Copyright 2001-2003, 2006 MontaVista Software Inc. * Copyright 2002 TimeSys Corp. * Added ethtool/mii-tool support, * Copyright 2004 Matt Porter <mporter@kernel.crashing.org> * Update: 2004 Bjoern Riemer, riemer@fokus.fraunhofer.de * or riemer@riemer-nt.de: fixed the link beat detection with * ioctls (SIOCGMIIPHY) * Copyright 2006 Herbert Valerio Riedel <hvr@gnu.org> *  converted to use linux-2.6.x's PHY framework * * Author: MontaVista Software, Inc. *         	ppopov@mvista.com or source@mvista.com * * ######################################################################## * *  This program is free software; you can distribute it and/or modify it *  under the terms of the GNU General Public License (Version 2) as *  published by the Free Software Foundation. * *  This program is distributed in the hope it will be useful, but WITHOUT *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License *  for more details. * *  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., *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * * ######################################################################## * * */#include <linux/dma-mapping.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/in.h>#include <linux/ioport.h>#include <linux/bitops.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/skbuff.h>#include <linux/delay.h>#include <linux/crc32.h>#include <linux/phy.h>#include <asm/cpu.h>#include <asm/mipsregs.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/processor.h>#include <au1000.h>#include <prom.h>#include "au1000_eth.h"#ifdef AU1000_ETH_DEBUGstatic int au1000_debug = 5;#elsestatic int au1000_debug = 3;#endif#define DRV_NAME	"au1000_eth"#define DRV_VERSION	"1.6"#define DRV_AUTHOR	"Pete Popov <ppopov@embeddedalley.com>"#define DRV_DESC	"Au1xxx on-chip Ethernet driver"MODULE_AUTHOR(DRV_AUTHOR);MODULE_DESCRIPTION(DRV_DESC);MODULE_LICENSE("GPL");// prototypesstatic void hard_stop(struct net_device *);static void enable_rx_tx(struct net_device *dev);static struct net_device * au1000_probe(int port_num);static int au1000_init(struct net_device *);static int au1000_open(struct net_device *);static int au1000_close(struct net_device *);static int au1000_tx(struct sk_buff *, struct net_device *);static int au1000_rx(struct net_device *);static irqreturn_t au1000_interrupt(int, void *);static void au1000_tx_timeout(struct net_device *);static void set_rx_mode(struct net_device *);static int au1000_ioctl(struct net_device *, struct ifreq *, int);static int mdio_read(struct net_device *, int, int);static void mdio_write(struct net_device *, int, int, u16);static void au1000_adjust_link(struct net_device *);static void enable_mac(struct net_device *, int);/* * Theory of operation * * The Au1000 MACs use a simple rx and tx descriptor ring scheme. * There are four receive and four transmit descriptors.  These * descriptors are not in memory; rather, they are just a set of * hardware registers. * * Since the Au1000 has a coherent data cache, the receive and * transmit buffers are allocated from the KSEG0 segment. The * hardware registers, however, are still mapped at KSEG1 to * make sure there's no out-of-order writes, and that all writes * complete immediately. *//* These addresses are only used if yamon doesn't tell us what * the mac address is, and the mac address is not passed on the * command line. */static unsigned char au1000_mac_addr[6] __devinitdata = {	0x00, 0x50, 0xc2, 0x0c, 0x30, 0x00};struct au1000_private *au_macs[NUM_ETH_INTERFACES];/* * board-specific configurations * * PHY detection algorithm * * If AU1XXX_PHY_STATIC_CONFIG is undefined, the PHY setup is * autodetected: * * mii_probe() first searches the current MAC's MII bus for a PHY, * selecting the first (or last, if AU1XXX_PHY_SEARCH_HIGHEST_ADDR is * defined) PHY address not already claimed by another netdev. * * If nothing was found that way when searching for the 2nd ethernet * controller's PHY and AU1XXX_PHY1_SEARCH_ON_MAC0 is defined, then * the first MII bus is searched as well for an unclaimed PHY; this is * needed in case of a dual-PHY accessible only through the MAC0's MII * bus. * * Finally, if no PHY is found, then the corresponding ethernet * controller is not registered to the network subsystem. *//* autodetection defaults */#undef  AU1XXX_PHY_SEARCH_HIGHEST_ADDR#define AU1XXX_PHY1_SEARCH_ON_MAC0/* static PHY setup * * most boards PHY setup should be detectable properly with the * autodetection algorithm in mii_probe(), but in some cases (e.g. if * you have a switch attached, or want to use the PHY's interrupt * notification capabilities) you can provide a static PHY * configuration here * * IRQs may only be set, if a PHY address was configured * If a PHY address is given, also a bus id is required to be set * * ps: make sure the used irqs are configured properly in the board * specific irq-map */#if defined(CONFIG_MIPS_BOSPORUS)/* * Micrel/Kendin 5 port switch attached to MAC0, * MAC0 is associated with PHY address 5 (== WAN port) * MAC1 is not associated with any PHY, since it's connected directly * to the switch. * no interrupts are used */# define AU1XXX_PHY_STATIC_CONFIG# define AU1XXX_PHY0_ADDR  5# define AU1XXX_PHY0_BUSID 0#  undef AU1XXX_PHY0_IRQ#  undef AU1XXX_PHY1_ADDR#  undef AU1XXX_PHY1_BUSID#  undef AU1XXX_PHY1_IRQ#endif#if defined(AU1XXX_PHY0_BUSID) && (AU1XXX_PHY0_BUSID > 0)# error MAC0-associated PHY attached 2nd MACs MII bus not supported yet#endif/* * MII operations */static int mdio_read(struct net_device *dev, int phy_addr, int reg){	struct au1000_private *aup = (struct au1000_private *) dev->priv;	volatile u32 *const mii_control_reg = &aup->mac->mii_control;	volatile u32 *const mii_data_reg = &aup->mac->mii_data;	u32 timedout = 20;	u32 mii_control;	while (*mii_control_reg & MAC_MII_BUSY) {		mdelay(1);		if (--timedout == 0) {			printk(KERN_ERR "%s: read_MII busy timeout!!\n",					dev->name);			return -1;		}	}	mii_control = MAC_SET_MII_SELECT_REG(reg) |		MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_READ;	*mii_control_reg = mii_control;	timedout = 20;	while (*mii_control_reg & MAC_MII_BUSY) {		mdelay(1);		if (--timedout == 0) {			printk(KERN_ERR "%s: mdio_read busy timeout!!\n",					dev->name);			return -1;		}	}	return (int)*mii_data_reg;}static void mdio_write(struct net_device *dev, int phy_addr, int reg, u16 value){	struct au1000_private *aup = (struct au1000_private *) dev->priv;	volatile u32 *const mii_control_reg = &aup->mac->mii_control;	volatile u32 *const mii_data_reg = &aup->mac->mii_data;	u32 timedout = 20;	u32 mii_control;	while (*mii_control_reg & MAC_MII_BUSY) {		mdelay(1);		if (--timedout == 0) {			printk(KERN_ERR "%s: mdio_write busy timeout!!\n",					dev->name);			return;		}	}	mii_control = MAC_SET_MII_SELECT_REG(reg) |		MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_WRITE;	*mii_data_reg = value;	*mii_control_reg = mii_control;}static int mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum){	/* WARNING: bus->phy_map[phy_addr].attached_dev == dev does	 * _NOT_ hold (e.g. when PHY is accessed through other MAC's MII bus) */	struct net_device *const dev = bus->priv;	enable_mac(dev, 0); /* make sure the MAC associated with this			     * mii_bus is enabled */	return mdio_read(dev, phy_addr, regnum);}static int mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,			 u16 value){	struct net_device *const dev = bus->priv;	enable_mac(dev, 0); /* make sure the MAC associated with this			     * mii_bus is enabled */	mdio_write(dev, phy_addr, regnum, value);	return 0;}static int mdiobus_reset(struct mii_bus *bus){	struct net_device *const dev = bus->priv;	enable_mac(dev, 0); /* make sure the MAC associated with this			     * mii_bus is enabled */	return 0;}static int mii_probe (struct net_device *dev){	struct au1000_private *const aup = (struct au1000_private *) dev->priv;	struct phy_device *phydev = NULL;#if defined(AU1XXX_PHY_STATIC_CONFIG)	BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);	if(aup->mac_id == 0) { /* get PHY0 */# if defined(AU1XXX_PHY0_ADDR)		phydev = au_macs[AU1XXX_PHY0_BUSID]->mii_bus.phy_map[AU1XXX_PHY0_ADDR];# else		printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",			dev->name);		return 0;# endif /* defined(AU1XXX_PHY0_ADDR) */	} else if (aup->mac_id == 1) { /* get PHY1 */# if defined(AU1XXX_PHY1_ADDR)		phydev = au_macs[AU1XXX_PHY1_BUSID]->mii_bus.phy_map[AU1XXX_PHY1_ADDR];# else		printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",			dev->name);		return 0;# endif /* defined(AU1XXX_PHY1_ADDR) */	}#else /* defined(AU1XXX_PHY_STATIC_CONFIG) */	int phy_addr;	/* find the first (lowest address) PHY on the current MAC's MII bus */	for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)		if (aup->mii_bus.phy_map[phy_addr]) {			phydev = aup->mii_bus.phy_map[phy_addr];# if !defined(AU1XXX_PHY_SEARCH_HIGHEST_ADDR)			break; /* break out with first one found */# endif		}# if defined(AU1XXX_PHY1_SEARCH_ON_MAC0)	/* try harder to find a PHY */	if (!phydev && (aup->mac_id == 1)) {		/* no PHY found, maybe we have a dual PHY? */		printk (KERN_INFO DRV_NAME ": no PHY found on MAC1, "			"let's see if it's attached to MAC0...\n");		BUG_ON(!au_macs[0]);		/* find the first (lowest address) non-attached PHY on		 * the MAC0 MII bus */		for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {			struct phy_device *const tmp_phydev =				au_macs[0]->mii_bus.phy_map[phy_addr];			if (!tmp_phydev)				continue; /* no PHY here... */			if (tmp_phydev->attached_dev)				continue; /* already claimed by MAC0 */			phydev = tmp_phydev;			break; /* found it */		}	}# endif /* defined(AU1XXX_PHY1_SEARCH_OTHER_BUS) */#endif /* defined(AU1XXX_PHY_STATIC_CONFIG) */	if (!phydev) {		printk (KERN_ERR DRV_NAME ":%s: no PHY found\n", dev->name);		return -1;	}	/* now we are supposed to have a proper phydev, to attach to... */	BUG_ON(!phydev);	BUG_ON(phydev->attached_dev);	phydev = phy_connect(dev, phydev->dev.bus_id, &au1000_adjust_link, 0,			PHY_INTERFACE_MODE_MII);	if (IS_ERR(phydev)) {		printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);		return PTR_ERR(phydev);	}	/* mask with MAC supported features */	phydev->supported &= (SUPPORTED_10baseT_Half			      | SUPPORTED_10baseT_Full			      | SUPPORTED_100baseT_Half			      | SUPPORTED_100baseT_Full			      | SUPPORTED_Autoneg			      /* | SUPPORTED_Pause | SUPPORTED_Asym_Pause */			      | SUPPORTED_MII			      | SUPPORTED_TP);	phydev->advertising = phydev->supported;	aup->old_link = 0;	aup->old_speed = 0;	aup->old_duplex = -1;	aup->phy_dev = phydev;	printk(KERN_INFO "%s: attached PHY driver [%s] "	       "(mii_bus:phy_addr=%s, irq=%d)\n",	       dev->name, phydev->drv->name, phydev->dev.bus_id, phydev->irq);	return 0;}/* * Buffer allocation/deallocation routines. The buffer descriptor returned * has the virtual and dma address of a buffer suitable for * both, receive and transmit operations. */static db_dest_t *GetFreeDB(struct au1000_private *aup){	db_dest_t *pDB;	pDB = aup->pDBfree;	if (pDB) {		aup->pDBfree = pDB->pnext;	}	return pDB;}void ReleaseDB(struct au1000_private *aup, db_dest_t *pDB){	db_dest_t *pDBfree = aup->pDBfree;	if (pDBfree)		pDBfree->pnext = pDB;	aup->pDBfree = pDB;}static void enable_rx_tx(struct net_device *dev){	struct au1000_private *aup = (struct au1000_private *) dev->priv;	if (au1000_debug > 4)		printk(KERN_INFO "%s: enable_rx_tx\n", dev->name);	aup->mac->control |= (MAC_RX_ENABLE | MAC_TX_ENABLE);	au_sync_delay(10);}static void hard_stop(struct net_device *dev){	struct au1000_private *aup = (struct au1000_private *) dev->priv;	if (au1000_debug > 4)		printk(KERN_INFO "%s: hard stop\n", dev->name);	aup->mac->control &= ~(MAC_RX_ENABLE | MAC_TX_ENABLE);	au_sync_delay(10);}static void enable_mac(struct net_device *dev, int force_reset){	unsigned long flags;

⌨️ 快捷键说明

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