brintos

brintos / linux-shallow public Read only

0
0
Text · 29.9 KiB · 55aa8d0 Raw
1202 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PHY drivers for the sungem ethernet driver.4 *5 * This file could be shared with other drivers.6 *7 * (c) 2002-2007, Benjamin Herrenscmidt (benh@kernel.crashing.org)8 *9 * TODO:10 *  - Add support for PHYs that provide an IRQ line11 *  - Eventually moved the entire polling state machine in12 *    there (out of the eth driver), so that it can easily be13 *    skipped on PHYs that implement it in hardware.14 *  - On LXT971 & BCM5201, Apple uses some chip specific regs15 *    to read the link status. Figure out why and if it makes16 *    sense to do the same (magic aneg ?)17 *  - Apple has some additional power management code for some18 *    Broadcom PHYs that they "hide" from the OpenSource version19 *    of darwin, still need to reverse engineer that20 */21 22 23#include <linux/module.h>24 25#include <linux/kernel.h>26#include <linux/types.h>27#include <linux/netdevice.h>28#include <linux/etherdevice.h>29#include <linux/mii.h>30#include <linux/ethtool.h>31#include <linux/delay.h>32#include <linux/of.h>33#include <linux/sungem_phy.h>34 35/* Link modes of the BCM5400 PHY */36static const int phy_BCM5400_link_table[8][3] = {37	{ 0, 0, 0 },	/* No link */38	{ 0, 0, 0 },	/* 10BT Half Duplex */39	{ 1, 0, 0 },	/* 10BT Full Duplex */40	{ 0, 1, 0 },	/* 100BT Half Duplex */41	{ 0, 1, 0 },	/* 100BT Half Duplex */42	{ 1, 1, 0 },	/* 100BT Full Duplex*/43	{ 1, 0, 1 },	/* 1000BT */44	{ 1, 0, 1 },	/* 1000BT */45};46 47static inline int __sungem_phy_read(struct mii_phy* phy, int id, int reg)48{49	return phy->mdio_read(phy->dev, id, reg);50}51 52static inline void __sungem_phy_write(struct mii_phy* phy, int id, int reg, int val)53{54	phy->mdio_write(phy->dev, id, reg, val);55}56 57static inline int sungem_phy_read(struct mii_phy* phy, int reg)58{59	return phy->mdio_read(phy->dev, phy->mii_id, reg);60}61 62static inline void sungem_phy_write(struct mii_phy* phy, int reg, int val)63{64	phy->mdio_write(phy->dev, phy->mii_id, reg, val);65}66 67static int reset_one_mii_phy(struct mii_phy* phy, int phy_id)68{69	u16 val;70	int limit = 10000;71 72	val = __sungem_phy_read(phy, phy_id, MII_BMCR);73	val &= ~(BMCR_ISOLATE | BMCR_PDOWN);74	val |= BMCR_RESET;75	__sungem_phy_write(phy, phy_id, MII_BMCR, val);76 77	udelay(100);78 79	while (--limit) {80		val = __sungem_phy_read(phy, phy_id, MII_BMCR);81		if ((val & BMCR_RESET) == 0)82			break;83		udelay(10);84	}85	if ((val & BMCR_ISOLATE) && limit > 0)86		__sungem_phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE);87 88	return limit <= 0;89}90 91static int bcm5201_init(struct mii_phy* phy)92{93	u16 data;94 95	data = sungem_phy_read(phy, MII_BCM5201_MULTIPHY);96	data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;97	sungem_phy_write(phy, MII_BCM5201_MULTIPHY, data);98 99	sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0);100 101	return 0;102}103 104static int bcm5201_suspend(struct mii_phy* phy)105{106	sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0);107	sungem_phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);108 109	return 0;110}111 112static int bcm5221_init(struct mii_phy* phy)113{114	u16 data;115 116	data = sungem_phy_read(phy, MII_BCM5221_TEST);117	sungem_phy_write(phy, MII_BCM5221_TEST,118		data | MII_BCM5221_TEST_ENABLE_SHADOWS);119 120	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);121	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,122		data | MII_BCM5221_SHDOW_AUX_STAT2_APD);123 124	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);125	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,126		data | MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR);127 128	data = sungem_phy_read(phy, MII_BCM5221_TEST);129	sungem_phy_write(phy, MII_BCM5221_TEST,130		data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);131 132	return 0;133}134 135static int bcm5221_suspend(struct mii_phy* phy)136{137	u16 data;138 139	data = sungem_phy_read(phy, MII_BCM5221_TEST);140	sungem_phy_write(phy, MII_BCM5221_TEST,141		data | MII_BCM5221_TEST_ENABLE_SHADOWS);142 143	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);144	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,145		  data | MII_BCM5221_SHDOW_AUX_MODE4_IDDQMODE);146 147	return 0;148}149 150static int bcm5241_init(struct mii_phy* phy)151{152	u16 data;153 154	data = sungem_phy_read(phy, MII_BCM5221_TEST);155	sungem_phy_write(phy, MII_BCM5221_TEST,156		data | MII_BCM5221_TEST_ENABLE_SHADOWS);157 158	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);159	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,160		data | MII_BCM5221_SHDOW_AUX_STAT2_APD);161 162	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);163	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,164		data & ~MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR);165 166	data = sungem_phy_read(phy, MII_BCM5221_TEST);167	sungem_phy_write(phy, MII_BCM5221_TEST,168		data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);169 170	return 0;171}172 173static int bcm5241_suspend(struct mii_phy* phy)174{175	u16 data;176 177	data = sungem_phy_read(phy, MII_BCM5221_TEST);178	sungem_phy_write(phy, MII_BCM5221_TEST,179		data | MII_BCM5221_TEST_ENABLE_SHADOWS);180 181	data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);182	sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,183		  data | MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR);184 185	return 0;186}187 188static int bcm5400_init(struct mii_phy* phy)189{190	u16 data;191 192	/* Configure for gigabit full duplex */193	data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL);194	data |= MII_BCM5400_AUXCONTROL_PWR10BASET;195	sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data);196 197	data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);198	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;199	sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);200 201	udelay(100);202 203	/* Reset and configure cascaded 10/100 PHY */204	(void)reset_one_mii_phy(phy, 0x1f);205 206	data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);207	data |= MII_BCM5201_MULTIPHY_SERIALMODE;208	__sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);209 210	data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL);211	data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;212	sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data);213 214	return 0;215}216 217static int bcm5400_suspend(struct mii_phy* phy)218{219#if 0 /* Commented out in Darwin... someone has those dawn docs ? */220	sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);221#endif222	return 0;223}224 225static int bcm5401_init(struct mii_phy* phy)226{227	u16 data;228	int rev;229 230	rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f;231	if (rev == 0 || rev == 3) {232		/* Some revisions of 5401 appear to need this233		 * initialisation sequence to disable, according234		 * to OF, "tap power management"235		 *236		 * WARNING ! OF and Darwin don't agree on the237		 * register addresses. OF seem to interpret the238		 * register numbers below as decimal239		 *240		 * Note: This should (and does) match tg3_init_5401phy_dsp241		 *       in the tg3.c driver. -DaveM242		 */243		sungem_phy_write(phy, 0x18, 0x0c20);244		sungem_phy_write(phy, 0x17, 0x0012);245		sungem_phy_write(phy, 0x15, 0x1804);246		sungem_phy_write(phy, 0x17, 0x0013);247		sungem_phy_write(phy, 0x15, 0x1204);248		sungem_phy_write(phy, 0x17, 0x8006);249		sungem_phy_write(phy, 0x15, 0x0132);250		sungem_phy_write(phy, 0x17, 0x8006);251		sungem_phy_write(phy, 0x15, 0x0232);252		sungem_phy_write(phy, 0x17, 0x201f);253		sungem_phy_write(phy, 0x15, 0x0a20);254	}255 256	/* Configure for gigabit full duplex */257	data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);258	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;259	sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);260 261	udelay(10);262 263	/* Reset and configure cascaded 10/100 PHY */264	(void)reset_one_mii_phy(phy, 0x1f);265 266	data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);267	data |= MII_BCM5201_MULTIPHY_SERIALMODE;268	__sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);269 270	return 0;271}272 273static int bcm5401_suspend(struct mii_phy* phy)274{275#if 0 /* Commented out in Darwin... someone has those dawn docs ? */276	sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);277#endif278	return 0;279}280 281static int bcm5411_init(struct mii_phy* phy)282{283	u16 data;284 285	/* Here's some more Apple black magic to setup286	 * some voltage stuffs.287	 */288	sungem_phy_write(phy, 0x1c, 0x8c23);289	sungem_phy_write(phy, 0x1c, 0x8ca3);290	sungem_phy_write(phy, 0x1c, 0x8c23);291 292	/* Here, Apple seems to want to reset it, do293	 * it as well294	 */295	sungem_phy_write(phy, MII_BMCR, BMCR_RESET);296	sungem_phy_write(phy, MII_BMCR, 0x1340);297 298	data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);299	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;300	sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);301 302	udelay(10);303 304	/* Reset and configure cascaded 10/100 PHY */305	(void)reset_one_mii_phy(phy, 0x1f);306 307	return 0;308}309 310static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)311{312	u16 ctl, adv;313 314	phy->autoneg = 1;315	phy->speed = SPEED_10;316	phy->duplex = DUPLEX_HALF;317	phy->pause = 0;318	phy->advertising = advertise;319 320	/* Setup standard advertise */321	adv = sungem_phy_read(phy, MII_ADVERTISE);322	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);323	if (advertise & ADVERTISED_10baseT_Half)324		adv |= ADVERTISE_10HALF;325	if (advertise & ADVERTISED_10baseT_Full)326		adv |= ADVERTISE_10FULL;327	if (advertise & ADVERTISED_100baseT_Half)328		adv |= ADVERTISE_100HALF;329	if (advertise & ADVERTISED_100baseT_Full)330		adv |= ADVERTISE_100FULL;331	sungem_phy_write(phy, MII_ADVERTISE, adv);332 333	/* Start/Restart aneg */334	ctl = sungem_phy_read(phy, MII_BMCR);335	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);336	sungem_phy_write(phy, MII_BMCR, ctl);337 338	return 0;339}340 341static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)342{343	u16 ctl;344 345	phy->autoneg = 0;346	phy->speed = speed;347	phy->duplex = fd;348	phy->pause = 0;349 350	ctl = sungem_phy_read(phy, MII_BMCR);351	ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE);352 353	/* First reset the PHY */354	sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET);355 356	/* Select speed & duplex */357	switch(speed) {358	case SPEED_10:359		break;360	case SPEED_100:361		ctl |= BMCR_SPEED100;362		break;363	case SPEED_1000:364	default:365		return -EINVAL;366	}367	if (fd == DUPLEX_FULL)368		ctl |= BMCR_FULLDPLX;369	sungem_phy_write(phy, MII_BMCR, ctl);370 371	return 0;372}373 374static int genmii_poll_link(struct mii_phy *phy)375{376	u16 status;377 378	(void)sungem_phy_read(phy, MII_BMSR);379	status = sungem_phy_read(phy, MII_BMSR);380	if ((status & BMSR_LSTATUS) == 0)381		return 0;382	if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE))383		return 0;384	return 1;385}386 387static int genmii_read_link(struct mii_phy *phy)388{389	u16 lpa;390 391	if (phy->autoneg) {392		lpa = sungem_phy_read(phy, MII_LPA);393 394		if (lpa & (LPA_10FULL | LPA_100FULL))395			phy->duplex = DUPLEX_FULL;396		else397			phy->duplex = DUPLEX_HALF;398		if (lpa & (LPA_100FULL | LPA_100HALF))399			phy->speed = SPEED_100;400		else401			phy->speed = SPEED_10;402		phy->pause = 0;403	}404	/* On non-aneg, we assume what we put in BMCR is the speed,405	 * though magic-aneg shouldn't prevent this case from occurring406	 */407 408	return 0;409}410 411static int generic_suspend(struct mii_phy* phy)412{413	sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);414 415	return 0;416}417 418static int bcm5421_init(struct mii_phy* phy)419{420	u16 data;421	unsigned int id;422 423	id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2));424 425	/* Revision 0 of 5421 needs some fixups */426	if (id == 0x002060e0) {427		/* This is borrowed from MacOS428		 */429		sungem_phy_write(phy, 0x18, 0x1007);430		data = sungem_phy_read(phy, 0x18);431		sungem_phy_write(phy, 0x18, data | 0x0400);432		sungem_phy_write(phy, 0x18, 0x0007);433		data = sungem_phy_read(phy, 0x18);434		sungem_phy_write(phy, 0x18, data | 0x0800);435		sungem_phy_write(phy, 0x17, 0x000a);436		data = sungem_phy_read(phy, 0x15);437		sungem_phy_write(phy, 0x15, data | 0x0200);438	}439 440	/* Pick up some init code from OF for K2 version */441	if ((id & 0xfffffff0) == 0x002062e0) {442		sungem_phy_write(phy, 4, 0x01e1);443		sungem_phy_write(phy, 9, 0x0300);444	}445 446	/* Check if we can enable automatic low power */447#ifdef CONFIG_PPC_PMAC448	if (phy->platform_data) {449		struct device_node *np = of_get_parent(phy->platform_data);450		int can_low_power = 1;451		if (np == NULL || of_get_property(np, "no-autolowpower", NULL))452			can_low_power = 0;453		of_node_put(np);454		if (can_low_power) {455			/* Enable automatic low-power */456			sungem_phy_write(phy, 0x1c, 0x9002);457			sungem_phy_write(phy, 0x1c, 0xa821);458			sungem_phy_write(phy, 0x1c, 0x941d);459		}460	}461#endif /* CONFIG_PPC_PMAC */462 463	return 0;464}465 466static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise)467{468	u16 ctl, adv;469 470	phy->autoneg = 1;471	phy->speed = SPEED_10;472	phy->duplex = DUPLEX_HALF;473	phy->pause = 0;474	phy->advertising = advertise;475 476	/* Setup standard advertise */477	adv = sungem_phy_read(phy, MII_ADVERTISE);478	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);479	if (advertise & ADVERTISED_10baseT_Half)480		adv |= ADVERTISE_10HALF;481	if (advertise & ADVERTISED_10baseT_Full)482		adv |= ADVERTISE_10FULL;483	if (advertise & ADVERTISED_100baseT_Half)484		adv |= ADVERTISE_100HALF;485	if (advertise & ADVERTISED_100baseT_Full)486		adv |= ADVERTISE_100FULL;487	if (advertise & ADVERTISED_Pause)488		adv |= ADVERTISE_PAUSE_CAP;489	if (advertise & ADVERTISED_Asym_Pause)490		adv |= ADVERTISE_PAUSE_ASYM;491	sungem_phy_write(phy, MII_ADVERTISE, adv);492 493	/* Setup 1000BT advertise */494	adv = sungem_phy_read(phy, MII_1000BASETCONTROL);495	adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP|MII_1000BASETCONTROL_HALFDUPLEXCAP);496	if (advertise & SUPPORTED_1000baseT_Half)497		adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;498	if (advertise & SUPPORTED_1000baseT_Full)499		adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;500	sungem_phy_write(phy, MII_1000BASETCONTROL, adv);501 502	/* Start/Restart aneg */503	ctl = sungem_phy_read(phy, MII_BMCR);504	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);505	sungem_phy_write(phy, MII_BMCR, ctl);506 507	return 0;508}509 510static int bcm54xx_setup_forced(struct mii_phy *phy, int speed, int fd)511{512	u16 ctl;513 514	phy->autoneg = 0;515	phy->speed = speed;516	phy->duplex = fd;517	phy->pause = 0;518 519	ctl = sungem_phy_read(phy, MII_BMCR);520	ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);521 522	/* First reset the PHY */523	sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET);524 525	/* Select speed & duplex */526	switch(speed) {527	case SPEED_10:528		break;529	case SPEED_100:530		ctl |= BMCR_SPEED100;531		break;532	case SPEED_1000:533		ctl |= BMCR_SPD2;534	}535	if (fd == DUPLEX_FULL)536		ctl |= BMCR_FULLDPLX;537 538	// XXX Should we set the sungem to GII now on 1000BT ?539 540	sungem_phy_write(phy, MII_BMCR, ctl);541 542	return 0;543}544 545static int bcm54xx_read_link(struct mii_phy *phy)546{547	int link_mode;548	u16 val;549 550	if (phy->autoneg) {551	    	val = sungem_phy_read(phy, MII_BCM5400_AUXSTATUS);552		link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>553			     MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT);554		phy->duplex = phy_BCM5400_link_table[link_mode][0] ?555			DUPLEX_FULL : DUPLEX_HALF;556		phy->speed = phy_BCM5400_link_table[link_mode][2] ?557				SPEED_1000 :558				(phy_BCM5400_link_table[link_mode][1] ?559				 SPEED_100 : SPEED_10);560		val = sungem_phy_read(phy, MII_LPA);561		phy->pause = (phy->duplex == DUPLEX_FULL) &&562			((val & LPA_PAUSE) != 0);563	}564	/* On non-aneg, we assume what we put in BMCR is the speed,565	 * though magic-aneg shouldn't prevent this case from occurring566	 */567 568	return 0;569}570 571static int marvell88e1111_init(struct mii_phy* phy)572{573	u16 rev;574 575	/* magic init sequence for rev 0 */576	rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f;577	if (rev == 0) {578		sungem_phy_write(phy, 0x1d, 0x000a);579		sungem_phy_write(phy, 0x1e, 0x0821);580 581		sungem_phy_write(phy, 0x1d, 0x0006);582		sungem_phy_write(phy, 0x1e, 0x8600);583 584		sungem_phy_write(phy, 0x1d, 0x000b);585		sungem_phy_write(phy, 0x1e, 0x0100);586 587		sungem_phy_write(phy, 0x1d, 0x0004);588		sungem_phy_write(phy, 0x1e, 0x4850);589	}590	return 0;591}592 593#define BCM5421_MODE_MASK	(1 << 5)594 595static int bcm5421_poll_link(struct mii_phy* phy)596{597	u32 phy_reg;598	int mode;599 600	/* find out in what mode we are */601	sungem_phy_write(phy, MII_NCONFIG, 0x1000);602	phy_reg = sungem_phy_read(phy, MII_NCONFIG);603 604	mode = (phy_reg & BCM5421_MODE_MASK) >> 5;605 606	if ( mode == BCM54XX_COPPER)607		return genmii_poll_link(phy);608 609	/* try to find out whether we have a link */610	sungem_phy_write(phy, MII_NCONFIG, 0x2000);611	phy_reg = sungem_phy_read(phy, MII_NCONFIG);612 613	if (phy_reg & 0x0020)614		return 0;615	else616		return 1;617}618 619static int bcm5421_read_link(struct mii_phy* phy)620{621	u32 phy_reg;622	int mode;623 624	/* find out in what mode we are */625	sungem_phy_write(phy, MII_NCONFIG, 0x1000);626	phy_reg = sungem_phy_read(phy, MII_NCONFIG);627 628	mode = (phy_reg & BCM5421_MODE_MASK ) >> 5;629 630	if ( mode == BCM54XX_COPPER)631		return bcm54xx_read_link(phy);632 633	phy->speed = SPEED_1000;634 635	/* find out whether we are running half- or full duplex */636	sungem_phy_write(phy, MII_NCONFIG, 0x2000);637	phy_reg = sungem_phy_read(phy, MII_NCONFIG);638 639	if ( (phy_reg & 0x0080) >> 7)640		phy->duplex |=  DUPLEX_HALF;641	else642		phy->duplex |=  DUPLEX_FULL;643 644	return 0;645}646 647static int bcm5421_enable_fiber(struct mii_phy* phy, int autoneg)648{649	/* enable fiber mode */650	sungem_phy_write(phy, MII_NCONFIG, 0x9020);651	/* LEDs active in both modes, autosense prio = fiber */652	sungem_phy_write(phy, MII_NCONFIG, 0x945f);653 654	if (!autoneg) {655		/* switch off fibre autoneg */656		sungem_phy_write(phy, MII_NCONFIG, 0xfc01);657		sungem_phy_write(phy, 0x0b, 0x0004);658	}659 660	phy->autoneg = autoneg;661 662	return 0;663}664 665#define BCM5461_FIBER_LINK	(1 << 2)666#define BCM5461_MODE_MASK	(3 << 1)667 668static int bcm5461_poll_link(struct mii_phy* phy)669{670	u32 phy_reg;671	int mode;672 673	/* find out in what mode we are */674	sungem_phy_write(phy, MII_NCONFIG, 0x7c00);675	phy_reg = sungem_phy_read(phy, MII_NCONFIG);676 677	mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;678 679	if ( mode == BCM54XX_COPPER)680		return genmii_poll_link(phy);681 682	/* find out whether we have a link */683	sungem_phy_write(phy, MII_NCONFIG, 0x7000);684	phy_reg = sungem_phy_read(phy, MII_NCONFIG);685 686	if (phy_reg & BCM5461_FIBER_LINK)687		return 1;688	else689		return 0;690}691 692#define BCM5461_FIBER_DUPLEX	(1 << 3)693 694static int bcm5461_read_link(struct mii_phy* phy)695{696	u32 phy_reg;697	int mode;698 699	/* find out in what mode we are */700	sungem_phy_write(phy, MII_NCONFIG, 0x7c00);701	phy_reg = sungem_phy_read(phy, MII_NCONFIG);702 703	mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;704 705	if ( mode == BCM54XX_COPPER) {706		return bcm54xx_read_link(phy);707	}708 709	phy->speed = SPEED_1000;710 711	/* find out whether we are running half- or full duplex */712	sungem_phy_write(phy, MII_NCONFIG, 0x7000);713	phy_reg = sungem_phy_read(phy, MII_NCONFIG);714 715	if (phy_reg & BCM5461_FIBER_DUPLEX)716		phy->duplex |=  DUPLEX_FULL;717	else718		phy->duplex |=  DUPLEX_HALF;719 720	return 0;721}722 723static int bcm5461_enable_fiber(struct mii_phy* phy, int autoneg)724{725	/* select fiber mode, enable 1000 base-X registers */726	sungem_phy_write(phy, MII_NCONFIG, 0xfc0b);727 728	if (autoneg) {729		/* enable fiber with no autonegotiation */730		sungem_phy_write(phy, MII_ADVERTISE, 0x01e0);731		sungem_phy_write(phy, MII_BMCR, 0x1140);732	} else {733		/* enable fiber with autonegotiation */734		sungem_phy_write(phy, MII_BMCR, 0x0140);735	}736 737	phy->autoneg = autoneg;738 739	return 0;740}741 742static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise)743{744	u16 ctl, adv;745 746	phy->autoneg = 1;747	phy->speed = SPEED_10;748	phy->duplex = DUPLEX_HALF;749	phy->pause = 0;750	phy->advertising = advertise;751 752	/* Setup standard advertise */753	adv = sungem_phy_read(phy, MII_ADVERTISE);754	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);755	if (advertise & ADVERTISED_10baseT_Half)756		adv |= ADVERTISE_10HALF;757	if (advertise & ADVERTISED_10baseT_Full)758		adv |= ADVERTISE_10FULL;759	if (advertise & ADVERTISED_100baseT_Half)760		adv |= ADVERTISE_100HALF;761	if (advertise & ADVERTISED_100baseT_Full)762		adv |= ADVERTISE_100FULL;763	if (advertise & ADVERTISED_Pause)764		adv |= ADVERTISE_PAUSE_CAP;765	if (advertise & ADVERTISED_Asym_Pause)766		adv |= ADVERTISE_PAUSE_ASYM;767	sungem_phy_write(phy, MII_ADVERTISE, adv);768 769	/* Setup 1000BT advertise & enable crossover detect770	 * XXX How do we advertise 1000BT ? Darwin source is771	 * confusing here, they read from specific control and772	 * write to control... Someone has specs for those773	 * beasts ?774	 */775	adv = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);776	adv |= MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX;777	adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |778			MII_1000BASETCONTROL_HALFDUPLEXCAP);779	if (advertise & SUPPORTED_1000baseT_Half)780		adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;781	if (advertise & SUPPORTED_1000baseT_Full)782		adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;783	sungem_phy_write(phy, MII_1000BASETCONTROL, adv);784 785	/* Start/Restart aneg */786	ctl = sungem_phy_read(phy, MII_BMCR);787	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);788	sungem_phy_write(phy, MII_BMCR, ctl);789 790	return 0;791}792 793static int marvell_setup_forced(struct mii_phy *phy, int speed, int fd)794{795	u16 ctl, ctl2;796 797	phy->autoneg = 0;798	phy->speed = speed;799	phy->duplex = fd;800	phy->pause = 0;801 802	ctl = sungem_phy_read(phy, MII_BMCR);803	ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);804	ctl |= BMCR_RESET;805 806	/* Select speed & duplex */807	switch(speed) {808	case SPEED_10:809		break;810	case SPEED_100:811		ctl |= BMCR_SPEED100;812		break;813	/* I'm not sure about the one below, again, Darwin source is814	 * quite confusing and I lack chip specs815	 */816	case SPEED_1000:817		ctl |= BMCR_SPD2;818	}819	if (fd == DUPLEX_FULL)820		ctl |= BMCR_FULLDPLX;821 822	/* Disable crossover. Again, the way Apple does it is strange,823	 * though I don't assume they are wrong ;)824	 */825	ctl2 = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);826	ctl2 &= ~(MII_M1011_PHY_SPEC_CONTROL_MANUAL_MDIX |827		MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX |828		MII_1000BASETCONTROL_FULLDUPLEXCAP |829		MII_1000BASETCONTROL_HALFDUPLEXCAP);830	if (speed == SPEED_1000)831		ctl2 |= (fd == DUPLEX_FULL) ?832			MII_1000BASETCONTROL_FULLDUPLEXCAP :833			MII_1000BASETCONTROL_HALFDUPLEXCAP;834	sungem_phy_write(phy, MII_1000BASETCONTROL, ctl2);835 836	// XXX Should we set the sungem to GII now on 1000BT ?837 838	sungem_phy_write(phy, MII_BMCR, ctl);839 840	return 0;841}842 843static int marvell_read_link(struct mii_phy *phy)844{845	u16 status, pmask;846 847	if (phy->autoneg) {848		status = sungem_phy_read(phy, MII_M1011_PHY_SPEC_STATUS);849		if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)850			return -EAGAIN;851		if (status & MII_M1011_PHY_SPEC_STATUS_1000)852			phy->speed = SPEED_1000;853		else if (status & MII_M1011_PHY_SPEC_STATUS_100)854			phy->speed = SPEED_100;855		else856			phy->speed = SPEED_10;857		if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)858			phy->duplex = DUPLEX_FULL;859		else860			phy->duplex = DUPLEX_HALF;861		pmask = MII_M1011_PHY_SPEC_STATUS_TX_PAUSE |862			MII_M1011_PHY_SPEC_STATUS_RX_PAUSE;863		phy->pause = (status & pmask) == pmask;864	}865	/* On non-aneg, we assume what we put in BMCR is the speed,866	 * though magic-aneg shouldn't prevent this case from occurring867	 */868 869	return 0;870}871 872#define MII_BASIC_FEATURES \873	(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |	\874	 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |	\875	 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII |	\876	 SUPPORTED_Pause)877 878/* On gigabit capable PHYs, we advertise Pause support but not asym pause879 * support for now as I'm not sure it's supported and Darwin doesn't do880 * it neither. --BenH.881 */882#define MII_GBIT_FEATURES \883	(MII_BASIC_FEATURES |	\884	 SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)885 886/* Broadcom BCM 5201 */887static const struct mii_phy_ops bcm5201_phy_ops = {888	.init		= bcm5201_init,889	.suspend	= bcm5201_suspend,890	.setup_aneg	= genmii_setup_aneg,891	.setup_forced	= genmii_setup_forced,892	.poll_link	= genmii_poll_link,893	.read_link	= genmii_read_link,894};895 896static const struct mii_phy_def bcm5201_phy_def = {897	.phy_id		= 0x00406210,898	.phy_id_mask	= 0xfffffff0,899	.name		= "BCM5201",900	.features	= MII_BASIC_FEATURES,901	.magic_aneg	= 1,902	.ops		= &bcm5201_phy_ops903};904 905/* Broadcom BCM 5221 */906static const struct mii_phy_ops bcm5221_phy_ops = {907	.suspend	= bcm5221_suspend,908	.init		= bcm5221_init,909	.setup_aneg	= genmii_setup_aneg,910	.setup_forced	= genmii_setup_forced,911	.poll_link	= genmii_poll_link,912	.read_link	= genmii_read_link,913};914 915static const struct mii_phy_def bcm5221_phy_def = {916	.phy_id		= 0x004061e0,917	.phy_id_mask	= 0xfffffff0,918	.name		= "BCM5221",919	.features	= MII_BASIC_FEATURES,920	.magic_aneg	= 1,921	.ops		= &bcm5221_phy_ops922};923 924/* Broadcom BCM 5241 */925static const struct mii_phy_ops bcm5241_phy_ops = {926	.suspend	= bcm5241_suspend,927	.init		= bcm5241_init,928	.setup_aneg	= genmii_setup_aneg,929	.setup_forced	= genmii_setup_forced,930	.poll_link	= genmii_poll_link,931	.read_link	= genmii_read_link,932};933 934static const struct mii_phy_def bcm5241_phy_def = {935	.phy_id		= 0x0143bc30,936	.phy_id_mask	= 0xfffffff0,937	.name		= "BCM5241",938	.features	= MII_BASIC_FEATURES,939	.magic_aneg	= 1,940	.ops		= &bcm5241_phy_ops941};942 943/* Broadcom BCM 5400 */944static const struct mii_phy_ops bcm5400_phy_ops = {945	.init		= bcm5400_init,946	.suspend	= bcm5400_suspend,947	.setup_aneg	= bcm54xx_setup_aneg,948	.setup_forced	= bcm54xx_setup_forced,949	.poll_link	= genmii_poll_link,950	.read_link	= bcm54xx_read_link,951};952 953static const struct mii_phy_def bcm5400_phy_def = {954	.phy_id		= 0x00206040,955	.phy_id_mask	= 0xfffffff0,956	.name		= "BCM5400",957	.features	= MII_GBIT_FEATURES,958	.magic_aneg	= 1,959	.ops		= &bcm5400_phy_ops960};961 962/* Broadcom BCM 5401 */963static const struct mii_phy_ops bcm5401_phy_ops = {964	.init		= bcm5401_init,965	.suspend	= bcm5401_suspend,966	.setup_aneg	= bcm54xx_setup_aneg,967	.setup_forced	= bcm54xx_setup_forced,968	.poll_link	= genmii_poll_link,969	.read_link	= bcm54xx_read_link,970};971 972static const struct mii_phy_def bcm5401_phy_def = {973	.phy_id		= 0x00206050,974	.phy_id_mask	= 0xfffffff0,975	.name		= "BCM5401",976	.features	= MII_GBIT_FEATURES,977	.magic_aneg	= 1,978	.ops		= &bcm5401_phy_ops979};980 981/* Broadcom BCM 5411 */982static const struct mii_phy_ops bcm5411_phy_ops = {983	.init		= bcm5411_init,984	.suspend	= generic_suspend,985	.setup_aneg	= bcm54xx_setup_aneg,986	.setup_forced	= bcm54xx_setup_forced,987	.poll_link	= genmii_poll_link,988	.read_link	= bcm54xx_read_link,989};990 991static const struct mii_phy_def bcm5411_phy_def = {992	.phy_id		= 0x00206070,993	.phy_id_mask	= 0xfffffff0,994	.name		= "BCM5411",995	.features	= MII_GBIT_FEATURES,996	.magic_aneg	= 1,997	.ops		= &bcm5411_phy_ops998};999 1000/* Broadcom BCM 5421 */1001static const struct mii_phy_ops bcm5421_phy_ops = {1002	.init		= bcm5421_init,1003	.suspend	= generic_suspend,1004	.setup_aneg	= bcm54xx_setup_aneg,1005	.setup_forced	= bcm54xx_setup_forced,1006	.poll_link	= bcm5421_poll_link,1007	.read_link	= bcm5421_read_link,1008	.enable_fiber   = bcm5421_enable_fiber,1009};1010 1011static const struct mii_phy_def bcm5421_phy_def = {1012	.phy_id		= 0x002060e0,1013	.phy_id_mask	= 0xfffffff0,1014	.name		= "BCM5421",1015	.features	= MII_GBIT_FEATURES,1016	.magic_aneg	= 1,1017	.ops		= &bcm5421_phy_ops1018};1019 1020/* Broadcom BCM 5421 built-in K2 */1021static const struct mii_phy_ops bcm5421k2_phy_ops = {1022	.init		= bcm5421_init,1023	.suspend	= generic_suspend,1024	.setup_aneg	= bcm54xx_setup_aneg,1025	.setup_forced	= bcm54xx_setup_forced,1026	.poll_link	= genmii_poll_link,1027	.read_link	= bcm54xx_read_link,1028};1029 1030static const struct mii_phy_def bcm5421k2_phy_def = {1031	.phy_id		= 0x002062e0,1032	.phy_id_mask	= 0xfffffff0,1033	.name		= "BCM5421-K2",1034	.features	= MII_GBIT_FEATURES,1035	.magic_aneg	= 1,1036	.ops		= &bcm5421k2_phy_ops1037};1038 1039static const struct mii_phy_ops bcm5461_phy_ops = {1040	.init		= bcm5421_init,1041	.suspend	= generic_suspend,1042	.setup_aneg	= bcm54xx_setup_aneg,1043	.setup_forced	= bcm54xx_setup_forced,1044	.poll_link	= bcm5461_poll_link,1045	.read_link	= bcm5461_read_link,1046	.enable_fiber   = bcm5461_enable_fiber,1047};1048 1049static const struct mii_phy_def bcm5461_phy_def = {1050	.phy_id		= 0x002060c0,1051	.phy_id_mask	= 0xfffffff0,1052	.name		= "BCM5461",1053	.features	= MII_GBIT_FEATURES,1054	.magic_aneg	= 1,1055	.ops		= &bcm5461_phy_ops1056};1057 1058/* Broadcom BCM 5462 built-in Vesta */1059static const struct mii_phy_ops bcm5462V_phy_ops = {1060	.init		= bcm5421_init,1061	.suspend	= generic_suspend,1062	.setup_aneg	= bcm54xx_setup_aneg,1063	.setup_forced	= bcm54xx_setup_forced,1064	.poll_link	= genmii_poll_link,1065	.read_link	= bcm54xx_read_link,1066};1067 1068static const struct mii_phy_def bcm5462V_phy_def = {1069	.phy_id		= 0x002060d0,1070	.phy_id_mask	= 0xfffffff0,1071	.name		= "BCM5462-Vesta",1072	.features	= MII_GBIT_FEATURES,1073	.magic_aneg	= 1,1074	.ops		= &bcm5462V_phy_ops1075};1076 1077/* Marvell 88E1101 amd 88E1111 */1078static const struct mii_phy_ops marvell88e1101_phy_ops = {1079	.suspend	= generic_suspend,1080	.setup_aneg	= marvell_setup_aneg,1081	.setup_forced	= marvell_setup_forced,1082	.poll_link	= genmii_poll_link,1083	.read_link	= marvell_read_link1084};1085 1086static const struct mii_phy_ops marvell88e1111_phy_ops = {1087	.init		= marvell88e1111_init,1088	.suspend	= generic_suspend,1089	.setup_aneg	= marvell_setup_aneg,1090	.setup_forced	= marvell_setup_forced,1091	.poll_link	= genmii_poll_link,1092	.read_link	= marvell_read_link1093};1094 1095/* two revs in darwin for the 88e1101 ... I could use a datasheet1096 * to get the proper names...1097 */1098static const struct mii_phy_def marvell88e1101v1_phy_def = {1099	.phy_id		= 0x01410c20,1100	.phy_id_mask	= 0xfffffff0,1101	.name		= "Marvell 88E1101v1",1102	.features	= MII_GBIT_FEATURES,1103	.magic_aneg	= 1,1104	.ops		= &marvell88e1101_phy_ops1105};1106 1107static const struct mii_phy_def marvell88e1101v2_phy_def = {1108	.phy_id		= 0x01410c60,1109	.phy_id_mask	= 0xfffffff0,1110	.name		= "Marvell 88E1101v2",1111	.features	= MII_GBIT_FEATURES,1112	.magic_aneg	= 1,1113	.ops		= &marvell88e1101_phy_ops1114};1115 1116static const struct mii_phy_def marvell88e1111_phy_def = {1117	.phy_id		= 0x01410cc0,1118	.phy_id_mask	= 0xfffffff0,1119	.name		= "Marvell 88E1111",1120	.features	= MII_GBIT_FEATURES,1121	.magic_aneg	= 1,1122	.ops		= &marvell88e1111_phy_ops1123};1124 1125/* Generic implementation for most 10/100 PHYs */1126static const struct mii_phy_ops generic_phy_ops = {1127	.setup_aneg	= genmii_setup_aneg,1128	.setup_forced	= genmii_setup_forced,1129	.poll_link	= genmii_poll_link,1130	.read_link	= genmii_read_link1131};1132 1133static const struct mii_phy_def genmii_phy_def = {1134	.phy_id		= 0x00000000,1135	.phy_id_mask	= 0x00000000,1136	.name		= "Generic MII",1137	.features	= MII_BASIC_FEATURES,1138	.magic_aneg	= 0,1139	.ops		= &generic_phy_ops1140};1141 1142static const struct mii_phy_def *mii_phy_table[] = {1143	&bcm5201_phy_def,1144	&bcm5221_phy_def,1145	&bcm5241_phy_def,1146	&bcm5400_phy_def,1147	&bcm5401_phy_def,1148	&bcm5411_phy_def,1149	&bcm5421_phy_def,1150	&bcm5421k2_phy_def,1151	&bcm5461_phy_def,1152	&bcm5462V_phy_def,1153	&marvell88e1101v1_phy_def,1154	&marvell88e1101v2_phy_def,1155	&marvell88e1111_phy_def,1156	&genmii_phy_def,1157	NULL1158};1159 1160int sungem_phy_probe(struct mii_phy *phy, int mii_id)1161{1162	const struct mii_phy_def *def;1163	int rc;1164	u32 id;1165	int i;1166 1167	/* We do not reset the mii_phy structure as the driver1168	 * may re-probe the PHY regulary1169	 */1170	phy->mii_id = mii_id;1171 1172	/* Take PHY out of isloate mode and reset it. */1173	rc = reset_one_mii_phy(phy, mii_id);1174	if (rc)1175		goto fail;1176 1177	/* Read ID and find matching entry */1178	id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2));1179	printk(KERN_DEBUG KBUILD_MODNAME ": " "PHY ID: %x, addr: %x\n",1180	       id, mii_id);1181	for (i=0; (def = mii_phy_table[i]) != NULL; i++)1182		if ((id & def->phy_id_mask) == def->phy_id)1183			break;1184	/* Should never be NULL (we have a generic entry), but... */1185	if (def == NULL)1186		goto fail;1187 1188	phy->def = def;1189 1190	return 0;1191fail:1192	phy->speed = 0;1193	phy->duplex = 0;1194	phy->pause = 0;1195	phy->advertising = 0;1196	return -ENODEV;1197}1198 1199EXPORT_SYMBOL(sungem_phy_probe);1200MODULE_DESCRIPTION("PHY drivers for the sungem Ethernet MAC driver");1201MODULE_LICENSE("GPL");1202