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

📄 gmac.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 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 <bh40@calva.net> - 03/09/2000 * - Add support for new PHYs * - Add some PowerBook 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/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.3, kernel 2.4.x */#define GMAC_VERSION	"v1.3k4"static unsigned char dummy_buf[RX_BUF_ALLOC_SIZE + RX_OFFSET + GMAC_BUFFER_ALIGN];static struct net_device *gmacs = NULL;/* 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 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("Link state change, phy_status: 0x%04x\n", phy_status);#endif		gm->phy_status = phy_status;		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;		    if (gm->phy_type == PHY_B5201) {		    	int aux_stat = mii_read(gm, gm->phy_addr, MII_BCM5201_AUXCTLSTATUS);#ifdef DEBUG_PHY			printk("    Link up ! BCM5201 aux_stat: 0x%04x\n", aux_stat);#endif		    	full_duplex = ((aux_stat & MII_BCM5201_AUXCTLSTATUS_DUPLEX) != 0);		    	link_100 = ((aux_stat & MII_BCM5201_AUXCTLSTATUS_SPEED) != 0);		    } else if (gm->phy_type == PHY_B5400) {		    	int aux_stat = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXSTATUS);		    	int link = (aux_stat & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>		    			MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT;#ifdef DEBUG_PHY			printk("    Link up ! BCM5400 aux_stat: 0x%04x (link mode: %d)\n",				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];		    } else if (gm->phy_type == PHY_LXT971) {		    	int stat2 = mii_read(gm, gm->phy_addr, MII_LXT971_STATUS2);#ifdef DEBUG_PHY			printk("    Link up ! LXT971 stat2: 0x%04x\n", stat2);#endif		    	full_duplex = ((stat2 & MII_LXT971_STATUS2_FULLDUPLEX) != 0);		    	link_100 = ((stat2 & MII_LXT971_STATUS2_SPEED) != 0);		    }#ifdef DEBUG_PHY		    printk("    full_duplex: %d, speed: %s\n", 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("    Link down !\n");#endif		}	}}static 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;}static voidmii_init_BCM5400(struct gmac *gm){	int 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);		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);	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 intmii_lookup_and_reset(struct gmac *gm){	int	i, mii_status, mii_control;	gm->phy_addr = -1;	gm->phy_type = PHY_UNKNOWN;	/* Hard reset the PHY */	feature_set_gmac_phy_reset(gm->of_node, KL_GPIO_ETH_PHY_RESET_ASSERT);	mdelay(10);	feature_set_gmac_phy_reset(gm->of_node, KL_GPIO_ETH_PHY_RESET_RELEASE);	mdelay(10);		/* Find the PHY */	for(i=0; i<=31; i++) {		mii_control = mii_read(gm, i, MII_CR);		mii_status = mii_read(gm, i, MII_SR);		if (mii_control != -1  && mii_status != -1 &&			(mii_control != 0xffff || mii_status != 0xffff))			break;	}	gm->phy_addr = i;	if (gm->phy_addr > 31)		return 0;	/* Reset it */	if (mii_do_reset_phy(gm, gm->phy_addr))		goto fail;		/* Read the PHY ID */	gm->phy_id = (mii_read(gm, gm->phy_addr, MII_ID0) << 16) |		mii_read(gm, gm->phy_addr, MII_ID1);#ifdef DEBUG_PHY	printk("%s PHY ID: 0x%08x\n", gm->dev->name, gm->phy_id);#endif	if ((gm->phy_id & MII_BCM5400_MASK) == MII_BCM5400_ID) {		gm->phy_type = PHY_B5400;		printk(KERN_ERR "%s Found Broadcom BCM5400 PHY (Gigabit)\n",			gm->dev->name);		mii_init_BCM5400(gm);			} else if ((gm->phy_id & MII_BCM5201_MASK) == MII_BCM5201_ID) {		gm->phy_type = PHY_B5201;		printk(KERN_INFO "%s Found Broadcom BCM5201 PHY\n", gm->dev->name);	} else if ((gm->phy_id & MII_LXT971_MASK) == MII_LXT971_ID) {		gm->phy_type = PHY_LXT971;		printk(KERN_INFO "%s Found LevelOne LX971 PHY\n", gm->dev->name);	} else {		printk(KERN_ERR "%s: Warning ! Unknown PHY ID 0x%08x !\n",			gm->dev->name, gm->phy_id);	}	return 1;	fail:	gm->phy_addr = -1;	return 0;}/*  * Setup the PHY autonegociation parameters *  * Code to force the PHY duplex mode and speed should be * added here */static voidmii_setup_phy(struct gmac *gm){	int data;		/* Stop auto-negociation */	data = mii_read(gm, gm->phy_addr, MII_CR);	mii_write(gm, gm->phy_addr, MII_CR, data & ~MII_CR_ASSE);	/* Set advertisement to 10/100 and Half/Full duplex	 * (full capabilities) */	data = mii_read(gm, gm->phy_addr, MII_ANA);	data |= MII_ANA_TXAM | MII_ANA_FDAM | MII_ANA_10M;	mii_write(gm, gm->phy_addr, MII_ANA, data);		/* Restart auto-negociation */	data = mii_read(gm, gm->phy_addr, MII_CR);	data |= MII_CR_ASSE;	mii_write(gm, gm->phy_addr, MII_CR, data);	data |= MII_CR_RAN;	mii_write(gm, gm->phy_addr, MII_CR, data);}/*  * Turn On/Off the gmac cell inside Uni-N *  * ToDo: Add code to support powering down of the PHY. */static voidgmac_set_power(struct gmac *gm, int power_up){	if (power_up) {		feature_set_gmac_power(gm->of_node, 1);		if (gm->pci_devfn != 0xff) {			u16 cmd;						/*			 * Make sure PCI is correctly configured			 *			 * We use old pci_bios versions of the function since, by			 * default, gmac is not powered up, and so will be absent			 * from the kernel initial PCI lookup. 			 * 			 * Should be replaced by 2.4 new PCI mecanisms and really			 * regiser the device.			 */			pcibios_read_config_word(gm->pci_bus, gm->pci_devfn,				PCI_COMMAND, &cmd);			cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;	    		pcibios_write_config_word(gm->pci_bus, gm->pci_devfn,	    			PCI_COMMAND, cmd);	    		pcibios_write_config_byte(gm->pci_bus, gm->pci_devfn,	    			PCI_LATENCY_TIMER, 16);	    		pcibios_write_config_byte(gm->pci_bus, gm->pci_devfn,	    			PCI_CACHE_LINE_SIZE, 8);		}	} else {		/* FIXME: Add PHY power down */		gm->phy_type = 0;		feature_set_gmac_power(gm->of_node, 0);	}}/* * Makes sure the GMAC cell is powered up, and reset it */static intgmac_powerup_and_reset(struct net_device *dev){	struct gmac *gm = (struct gmac *) dev->priv;	int timeout;		/* turn on GB clock */	gmac_set_power(gm, 1);	/* Perform a software reset */	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) {			/* Mask out all chips interrupts */			GM_OUT(GM_IRQ_MASK, 0xffffffff);			return 0;		}	}	printk(KERN_ERR "%s reset failed!\n", dev->name);	gmac_set_power(gm, 0);

⌨️ 快捷键说明

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