sungem_phy.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 868 行 · 第 1/2 页

C
868
字号
/* * PHY drivers for the sungem ethernet driver. *  * This file could be shared with other drivers. *  * (c) 2002, Benjamin Herrenscmidt (benh@kernel.crashing.org) * * TODO: *  - Implement WOL *  - Add support for PHYs that provide an IRQ line *  - Eventually moved the entire polling state machine in *    there (out of the eth driver), so that it can easily be *    skipped on PHYs that implement it in hardware. *  - On LXT971 & BCM5201, Apple uses some chip specific regs *    to read the link status. Figure out why and if it makes *    sense to do the same (magic aneg ?) *  - Apple has some additional power management code for some *    Broadcom PHYs that they "hide" from the OpenSource version *    of darwin, still need to reverse engineer that */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <linux/delay.h>#include "sungem_phy.h"/* 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 inline int __phy_read(struct mii_phy* phy, int id, int reg){	return phy->mdio_read(phy->dev, id, reg);}static inline void __phy_write(struct mii_phy* phy, int id, int reg, int val){	phy->mdio_write(phy->dev, id, reg, val);}static inline int phy_read(struct mii_phy* phy, int reg){	return phy->mdio_read(phy->dev, phy->mii_id, reg);}static inline void phy_write(struct mii_phy* phy, int reg, int val){	phy->mdio_write(phy->dev, phy->mii_id, reg, val);}static int reset_one_mii_phy(struct mii_phy* phy, int phy_id){	u16 val;	int limit = 10000;		val = __phy_read(phy, phy_id, MII_BMCR);	val &= ~(BMCR_ISOLATE | BMCR_PDOWN);	val |= BMCR_RESET;	__phy_write(phy, phy_id, MII_BMCR, val);	udelay(100);	while (limit--) {		val = __phy_read(phy, phy_id, MII_BMCR);		if ((val & BMCR_RESET) == 0)			break;		udelay(10);	}	if ((val & BMCR_ISOLATE) && limit > 0)		__phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE);		return (limit <= 0);}static int bcm5201_init(struct mii_phy* phy){	u16 data;	data = phy_read(phy, MII_BCM5201_MULTIPHY);	data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;	phy_write(phy, MII_BCM5201_MULTIPHY, data);	return 0;}static int bcm5201_suspend(struct mii_phy* phy, int wol_options){	if (!wol_options)		phy_write(phy, MII_BCM5201_INTERRUPT, 0);	/* Here's a strange hack used by both MacOS 9 and X */	phy_write(phy, MII_LPA, phy_read(phy, MII_LPA));		if (!wol_options) {#if 0 /* Commented out in Darwin... someone has those dawn docs ? */		u16 val = phy_read(phy, MII_BCM5201_AUXMODE2)		phy_write(phy, MII_BCM5201_AUXMODE2,			  val & ~MII_BCM5201_AUXMODE2_LOWPOWER);#endif					phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);	}	return 0;}static int bcm5221_init(struct mii_phy* phy){	u16 data;	data = phy_read(phy, MII_BCM5221_TEST);	phy_write(phy, MII_BCM5221_TEST,		data | MII_BCM5221_TEST_ENABLE_SHADOWS);	data = phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);	phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,		data | MII_BCM5221_SHDOW_AUX_STAT2_APD);	data = phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);	phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,		data | MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR);	data = phy_read(phy, MII_BCM5221_TEST);	phy_write(phy, MII_BCM5221_TEST,		data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);	return 0;}static int bcm5400_init(struct mii_phy* phy){	u16 data;	/* Configure for gigabit full duplex */	data = phy_read(phy, MII_BCM5400_AUXCONTROL);	data |= MII_BCM5400_AUXCONTROL_PWR10BASET;	phy_write(phy, MII_BCM5400_AUXCONTROL, data);		data = phy_read(phy, MII_BCM5400_GB_CONTROL);	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;	phy_write(phy, MII_BCM5400_GB_CONTROL, data);		udelay(100);	/* Reset and configure cascaded 10/100 PHY */	(void)reset_one_mii_phy(phy, 0x1f);		data = __phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);	data |= MII_BCM5201_MULTIPHY_SERIALMODE;	__phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);	data = phy_read(phy, MII_BCM5400_AUXCONTROL);	data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;	phy_write(phy, MII_BCM5400_AUXCONTROL, data);	return 0;}static int bcm5400_suspend(struct mii_phy* phy, int wol_options){#if 0 /* Commented out in Darwin... someone has those dawn docs ? */	phy_write(phy, MII_BMCR, BMCR_PDOWN);#endif	return 0;}static int bcm5401_init(struct mii_phy* phy){	u16 data;	int rev;	rev = phy_read(phy, MII_PHYSID2) & 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		 *		 * Note: This should (and does) match tg3_init_5401phy_dsp		 *       in the tg3.c driver. -DaveM		 */		phy_write(phy, 0x18, 0x0c20);		phy_write(phy, 0x17, 0x0012);		phy_write(phy, 0x15, 0x1804);		phy_write(phy, 0x17, 0x0013);		phy_write(phy, 0x15, 0x1204);		phy_write(phy, 0x17, 0x8006);		phy_write(phy, 0x15, 0x0132);		phy_write(phy, 0x17, 0x8006);		phy_write(phy, 0x15, 0x0232);		phy_write(phy, 0x17, 0x201f);		phy_write(phy, 0x15, 0x0a20);	}		/* Configure for gigabit full duplex */	data = phy_read(phy, MII_BCM5400_GB_CONTROL);	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;	phy_write(phy, MII_BCM5400_GB_CONTROL, data);	udelay(10);	/* Reset and configure cascaded 10/100 PHY */	(void)reset_one_mii_phy(phy, 0x1f);		data = __phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);	data |= MII_BCM5201_MULTIPHY_SERIALMODE;	__phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);	return 0;}static int bcm5401_suspend(struct mii_phy* phy, int wol_options){#if 0 /* Commented out in Darwin... someone has those dawn docs ? */	phy_write(phy, MII_BMCR, BMCR_PDOWN);#endif	return 0;}static int bcm5411_init(struct mii_phy* phy){	u16 data;	/* Here's some more Apple black magic to setup	 * some voltage stuffs.	 */	phy_write(phy, 0x1c, 0x8c23);	phy_write(phy, 0x1c, 0x8ca3);	phy_write(phy, 0x1c, 0x8c23);	/* Here, Apple seems to want to reset it, do	 * it as well	 */	phy_write(phy, MII_BMCR, BMCR_RESET);	phy_write(phy, MII_BMCR, 0x1340);	data = phy_read(phy, MII_BCM5400_GB_CONTROL);	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;	phy_write(phy, MII_BCM5400_GB_CONTROL, data);	udelay(10);	/* Reset and configure cascaded 10/100 PHY */	(void)reset_one_mii_phy(phy, 0x1f);		return 0;}static int bcm5411_suspend(struct mii_phy* phy, int wol_options){	phy_write(phy, MII_BMCR, BMCR_PDOWN);	return 0;}static int bcm5421_init(struct mii_phy* phy){	u16 data;	int rev;	rev = phy_read(phy, MII_PHYSID2) & 0x000f;	if (rev == 0) {		/* This is borrowed from MacOS		 */		phy_write(phy, 0x18, 0x1007);		data = phy_read(phy, 0x18);		phy_write(phy, 0x18, data | 0x0400);		phy_write(phy, 0x18, 0x0007);		data = phy_read(phy, 0x18);		phy_write(phy, 0x18, data | 0x0800);		phy_write(phy, 0x17, 0x000a);		data = phy_read(phy, 0x15);		phy_write(phy, 0x15, data | 0x0200);	}#if 0	/* This has to be verified before I enable it */	/* Enable automatic low-power */	phy_write(phy, 0x1c, 0x9002);	phy_write(phy, 0x1c, 0xa821);	phy_write(phy, 0x1c, 0x941d);#endif	return 0;}static int bcm5421k2_init(struct mii_phy* phy){	/* Init code borrowed from OF */	phy_write(phy, 4, 0x01e1);	phy_write(phy, 9, 0x0300);	return 0;}static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise){	u16 ctl, adv;		phy->autoneg = 1;	phy->speed = SPEED_10;	phy->duplex = DUPLEX_HALF;	phy->pause = 0;	phy->advertising = advertise;	/* Setup standard advertise */	adv = phy_read(phy, MII_ADVERTISE);	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);	if (advertise & ADVERTISED_10baseT_Half)		adv |= ADVERTISE_10HALF;	if (advertise & ADVERTISED_10baseT_Full)		adv |= ADVERTISE_10FULL;	if (advertise & ADVERTISED_100baseT_Half)		adv |= ADVERTISE_100HALF;	if (advertise & ADVERTISED_100baseT_Full)		adv |= ADVERTISE_100FULL;	phy_write(phy, MII_ADVERTISE, adv);	/* Setup 1000BT advertise */	adv = phy_read(phy, MII_1000BASETCONTROL);	adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP|MII_1000BASETCONTROL_HALFDUPLEXCAP);	if (advertise & SUPPORTED_1000baseT_Half)		adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;	if (advertise & SUPPORTED_1000baseT_Full)		adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;	phy_write(phy, MII_1000BASETCONTROL, adv);	/* Start/Restart aneg */	ctl = phy_read(phy, MII_BMCR);	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);	phy_write(phy, MII_BMCR, ctl);	return 0;}static int bcm54xx_setup_forced(struct mii_phy *phy, int speed, int fd){	u16 ctl;		phy->autoneg = 0;	phy->speed = speed;	phy->duplex = fd;	phy->pause = 0;	ctl = phy_read(phy, MII_BMCR);	ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);	/* First reset the PHY */	phy_write(phy, MII_BMCR, ctl | BMCR_RESET);	/* Select speed & duplex */	switch(speed) {	case SPEED_10:		break;	case SPEED_100:		ctl |= BMCR_SPEED100;		break;	case SPEED_1000:		ctl |= BMCR_SPD2;	}	if (fd == DUPLEX_FULL)		ctl |= BMCR_FULLDPLX;	// XXX Should we set the sungem to GII now on 1000BT ?		phy_write(phy, MII_BMCR, ctl);	return 0;}static int bcm54xx_read_link(struct mii_phy *phy){	int link_mode;		u16 val;		if (phy->autoneg) {	    	val = phy_read(phy, MII_BCM5400_AUXSTATUS);		link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>			     MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT);		phy->duplex = phy_BCM5400_link_table[link_mode][0] ? DUPLEX_FULL : DUPLEX_HALF;		phy->speed = phy_BCM5400_link_table[link_mode][2] ?				SPEED_1000 :				(phy_BCM5400_link_table[link_mode][1] ? SPEED_100 : SPEED_10);		val = phy_read(phy, MII_LPA);		phy->pause = ((val & LPA_PAUSE) != 0);	}	/* On non-aneg, we assume what we put in BMCR is the speed,	 * though magic-aneg shouldn't prevent this case from occurring	 */	return 0;}static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise){	u16 ctl, adv;		phy->autoneg = 1;	phy->speed = SPEED_10;	phy->duplex = DUPLEX_HALF;	phy->pause = 0;	phy->advertising = advertise;	/* Setup standard advertise */	adv = phy_read(phy, MII_ADVERTISE);	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);	if (advertise & ADVERTISED_10baseT_Half)		adv |= ADVERTISE_10HALF;	if (advertise & ADVERTISED_10baseT_Full)		adv |= ADVERTISE_10FULL;	if (advertise & ADVERTISED_100baseT_Half)		adv |= ADVERTISE_100HALF;	if (advertise & ADVERTISED_100baseT_Full)		adv |= ADVERTISE_100FULL;	phy_write(phy, MII_ADVERTISE, adv);

⌨️ 快捷键说明

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