xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/ibm/emac/phy.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * drivers/net/ethernet/ibm/emac/phy.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Driver for PowerPC 4xx on-chip ethernet controller, PHY support.
6*4882a593Smuzhiyun  * Borrowed from sungem_phy.c, though I only kept the generic MII
7*4882a593Smuzhiyun  * driver for now.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * This file should be shared with other drivers or eventually
10*4882a593Smuzhiyun  * merged as the "low level" part of miilib
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
13*4882a593Smuzhiyun  *                <benh@kernel.crashing.org>
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  * Based on the arch/ppc version of the driver:
16*4882a593Smuzhiyun  *
17*4882a593Smuzhiyun  * (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org)
18*4882a593Smuzhiyun  * (c) 2004-2005, Eugene Surovegin <ebs@ebshome.net>
19*4882a593Smuzhiyun  *
20*4882a593Smuzhiyun  */
21*4882a593Smuzhiyun #include <linux/module.h>
22*4882a593Smuzhiyun #include <linux/kernel.h>
23*4882a593Smuzhiyun #include <linux/types.h>
24*4882a593Smuzhiyun #include <linux/netdevice.h>
25*4882a593Smuzhiyun #include <linux/mii.h>
26*4882a593Smuzhiyun #include <linux/ethtool.h>
27*4882a593Smuzhiyun #include <linux/delay.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #include "emac.h"
30*4882a593Smuzhiyun #include "phy.h"
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #define phy_read _phy_read
33*4882a593Smuzhiyun #define phy_write _phy_write
34*4882a593Smuzhiyun 
_phy_read(struct mii_phy * phy,int reg)35*4882a593Smuzhiyun static inline int _phy_read(struct mii_phy *phy, int reg)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun 	return phy->mdio_read(phy->dev, phy->address, reg);
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun 
_phy_write(struct mii_phy * phy,int reg,int val)40*4882a593Smuzhiyun static inline void _phy_write(struct mii_phy *phy, int reg, int val)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun 	phy->mdio_write(phy->dev, phy->address, reg, val);
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun 
gpcs_phy_read(struct mii_phy * phy,int reg)45*4882a593Smuzhiyun static inline int gpcs_phy_read(struct mii_phy *phy, int reg)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun 	return phy->mdio_read(phy->dev, phy->gpcs_address, reg);
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun 
gpcs_phy_write(struct mii_phy * phy,int reg,int val)50*4882a593Smuzhiyun static inline void gpcs_phy_write(struct mii_phy *phy, int reg, int val)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun 	phy->mdio_write(phy->dev, phy->gpcs_address, reg, val);
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun 
emac_mii_reset_phy(struct mii_phy * phy)55*4882a593Smuzhiyun int emac_mii_reset_phy(struct mii_phy *phy)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	int val;
58*4882a593Smuzhiyun 	int limit = 10000;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	val = phy_read(phy, MII_BMCR);
61*4882a593Smuzhiyun 	val &= ~(BMCR_ISOLATE | BMCR_ANENABLE);
62*4882a593Smuzhiyun 	val |= BMCR_RESET;
63*4882a593Smuzhiyun 	phy_write(phy, MII_BMCR, val);
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	udelay(300);
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	while (--limit) {
68*4882a593Smuzhiyun 		val = phy_read(phy, MII_BMCR);
69*4882a593Smuzhiyun 		if (val >= 0 && (val & BMCR_RESET) == 0)
70*4882a593Smuzhiyun 			break;
71*4882a593Smuzhiyun 		udelay(10);
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 	if ((val & BMCR_ISOLATE) && limit > 0)
74*4882a593Smuzhiyun 		phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	return limit <= 0;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
emac_mii_reset_gpcs(struct mii_phy * phy)79*4882a593Smuzhiyun int emac_mii_reset_gpcs(struct mii_phy *phy)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun 	int val;
82*4882a593Smuzhiyun 	int limit = 10000;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	val = gpcs_phy_read(phy, MII_BMCR);
85*4882a593Smuzhiyun 	val &= ~(BMCR_ISOLATE | BMCR_ANENABLE);
86*4882a593Smuzhiyun 	val |= BMCR_RESET;
87*4882a593Smuzhiyun 	gpcs_phy_write(phy, MII_BMCR, val);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	udelay(300);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	while (--limit) {
92*4882a593Smuzhiyun 		val = gpcs_phy_read(phy, MII_BMCR);
93*4882a593Smuzhiyun 		if (val >= 0 && (val & BMCR_RESET) == 0)
94*4882a593Smuzhiyun 			break;
95*4882a593Smuzhiyun 		udelay(10);
96*4882a593Smuzhiyun 	}
97*4882a593Smuzhiyun 	if ((val & BMCR_ISOLATE) && limit > 0)
98*4882a593Smuzhiyun 		gpcs_phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	if (limit > 0 && phy->mode == PHY_INTERFACE_MODE_SGMII) {
101*4882a593Smuzhiyun 		/* Configure GPCS interface to recommended setting for SGMII */
102*4882a593Smuzhiyun 		gpcs_phy_write(phy, 0x04, 0x8120); /* AsymPause, FDX */
103*4882a593Smuzhiyun 		gpcs_phy_write(phy, 0x07, 0x2801); /* msg_pg, toggle */
104*4882a593Smuzhiyun 		gpcs_phy_write(phy, 0x00, 0x0140); /* 1Gbps, FDX     */
105*4882a593Smuzhiyun 	}
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	return limit <= 0;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun 
genmii_setup_aneg(struct mii_phy * phy,u32 advertise)110*4882a593Smuzhiyun static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	int ctl, adv;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	phy->autoneg = AUTONEG_ENABLE;
115*4882a593Smuzhiyun 	phy->speed = SPEED_10;
116*4882a593Smuzhiyun 	phy->duplex = DUPLEX_HALF;
117*4882a593Smuzhiyun 	phy->pause = phy->asym_pause = 0;
118*4882a593Smuzhiyun 	phy->advertising = advertise;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	ctl = phy_read(phy, MII_BMCR);
121*4882a593Smuzhiyun 	if (ctl < 0)
122*4882a593Smuzhiyun 		return ctl;
123*4882a593Smuzhiyun 	ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	/* First clear the PHY */
126*4882a593Smuzhiyun 	phy_write(phy, MII_BMCR, ctl);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	/* Setup standard advertise */
129*4882a593Smuzhiyun 	adv = phy_read(phy, MII_ADVERTISE);
130*4882a593Smuzhiyun 	if (adv < 0)
131*4882a593Smuzhiyun 		return adv;
132*4882a593Smuzhiyun 	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
133*4882a593Smuzhiyun 		 ADVERTISE_PAUSE_ASYM);
134*4882a593Smuzhiyun 	if (advertise & ADVERTISED_10baseT_Half)
135*4882a593Smuzhiyun 		adv |= ADVERTISE_10HALF;
136*4882a593Smuzhiyun 	if (advertise & ADVERTISED_10baseT_Full)
137*4882a593Smuzhiyun 		adv |= ADVERTISE_10FULL;
138*4882a593Smuzhiyun 	if (advertise & ADVERTISED_100baseT_Half)
139*4882a593Smuzhiyun 		adv |= ADVERTISE_100HALF;
140*4882a593Smuzhiyun 	if (advertise & ADVERTISED_100baseT_Full)
141*4882a593Smuzhiyun 		adv |= ADVERTISE_100FULL;
142*4882a593Smuzhiyun 	if (advertise & ADVERTISED_Pause)
143*4882a593Smuzhiyun 		adv |= ADVERTISE_PAUSE_CAP;
144*4882a593Smuzhiyun 	if (advertise & ADVERTISED_Asym_Pause)
145*4882a593Smuzhiyun 		adv |= ADVERTISE_PAUSE_ASYM;
146*4882a593Smuzhiyun 	phy_write(phy, MII_ADVERTISE, adv);
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	if (phy->features &
149*4882a593Smuzhiyun 	    (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {
150*4882a593Smuzhiyun 		adv = phy_read(phy, MII_CTRL1000);
151*4882a593Smuzhiyun 		if (adv < 0)
152*4882a593Smuzhiyun 			return adv;
153*4882a593Smuzhiyun 		adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
154*4882a593Smuzhiyun 		if (advertise & ADVERTISED_1000baseT_Full)
155*4882a593Smuzhiyun 			adv |= ADVERTISE_1000FULL;
156*4882a593Smuzhiyun 		if (advertise & ADVERTISED_1000baseT_Half)
157*4882a593Smuzhiyun 			adv |= ADVERTISE_1000HALF;
158*4882a593Smuzhiyun 		phy_write(phy, MII_CTRL1000, adv);
159*4882a593Smuzhiyun 	}
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	/* Start/Restart aneg */
162*4882a593Smuzhiyun 	ctl = phy_read(phy, MII_BMCR);
163*4882a593Smuzhiyun 	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
164*4882a593Smuzhiyun 	phy_write(phy, MII_BMCR, ctl);
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	return 0;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun 
genmii_setup_forced(struct mii_phy * phy,int speed,int fd)169*4882a593Smuzhiyun static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun 	int ctl;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	phy->autoneg = AUTONEG_DISABLE;
174*4882a593Smuzhiyun 	phy->speed = speed;
175*4882a593Smuzhiyun 	phy->duplex = fd;
176*4882a593Smuzhiyun 	phy->pause = phy->asym_pause = 0;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	ctl = phy_read(phy, MII_BMCR);
179*4882a593Smuzhiyun 	if (ctl < 0)
180*4882a593Smuzhiyun 		return ctl;
181*4882a593Smuzhiyun 	ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	/* First clear the PHY */
184*4882a593Smuzhiyun 	phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	/* Select speed & duplex */
187*4882a593Smuzhiyun 	switch (speed) {
188*4882a593Smuzhiyun 	case SPEED_10:
189*4882a593Smuzhiyun 		break;
190*4882a593Smuzhiyun 	case SPEED_100:
191*4882a593Smuzhiyun 		ctl |= BMCR_SPEED100;
192*4882a593Smuzhiyun 		break;
193*4882a593Smuzhiyun 	case SPEED_1000:
194*4882a593Smuzhiyun 		ctl |= BMCR_SPEED1000;
195*4882a593Smuzhiyun 		break;
196*4882a593Smuzhiyun 	default:
197*4882a593Smuzhiyun 		return -EINVAL;
198*4882a593Smuzhiyun 	}
199*4882a593Smuzhiyun 	if (fd == DUPLEX_FULL)
200*4882a593Smuzhiyun 		ctl |= BMCR_FULLDPLX;
201*4882a593Smuzhiyun 	phy_write(phy, MII_BMCR, ctl);
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	return 0;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
genmii_poll_link(struct mii_phy * phy)206*4882a593Smuzhiyun static int genmii_poll_link(struct mii_phy *phy)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun 	int status;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	/* Clear latched value with dummy read */
211*4882a593Smuzhiyun 	phy_read(phy, MII_BMSR);
212*4882a593Smuzhiyun 	status = phy_read(phy, MII_BMSR);
213*4882a593Smuzhiyun 	if (status < 0 || (status & BMSR_LSTATUS) == 0)
214*4882a593Smuzhiyun 		return 0;
215*4882a593Smuzhiyun 	if (phy->autoneg == AUTONEG_ENABLE && !(status & BMSR_ANEGCOMPLETE))
216*4882a593Smuzhiyun 		return 0;
217*4882a593Smuzhiyun 	return 1;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
genmii_read_link(struct mii_phy * phy)220*4882a593Smuzhiyun static int genmii_read_link(struct mii_phy *phy)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	if (phy->autoneg == AUTONEG_ENABLE) {
223*4882a593Smuzhiyun 		int glpa = 0;
224*4882a593Smuzhiyun 		int lpa = phy_read(phy, MII_LPA) & phy_read(phy, MII_ADVERTISE);
225*4882a593Smuzhiyun 		if (lpa < 0)
226*4882a593Smuzhiyun 			return lpa;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 		if (phy->features &
229*4882a593Smuzhiyun 		    (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {
230*4882a593Smuzhiyun 			int adv = phy_read(phy, MII_CTRL1000);
231*4882a593Smuzhiyun 			glpa = phy_read(phy, MII_STAT1000);
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 			if (glpa < 0 || adv < 0)
234*4882a593Smuzhiyun 				return adv;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 			glpa &= adv << 2;
237*4882a593Smuzhiyun 		}
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 		phy->speed = SPEED_10;
240*4882a593Smuzhiyun 		phy->duplex = DUPLEX_HALF;
241*4882a593Smuzhiyun 		phy->pause = phy->asym_pause = 0;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 		if (glpa & (LPA_1000FULL | LPA_1000HALF)) {
244*4882a593Smuzhiyun 			phy->speed = SPEED_1000;
245*4882a593Smuzhiyun 			if (glpa & LPA_1000FULL)
246*4882a593Smuzhiyun 				phy->duplex = DUPLEX_FULL;
247*4882a593Smuzhiyun 		} else if (lpa & (LPA_100FULL | LPA_100HALF)) {
248*4882a593Smuzhiyun 			phy->speed = SPEED_100;
249*4882a593Smuzhiyun 			if (lpa & LPA_100FULL)
250*4882a593Smuzhiyun 				phy->duplex = DUPLEX_FULL;
251*4882a593Smuzhiyun 		} else if (lpa & LPA_10FULL)
252*4882a593Smuzhiyun 			phy->duplex = DUPLEX_FULL;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 		if (phy->duplex == DUPLEX_FULL) {
255*4882a593Smuzhiyun 			phy->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
256*4882a593Smuzhiyun 			phy->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
257*4882a593Smuzhiyun 		}
258*4882a593Smuzhiyun 	} else {
259*4882a593Smuzhiyun 		int bmcr = phy_read(phy, MII_BMCR);
260*4882a593Smuzhiyun 		if (bmcr < 0)
261*4882a593Smuzhiyun 			return bmcr;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 		if (bmcr & BMCR_FULLDPLX)
264*4882a593Smuzhiyun 			phy->duplex = DUPLEX_FULL;
265*4882a593Smuzhiyun 		else
266*4882a593Smuzhiyun 			phy->duplex = DUPLEX_HALF;
267*4882a593Smuzhiyun 		if (bmcr & BMCR_SPEED1000)
268*4882a593Smuzhiyun 			phy->speed = SPEED_1000;
269*4882a593Smuzhiyun 		else if (bmcr & BMCR_SPEED100)
270*4882a593Smuzhiyun 			phy->speed = SPEED_100;
271*4882a593Smuzhiyun 		else
272*4882a593Smuzhiyun 			phy->speed = SPEED_10;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 		phy->pause = phy->asym_pause = 0;
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 	return 0;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun /* Generic implementation for most 10/100/1000 PHYs */
280*4882a593Smuzhiyun static const struct mii_phy_ops generic_phy_ops = {
281*4882a593Smuzhiyun 	.setup_aneg	= genmii_setup_aneg,
282*4882a593Smuzhiyun 	.setup_forced	= genmii_setup_forced,
283*4882a593Smuzhiyun 	.poll_link	= genmii_poll_link,
284*4882a593Smuzhiyun 	.read_link	= genmii_read_link
285*4882a593Smuzhiyun };
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun static struct mii_phy_def genmii_phy_def = {
288*4882a593Smuzhiyun 	.phy_id		= 0x00000000,
289*4882a593Smuzhiyun 	.phy_id_mask	= 0x00000000,
290*4882a593Smuzhiyun 	.name		= "Generic MII",
291*4882a593Smuzhiyun 	.ops		= &generic_phy_ops
292*4882a593Smuzhiyun };
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun /* CIS8201 */
295*4882a593Smuzhiyun #define MII_CIS8201_10BTCSR	0x16
296*4882a593Smuzhiyun #define  TENBTCSR_ECHO_DISABLE	0x2000
297*4882a593Smuzhiyun #define MII_CIS8201_EPCR	0x17
298*4882a593Smuzhiyun #define  EPCR_MODE_MASK		0x3000
299*4882a593Smuzhiyun #define  EPCR_GMII_MODE		0x0000
300*4882a593Smuzhiyun #define  EPCR_RGMII_MODE	0x1000
301*4882a593Smuzhiyun #define  EPCR_TBI_MODE		0x2000
302*4882a593Smuzhiyun #define  EPCR_RTBI_MODE		0x3000
303*4882a593Smuzhiyun #define MII_CIS8201_ACSR	0x1c
304*4882a593Smuzhiyun #define  ACSR_PIN_PRIO_SELECT	0x0004
305*4882a593Smuzhiyun 
cis8201_init(struct mii_phy * phy)306*4882a593Smuzhiyun static int cis8201_init(struct mii_phy *phy)
307*4882a593Smuzhiyun {
308*4882a593Smuzhiyun 	int epcr;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	epcr = phy_read(phy, MII_CIS8201_EPCR);
311*4882a593Smuzhiyun 	if (epcr < 0)
312*4882a593Smuzhiyun 		return epcr;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	epcr &= ~EPCR_MODE_MASK;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	switch (phy->mode) {
317*4882a593Smuzhiyun 	case PHY_INTERFACE_MODE_TBI:
318*4882a593Smuzhiyun 		epcr |= EPCR_TBI_MODE;
319*4882a593Smuzhiyun 		break;
320*4882a593Smuzhiyun 	case PHY_INTERFACE_MODE_RTBI:
321*4882a593Smuzhiyun 		epcr |= EPCR_RTBI_MODE;
322*4882a593Smuzhiyun 		break;
323*4882a593Smuzhiyun 	case PHY_INTERFACE_MODE_GMII:
324*4882a593Smuzhiyun 		epcr |= EPCR_GMII_MODE;
325*4882a593Smuzhiyun 		break;
326*4882a593Smuzhiyun 	case PHY_INTERFACE_MODE_RGMII:
327*4882a593Smuzhiyun 	default:
328*4882a593Smuzhiyun 		epcr |= EPCR_RGMII_MODE;
329*4882a593Smuzhiyun 	}
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	phy_write(phy, MII_CIS8201_EPCR, epcr);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	/* MII regs override strap pins */
334*4882a593Smuzhiyun 	phy_write(phy, MII_CIS8201_ACSR,
335*4882a593Smuzhiyun 		  phy_read(phy, MII_CIS8201_ACSR) | ACSR_PIN_PRIO_SELECT);
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	/* Disable TX_EN -> CRS echo mode, otherwise 10/HDX doesn't work */
338*4882a593Smuzhiyun 	phy_write(phy, MII_CIS8201_10BTCSR,
339*4882a593Smuzhiyun 		  phy_read(phy, MII_CIS8201_10BTCSR) | TENBTCSR_ECHO_DISABLE);
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	return 0;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun static const struct mii_phy_ops cis8201_phy_ops = {
345*4882a593Smuzhiyun 	.init		= cis8201_init,
346*4882a593Smuzhiyun 	.setup_aneg	= genmii_setup_aneg,
347*4882a593Smuzhiyun 	.setup_forced	= genmii_setup_forced,
348*4882a593Smuzhiyun 	.poll_link	= genmii_poll_link,
349*4882a593Smuzhiyun 	.read_link	= genmii_read_link
350*4882a593Smuzhiyun };
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun static struct mii_phy_def cis8201_phy_def = {
353*4882a593Smuzhiyun 	.phy_id		= 0x000fc410,
354*4882a593Smuzhiyun 	.phy_id_mask	= 0x000ffff0,
355*4882a593Smuzhiyun 	.name		= "CIS8201 Gigabit Ethernet",
356*4882a593Smuzhiyun 	.ops		= &cis8201_phy_ops
357*4882a593Smuzhiyun };
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun static struct mii_phy_def bcm5248_phy_def = {
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	.phy_id		= 0x0143bc00,
362*4882a593Smuzhiyun 	.phy_id_mask	= 0x0ffffff0,
363*4882a593Smuzhiyun 	.name		= "BCM5248 10/100 SMII Ethernet",
364*4882a593Smuzhiyun 	.ops		= &generic_phy_ops
365*4882a593Smuzhiyun };
366*4882a593Smuzhiyun 
m88e1111_init(struct mii_phy * phy)367*4882a593Smuzhiyun static int m88e1111_init(struct mii_phy *phy)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun 	pr_debug("%s: Marvell 88E1111 Ethernet\n", __func__);
370*4882a593Smuzhiyun 	phy_write(phy, 0x14, 0x0ce3);
371*4882a593Smuzhiyun 	phy_write(phy, 0x18, 0x4101);
372*4882a593Smuzhiyun 	phy_write(phy, 0x09, 0x0e00);
373*4882a593Smuzhiyun 	phy_write(phy, 0x04, 0x01e1);
374*4882a593Smuzhiyun 	phy_write(phy, 0x00, 0x9140);
375*4882a593Smuzhiyun 	phy_write(phy, 0x00, 0x1140);
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	return  0;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun 
m88e1112_init(struct mii_phy * phy)380*4882a593Smuzhiyun static int m88e1112_init(struct mii_phy *phy)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun 	/*
383*4882a593Smuzhiyun 	 * Marvell 88E1112 PHY needs to have the SGMII MAC
384*4882a593Smuzhiyun 	 * interace (page 2) properly configured to
385*4882a593Smuzhiyun 	 * communicate with the 460EX/GT GPCS interface.
386*4882a593Smuzhiyun 	 */
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	u16 reg_short;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	pr_debug("%s: Marvell 88E1112 Ethernet\n", __func__);
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	/* Set access to Page 2 */
393*4882a593Smuzhiyun 	phy_write(phy, 0x16, 0x0002);
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	phy_write(phy, 0x00, 0x0040); /* 1Gbps */
396*4882a593Smuzhiyun 	reg_short = (u16)(phy_read(phy, 0x1a));
397*4882a593Smuzhiyun 	reg_short |= 0x8000; /* bypass Auto-Negotiation */
398*4882a593Smuzhiyun 	phy_write(phy, 0x1a, reg_short);
399*4882a593Smuzhiyun 	emac_mii_reset_phy(phy); /* reset MAC interface */
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	/* Reset access to Page 0 */
402*4882a593Smuzhiyun 	phy_write(phy, 0x16, 0x0000);
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	return  0;
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
et1011c_init(struct mii_phy * phy)407*4882a593Smuzhiyun static int et1011c_init(struct mii_phy *phy)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	u16 reg_short;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	reg_short = (u16)(phy_read(phy, 0x16));
412*4882a593Smuzhiyun 	reg_short &= ~(0x7);
413*4882a593Smuzhiyun 	reg_short |= 0x6;	/* RGMII Trace Delay*/
414*4882a593Smuzhiyun 	phy_write(phy, 0x16, reg_short);
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	reg_short = (u16)(phy_read(phy, 0x17));
417*4882a593Smuzhiyun 	reg_short &= ~(0x40);
418*4882a593Smuzhiyun 	phy_write(phy, 0x17, reg_short);
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	phy_write(phy, 0x1c, 0x74f0);
421*4882a593Smuzhiyun 	return 0;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun static const struct mii_phy_ops et1011c_phy_ops = {
425*4882a593Smuzhiyun 	.init		= et1011c_init,
426*4882a593Smuzhiyun 	.setup_aneg	= genmii_setup_aneg,
427*4882a593Smuzhiyun 	.setup_forced	= genmii_setup_forced,
428*4882a593Smuzhiyun 	.poll_link	= genmii_poll_link,
429*4882a593Smuzhiyun 	.read_link	= genmii_read_link
430*4882a593Smuzhiyun };
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun static struct mii_phy_def et1011c_phy_def = {
433*4882a593Smuzhiyun 	.phy_id		= 0x0282f000,
434*4882a593Smuzhiyun 	.phy_id_mask	= 0x0fffff00,
435*4882a593Smuzhiyun 	.name		= "ET1011C Gigabit Ethernet",
436*4882a593Smuzhiyun 	.ops		= &et1011c_phy_ops
437*4882a593Smuzhiyun };
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun static const struct mii_phy_ops m88e1111_phy_ops = {
444*4882a593Smuzhiyun 	.init		= m88e1111_init,
445*4882a593Smuzhiyun 	.setup_aneg	= genmii_setup_aneg,
446*4882a593Smuzhiyun 	.setup_forced	= genmii_setup_forced,
447*4882a593Smuzhiyun 	.poll_link	= genmii_poll_link,
448*4882a593Smuzhiyun 	.read_link	= genmii_read_link
449*4882a593Smuzhiyun };
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun static struct mii_phy_def m88e1111_phy_def = {
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	.phy_id		= 0x01410CC0,
454*4882a593Smuzhiyun 	.phy_id_mask	= 0x0ffffff0,
455*4882a593Smuzhiyun 	.name		= "Marvell 88E1111 Ethernet",
456*4882a593Smuzhiyun 	.ops		= &m88e1111_phy_ops,
457*4882a593Smuzhiyun };
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun static const struct mii_phy_ops m88e1112_phy_ops = {
460*4882a593Smuzhiyun 	.init		= m88e1112_init,
461*4882a593Smuzhiyun 	.setup_aneg	= genmii_setup_aneg,
462*4882a593Smuzhiyun 	.setup_forced	= genmii_setup_forced,
463*4882a593Smuzhiyun 	.poll_link	= genmii_poll_link,
464*4882a593Smuzhiyun 	.read_link	= genmii_read_link
465*4882a593Smuzhiyun };
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun static struct mii_phy_def m88e1112_phy_def = {
468*4882a593Smuzhiyun 	.phy_id		= 0x01410C90,
469*4882a593Smuzhiyun 	.phy_id_mask	= 0x0ffffff0,
470*4882a593Smuzhiyun 	.name		= "Marvell 88E1112 Ethernet",
471*4882a593Smuzhiyun 	.ops		= &m88e1112_phy_ops,
472*4882a593Smuzhiyun };
473*4882a593Smuzhiyun 
ar8035_init(struct mii_phy * phy)474*4882a593Smuzhiyun static int ar8035_init(struct mii_phy *phy)
475*4882a593Smuzhiyun {
476*4882a593Smuzhiyun 	phy_write(phy, 0x1d, 0x5); /* Address debug register 5 */
477*4882a593Smuzhiyun 	phy_write(phy, 0x1e, 0x2d47); /* Value copied from u-boot */
478*4882a593Smuzhiyun 	phy_write(phy, 0x1d, 0xb);    /* Address hib ctrl */
479*4882a593Smuzhiyun 	phy_write(phy, 0x1e, 0xbc20); /* Value copied from u-boot */
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	return 0;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun static const struct mii_phy_ops ar8035_phy_ops = {
485*4882a593Smuzhiyun 	.init		= ar8035_init,
486*4882a593Smuzhiyun 	.setup_aneg	= genmii_setup_aneg,
487*4882a593Smuzhiyun 	.setup_forced	= genmii_setup_forced,
488*4882a593Smuzhiyun 	.poll_link	= genmii_poll_link,
489*4882a593Smuzhiyun 	.read_link	= genmii_read_link,
490*4882a593Smuzhiyun };
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun static struct mii_phy_def ar8035_phy_def = {
493*4882a593Smuzhiyun 	.phy_id		= 0x004dd070,
494*4882a593Smuzhiyun 	.phy_id_mask	= 0xfffffff0,
495*4882a593Smuzhiyun 	.name		= "Atheros 8035 Gigabit Ethernet",
496*4882a593Smuzhiyun 	.ops		= &ar8035_phy_ops,
497*4882a593Smuzhiyun };
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun static struct mii_phy_def *mii_phy_table[] = {
500*4882a593Smuzhiyun 	&et1011c_phy_def,
501*4882a593Smuzhiyun 	&cis8201_phy_def,
502*4882a593Smuzhiyun 	&bcm5248_phy_def,
503*4882a593Smuzhiyun 	&m88e1111_phy_def,
504*4882a593Smuzhiyun 	&m88e1112_phy_def,
505*4882a593Smuzhiyun 	&ar8035_phy_def,
506*4882a593Smuzhiyun 	&genmii_phy_def,
507*4882a593Smuzhiyun 	NULL
508*4882a593Smuzhiyun };
509*4882a593Smuzhiyun 
emac_mii_phy_probe(struct mii_phy * phy,int address)510*4882a593Smuzhiyun int emac_mii_phy_probe(struct mii_phy *phy, int address)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun 	struct mii_phy_def *def;
513*4882a593Smuzhiyun 	int i;
514*4882a593Smuzhiyun 	u32 id;
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	phy->autoneg = AUTONEG_DISABLE;
517*4882a593Smuzhiyun 	phy->advertising = 0;
518*4882a593Smuzhiyun 	phy->address = address;
519*4882a593Smuzhiyun 	phy->speed = SPEED_10;
520*4882a593Smuzhiyun 	phy->duplex = DUPLEX_HALF;
521*4882a593Smuzhiyun 	phy->pause = phy->asym_pause = 0;
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	/* Take PHY out of isolate mode and reset it. */
524*4882a593Smuzhiyun 	if (emac_mii_reset_phy(phy))
525*4882a593Smuzhiyun 		return -ENODEV;
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	/* Read ID and find matching entry */
528*4882a593Smuzhiyun 	id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2);
529*4882a593Smuzhiyun 	for (i = 0; (def = mii_phy_table[i]) != NULL; i++)
530*4882a593Smuzhiyun 		if ((id & def->phy_id_mask) == def->phy_id)
531*4882a593Smuzhiyun 			break;
532*4882a593Smuzhiyun 	/* Should never be NULL (we have a generic entry), but... */
533*4882a593Smuzhiyun 	if (!def)
534*4882a593Smuzhiyun 		return -ENODEV;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	phy->def = def;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	/* Determine PHY features if needed */
539*4882a593Smuzhiyun 	phy->features = def->features;
540*4882a593Smuzhiyun 	if (!phy->features) {
541*4882a593Smuzhiyun 		u16 bmsr = phy_read(phy, MII_BMSR);
542*4882a593Smuzhiyun 		if (bmsr & BMSR_ANEGCAPABLE)
543*4882a593Smuzhiyun 			phy->features |= SUPPORTED_Autoneg;
544*4882a593Smuzhiyun 		if (bmsr & BMSR_10HALF)
545*4882a593Smuzhiyun 			phy->features |= SUPPORTED_10baseT_Half;
546*4882a593Smuzhiyun 		if (bmsr & BMSR_10FULL)
547*4882a593Smuzhiyun 			phy->features |= SUPPORTED_10baseT_Full;
548*4882a593Smuzhiyun 		if (bmsr & BMSR_100HALF)
549*4882a593Smuzhiyun 			phy->features |= SUPPORTED_100baseT_Half;
550*4882a593Smuzhiyun 		if (bmsr & BMSR_100FULL)
551*4882a593Smuzhiyun 			phy->features |= SUPPORTED_100baseT_Full;
552*4882a593Smuzhiyun 		if (bmsr & BMSR_ESTATEN) {
553*4882a593Smuzhiyun 			u16 esr = phy_read(phy, MII_ESTATUS);
554*4882a593Smuzhiyun 			if (esr & ESTATUS_1000_TFULL)
555*4882a593Smuzhiyun 				phy->features |= SUPPORTED_1000baseT_Full;
556*4882a593Smuzhiyun 			if (esr & ESTATUS_1000_THALF)
557*4882a593Smuzhiyun 				phy->features |= SUPPORTED_1000baseT_Half;
558*4882a593Smuzhiyun 		}
559*4882a593Smuzhiyun 		phy->features |= SUPPORTED_MII;
560*4882a593Smuzhiyun 	}
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	/* Setup default advertising */
563*4882a593Smuzhiyun 	phy->advertising = phy->features;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 	return 0;
566*4882a593Smuzhiyun }
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun MODULE_LICENSE("GPL");
569