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

📄 gmac.c

📁 linux和2410结合开发 用他可以生成2410所需的zImage文件
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Network device driver for the GMAC ethernet controller on * Apple G4 Powermacs. * * Copyright (C) 2000 Paul Mackerras & Ben. Herrenschmidt *  * portions based on sunhme.c by David S. Miller * * Changes: * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/06/2000 * - check init_etherdev return in gmac_probe1 * BenH <benh@kernel.crashing.org> - 03/09/2000 * - Add support for new PHYs * - Add some PowerBook sleep code * BenH <benh@kernel.crashing.org> - ??/??/???? *  - PHY updates * BenH <benh@kernel.crashing.org> - 08/08/2001 * - Add more PHYs, fixes to sleep code */#include <linux/module.h>#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/init.h>#include <linux/pci.h>#include <asm/prom.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/machdep.h>#include <asm/pmac_feature.h>#include <asm/keylargo.h>#include <asm/pci-bridge.h>#ifdef CONFIG_PMAC_PBOOK#include <linux/adb.h>#include <linux/pmu.h>#include <asm/irq.h>#endif#include "gmac.h"#define DEBUG_PHY/* Driver version 1.5, kernel 2.4.x */#define GMAC_VERSION	"v1.5k4"#define DUMMY_BUF_LEN	RX_BUF_ALLOC_SIZE + RX_OFFSET + GMAC_BUFFER_ALIGNstatic unsigned char *dummy_buf;static struct net_device *gmacs;/* Prototypes */static int mii_read(struct gmac *gm, int phy, int r);static int mii_write(struct gmac *gm, int phy, int r, int v);static void mii_poll_start(struct gmac *gm);static void mii_poll_stop(struct gmac *gm);static void mii_interrupt(struct gmac *gm);static int mii_lookup_and_reset(struct gmac *gm);static void mii_setup_phy(struct gmac *gm);static int mii_do_reset_phy(struct gmac *gm, int phy_addr);static void mii_init_BCM5400(struct gmac *gm);static void mii_init_BCM5401(struct gmac *gm);static void gmac_set_power(struct gmac *gm, int power_up);static int gmac_powerup_and_reset(struct net_device *dev);static void gmac_set_gigabit_mode(struct gmac *gm, int gigabit);static void gmac_set_duplex_mode(struct gmac *gm, int full_duplex);static void gmac_mac_init(struct gmac *gm, unsigned char *mac_addr);static void gmac_init_rings(struct gmac *gm, int from_irq);static void gmac_start_dma(struct gmac *gm);static void gmac_stop_dma(struct gmac *gm);static void gmac_set_multicast(struct net_device *dev);static int gmac_open(struct net_device *dev);static int gmac_close(struct net_device *dev);static void gmac_tx_timeout(struct net_device *dev);static int gmac_xmit_start(struct sk_buff *skb, struct net_device *dev);static void gmac_tx_cleanup(struct net_device *dev, int force_cleanup);static void gmac_receive(struct net_device *dev);static void gmac_interrupt(int irq, void *dev_id, struct pt_regs *regs);static struct net_device_stats *gmac_stats(struct net_device *dev);static int gmac_probe(void);static void gmac_probe1(struct device_node *gmac);#ifdef CONFIG_PMAC_PBOOKint gmac_sleep_notify(struct pmu_sleep_notifier *self, int when);static struct pmu_sleep_notifier gmac_sleep_notifier = {	gmac_sleep_notify, SLEEP_LEVEL_NET,};#endif/* * Read via the mii interface from a PHY register */static intmii_read(struct gmac *gm, int phy, int r){	int timeout;	GM_OUT(GM_MIF_FRAME_CTL_DATA,		(0x01 << GM_MIF_FRAME_START_SHIFT) |		(0x02 << GM_MIF_FRAME_OPCODE_SHIFT) |		GM_MIF_FRAME_TURNAROUND_HI |		(phy << GM_MIF_FRAME_PHY_ADDR_SHIFT) |		(r << GM_MIF_FRAME_REG_ADDR_SHIFT));			for (timeout = 1000; timeout > 0; --timeout) {		udelay(20);		if (GM_IN(GM_MIF_FRAME_CTL_DATA) & GM_MIF_FRAME_TURNAROUND_LO)			return GM_IN(GM_MIF_FRAME_CTL_DATA) & GM_MIF_FRAME_DATA_MASK;	}	return -1;}/* * Write on the mii interface to a PHY register */static intmii_write(struct gmac *gm, int phy, int r, int v){	int timeout;	GM_OUT(GM_MIF_FRAME_CTL_DATA,		(0x01 << GM_MIF_FRAME_START_SHIFT) |		(0x01 << GM_MIF_FRAME_OPCODE_SHIFT) |		GM_MIF_FRAME_TURNAROUND_HI |		(phy << GM_MIF_FRAME_PHY_ADDR_SHIFT) |		(r << GM_MIF_FRAME_REG_ADDR_SHIFT) |		(v & GM_MIF_FRAME_DATA_MASK));	for (timeout = 1000; timeout > 0; --timeout) {		udelay(20);		if (GM_IN(GM_MIF_FRAME_CTL_DATA) & GM_MIF_FRAME_TURNAROUND_LO)			return 0;	}	return -1;}/* * Start MIF autopolling of the PHY status register */static void mii_poll_start(struct gmac *gm){	unsigned int tmp;		/* Start the MIF polling on the external transceiver. */	tmp = GM_IN(GM_MIF_CFG);	tmp &= ~(GM_MIF_CFGPR_MASK | GM_MIF_CFGPD_MASK);	tmp |= ((gm->phy_addr & 0x1f) << GM_MIF_CFGPD_SHIFT);	tmp |= (MII_SR << GM_MIF_CFGPR_SHIFT);	tmp |= GM_MIF_CFGPE;	GM_OUT(GM_MIF_CFG, tmp);	/* Let the bits set. */	udelay(GM_MIF_POLL_DELAY);	GM_OUT(GM_MIF_IRQ_MASK, 0xffc0);}/* * Stop MIF autopolling of the PHY status register */static void mii_poll_stop(struct gmac *gm){	GM_OUT(GM_MIF_IRQ_MASK, 0xffff);	GM_BIC(GM_MIF_CFG, GM_MIF_CFGPE);	udelay(GM_MIF_POLL_DELAY);}/* * Called when the MIF detect a change of the PHY status *  * handles monitoring the link and updating GMAC with the correct * duplex mode. *  * Note: Are we missing status changes ? In this case, we'll have to * a timer and control the autoneg. process more closely. Also, we may * want to stop rx and tx side when the link is down. *//* Link modes of the BCM5400 PHY */static int phy_BCM5400_link_table[8][3] = {	{ 0, 0, 0 },	/* No link */	{ 0, 0, 0 },	/* 10BT Half Duplex */	{ 1, 0, 0 },	/* 10BT Full Duplex */	{ 0, 1, 0 },	/* 100BT Half Duplex */	{ 0, 1, 0 },	/* 100BT Half Duplex */	{ 1, 1, 0 },	/* 100BT Full Duplex*/	{ 1, 0, 1 },	/* 1000BT */	{ 1, 0, 1 },	/* 1000BT */};static voidmii_interrupt(struct gmac *gm){	int		phy_status;	int		lpar_ability;		mii_poll_stop(gm);	/* May the status change before polling is re-enabled ? */	mii_poll_start(gm);		/* We read the Auxilliary Status Summary register */	phy_status = mii_read(gm, gm->phy_addr, MII_SR);	if ((phy_status ^ gm->phy_status) & (MII_SR_ASSC | MII_SR_LKS)) {		int		full_duplex = 0;		int		link_100 = 0;		int		gigabit = 0;#ifdef DEBUG_PHY		printk(KERN_INFO "%s: Link state change, phy_status: 0x%04x\n",			gm->dev->name, phy_status);#endif		gm->phy_status = phy_status;		/* Should we enable that in generic mode ? */		lpar_ability = mii_read(gm, gm->phy_addr, MII_ANLPA);		if (lpar_ability & MII_ANLPA_PAUS)			GM_BIS(GM_MAC_CTRL_CONFIG, GM_MAC_CTRL_CONF_SND_PAUSE_EN);		else			GM_BIC(GM_MAC_CTRL_CONFIG, GM_MAC_CTRL_CONF_SND_PAUSE_EN);		/* Link ? Check for speed and duplex */		if ((phy_status & MII_SR_LKS) && (phy_status & MII_SR_ASSC)) {		    int restart = 0;		    int aux_stat, link;		    switch (gm->phy_type) {		      case PHY_B5201:		      case PHY_B5221:		    	aux_stat = mii_read(gm, gm->phy_addr, MII_BCM5201_AUXCTLSTATUS);#ifdef DEBUG_PHY			printk(KERN_INFO "%s:    Link up ! BCM5201/5221 aux_stat: 0x%04x\n",				gm->dev->name, aux_stat);#endif		    	full_duplex = ((aux_stat & MII_BCM5201_AUXCTLSTATUS_DUPLEX) != 0);		    	link_100 = ((aux_stat & MII_BCM5201_AUXCTLSTATUS_SPEED) != 0);		        break;		      case PHY_B5400:		      case PHY_B5401:		      case PHY_B5411:		    	aux_stat = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXSTATUS);		    	link = (aux_stat & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>		    			MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT;#ifdef DEBUG_PHY			printk(KERN_INFO "%s:    Link up ! BCM54xx aux_stat: 0x%04x (link mode: %d)\n",				gm->dev->name, aux_stat, link);#endif		    	full_duplex = phy_BCM5400_link_table[link][0];		    	link_100 = phy_BCM5400_link_table[link][1];		    	gigabit = phy_BCM5400_link_table[link][2];		    	break;		      case PHY_LXT971:		    	aux_stat = mii_read(gm, gm->phy_addr, MII_LXT971_STATUS2);#ifdef DEBUG_PHY			printk(KERN_INFO "%s:    Link up ! LXT971 stat2: 0x%04x\n",				gm->dev->name, aux_stat);#endif		    	full_duplex = ((aux_stat & MII_LXT971_STATUS2_FULLDUPLEX) != 0);		    	link_100 = ((aux_stat & MII_LXT971_STATUS2_SPEED) != 0);		    	break;		      default:		    	full_duplex = (lpar_ability & MII_ANLPA_FDAM) != 0;		    	link_100 = (lpar_ability & MII_ANLPA_100M) != 0;		    	break;		    }#ifdef DEBUG_PHY		    printk(KERN_INFO "%s:    Full Duplex: %d, Speed: %s\n",		    	gm->dev->name, full_duplex,		    	gigabit ? "1000" : (link_100 ? "100" : "10"));#endif                    if (gigabit != gm->gigabit) {                    	gm->gigabit = gigabit;                    	gmac_set_gigabit_mode(gm, gm->gigabit);                    	restart = 1;                    }		    if (full_duplex != gm->full_duplex) {			gm->full_duplex = full_duplex;			gmac_set_duplex_mode(gm, gm->full_duplex);			restart = 1;		    }		    if (restart)			gmac_start_dma(gm);		} else if (!(phy_status & MII_SR_LKS)) {#ifdef DEBUG_PHY		    printk(KERN_INFO "%s:    Link down !\n", gm->dev->name);#endif		}	}}#ifdef CONFIG_PMAC_PBOOK/* Power management: stop PHY chip for suspend mode *  * TODO: This will have to be modified is WOL is to be supported. */static voidgmac_suspend(struct gmac* gm){	int data, timeout;	unsigned long flags;		gm->sleeping = 1;	netif_device_detach(gm->dev);	spin_lock_irqsave(&gm->lock, flags);	if (gm->opened) {		disable_irq(gm->dev->irq);		/* Stop polling PHY */		mii_poll_stop(gm);	}	/* Mask out all chips interrupts */	GM_OUT(GM_IRQ_MASK, 0xffffffff);	spin_unlock_irqrestore(&gm->lock, flags);		if (gm->opened) {		int i;		/* Empty Tx ring of any remaining gremlins */		gmac_tx_cleanup(gm->dev, 1);			/* Empty Rx ring of any remaining gremlins */		for (i = 0; i < NRX; ++i) {			if (gm->rx_buff[i] != 0) {				dev_kfree_skb_irq(gm->rx_buff[i]);				gm->rx_buff[i] = 0;			}		}	}	/* Clear interrupts on 5201 */	if (gm->phy_type == PHY_B5201 || gm->phy_type == PHY_B5221)		mii_write(gm, gm->phy_addr, MII_BCM5201_INTERRUPT, 0);			/* Drive MDIO high */	GM_OUT(GM_MIF_CFG, 0);		/* Unchanged, don't ask me why */	data = mii_read(gm, gm->phy_addr, MII_ANLPA);	mii_write(gm, gm->phy_addr, MII_ANLPA, data);		/* Stop everything */	GM_OUT(GM_MAC_RX_CONFIG, 0);	GM_OUT(GM_MAC_TX_CONFIG, 0);	GM_OUT(GM_MAC_XIF_CONFIG, 0);	GM_OUT(GM_TX_CONF, 0);	GM_OUT(GM_RX_CONF, 0);		/* Set MAC in reset state */	GM_OUT(GM_RESET, GM_RESET_TX | GM_RESET_RX);	for (timeout = 100; timeout > 0; --timeout) {		mdelay(10);		if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0)			break;	}	GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW);	GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW);	/* Superisolate PHY */	if (gm->phy_type == PHY_B5201 || gm->phy_type == PHY_B5221)		mii_write(gm, gm->phy_addr, MII_BCM5201_MULTIPHY,			MII_BCM5201_MULTIPHY_SUPERISOLATE);	/* Put MDIO in sane electric state. According to an obscure	 * Apple comment, not doing so may let them drive some current	 * during sleep and possibly damage BCM PHYs.	 */	GM_OUT(GM_MIF_CFG, GM_MIF_CFGBB);	GM_OUT(GM_MIF_BB_CLOCK, 0);	GM_OUT(GM_MIF_BB_DATA, 0);	GM_OUT(GM_MIF_BB_OUT_ENABLE, 0);	GM_OUT(GM_MAC_XIF_CONFIG,		GM_MAC_XIF_CONF_GMII_MODE|GM_MAC_XIF_CONF_MII_INT_LOOP);	(void)GM_IN(GM_MAC_XIF_CONFIG);		/* Unclock the GMAC chip */	gmac_set_power(gm, 0);}static voidgmac_resume(struct gmac *gm){	int data;	if (gmac_powerup_and_reset(gm->dev)) {		printk(KERN_ERR "%s: Couldn't revive gmac ethernet !\n", gm->dev->name);		return;	}	gm->sleeping = 0;		if (gm->opened) {		/* Create fresh rings */		gmac_init_rings(gm, 1);		/* re-initialize the MAC */		gmac_mac_init(gm, gm->dev->dev_addr);			/* re-initialize the multicast tables & promisc mode if any */		gmac_set_multicast(gm->dev);	}	/* Early enable Tx and Rx so that we are clocked */	GM_BIS(GM_TX_CONF, GM_TX_CONF_DMA_EN);	mdelay(20);	GM_BIS(GM_RX_CONF, GM_RX_CONF_DMA_EN);	mdelay(20);	GM_BIS(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE);	mdelay(20);	GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_ENABLE);	mdelay(20);	if (gm->phy_type == PHY_B5201 || gm->phy_type == PHY_B5221) {		data = mii_read(gm, gm->phy_addr, MII_BCM5201_MULTIPHY);		mii_write(gm, gm->phy_addr, MII_BCM5201_MULTIPHY,			data & ~MII_BCM5201_MULTIPHY_SUPERISOLATE);	}	mdelay(1);	if (gm->opened) {		/* restart polling PHY */		mii_interrupt(gm);		/* restart DMA operations */		gmac_start_dma(gm);		netif_device_attach(gm->dev);		enable_irq(gm->dev->irq);	} else {		/* Driver not opened, just leave things off. Note that		 * we could be smart and superisolate the PHY when the		 * driver is closed, but I won't do that unless I have		 * a better understanding of some electrical issues with		 * this PHY chip --BenH		 */		GM_OUT(GM_MAC_RX_CONFIG, 0);		GM_OUT(GM_MAC_TX_CONFIG, 0);		GM_OUT(GM_MAC_XIF_CONFIG, 0);		GM_OUT(GM_TX_CONF, 0);		GM_OUT(GM_RX_CONF, 0);	}}#endifstatic intmii_do_reset_phy(struct gmac *gm, int phy_addr){	int mii_control, timeout;		mii_control = mii_read(gm, phy_addr, MII_CR);	mii_write(gm, phy_addr, MII_CR, mii_control | MII_CR_RST);	mdelay(10);	for (timeout = 100; timeout > 0; --timeout) {		mii_control = mii_read(gm, phy_addr, MII_CR);		if (mii_control == -1) {			printk(KERN_ERR "%s PHY died after reset !\n",				gm->dev->name);			return 1;		}		if ((mii_control & MII_CR_RST) == 0)			break;		mdelay(10);	}	if (mii_control & MII_CR_RST) {		printk(KERN_ERR "%s PHY reset timeout !\n", gm->dev->name);		return 1;	}	mii_write(gm, phy_addr, MII_CR, mii_control & ~MII_CR_ISOL);	return 0;}/* Here's a bunch of configuration routines for * Broadcom PHYs used on various Mac models. Unfortunately, * except for the 5201, Broadcom never sent me any documentation, * so this is from my understanding of Apple's Open Firmware * drivers and Darwin's implementation */ static voidmii_init_BCM5400(struct gmac *gm){	int data;	/* Configure for gigabit full duplex */	data = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL);	data |= MII_BCM5400_AUXCONTROL_PWR10BASET;	mii_write(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL, data);		data = mii_read(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL);	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;	mii_write(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL, data);		mdelay(10);	/* Reset and configure cascaded 10/100 PHY */	mii_do_reset_phy(gm, 0x1f);		data = mii_read(gm, 0x1f, MII_BCM5201_MULTIPHY);	data |= MII_BCM5201_MULTIPHY_SERIALMODE;	mii_write(gm, 0x1f, MII_BCM5201_MULTIPHY, data);	data = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL);	data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;	mii_write(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL, data);}static voidmii_init_BCM5401(struct gmac *gm){	int data;	int rev;	rev = mii_read(gm, gm->phy_addr, MII_ID1) & 0x000f;	if (rev == 0 || rev == 3) {		/* Some revisions of 5401 appear to need this		 * initialisation sequence to disable, according		 * to OF, "tap power management"		 * 		 * WARNING ! OF and Darwin don't agree on the		 * register addresses. OF seem to interpret the		 * register numbers below as decimal		 */		mii_write(gm, gm->phy_addr, 0x18, 0x0c20);		mii_write(gm, gm->phy_addr, 0x17, 0x0012);		mii_write(gm, gm->phy_addr, 0x15, 0x1804);		mii_write(gm, gm->phy_addr, 0x17, 0x0013);		mii_write(gm, gm->phy_addr, 0x15, 0x1204);		mii_write(gm, gm->phy_addr, 0x17, 0x8006);		mii_write(gm, gm->phy_addr, 0x15, 0x0132);		mii_write(gm, gm->phy_addr, 0x17, 0x8006);		mii_write(gm, gm->phy_addr, 0x15, 0x0232);		mii_write(gm, gm->phy_addr, 0x17, 0x201f);		mii_write(gm, gm->phy_addr, 0x15, 0x0a20);	}		/* Configure for gigabit full duplex */	data = mii_read(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL);	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;	mii_write(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL, data);	mdelay(10);	/* Reset and configure cascaded 10/100 PHY */	mii_do_reset_phy(gm, 0x1f);		data = mii_read(gm, 0x1f, MII_BCM5201_MULTIPHY);	data |= MII_BCM5201_MULTIPHY_SERIALMODE;	mii_write(gm, 0x1f, MII_BCM5201_MULTIPHY, data);}static voidmii_init_BCM5411(struct gmac *gm){	int data;	/* Here's some more Apple black magic to setup	 * some voltage stuffs.	 */	mii_write(gm, gm->phy_addr, 0x1c, 0x8c23);	mii_write(gm, gm->phy_addr, 0x1c, 0x8ca3);	mii_write(gm, gm->phy_addr, 0x1c, 0x8c23);	/* Here, Apple seems to want to reset it, do	 * it as well	 */	mii_write(gm, gm->phy_addr, MII_CR, MII_CR_RST);	/* Start autoneg */	mii_write(gm, gm->phy_addr, MII_CR,			MII_CR_ASSE|MII_CR_FDM|	/* Autospeed, full duplex */

⌨️ 快捷键说明

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