xref: /OK3568_Linux_fs/u-boot/common/miiphyutil.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * (C) Copyright 2001
3*4882a593Smuzhiyun  * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0+
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun  * This provides a bit-banged interface to the ethernet MII management
10*4882a593Smuzhiyun  * channel.
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <common.h>
14*4882a593Smuzhiyun #include <dm.h>
15*4882a593Smuzhiyun #include <miiphy.h>
16*4882a593Smuzhiyun #include <phy.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <asm/types.h>
19*4882a593Smuzhiyun #include <linux/list.h>
20*4882a593Smuzhiyun #include <malloc.h>
21*4882a593Smuzhiyun #include <net.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /* local debug macro */
24*4882a593Smuzhiyun #undef MII_DEBUG
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #undef debug
27*4882a593Smuzhiyun #ifdef MII_DEBUG
28*4882a593Smuzhiyun #define debug(fmt, args...)	printf(fmt, ##args)
29*4882a593Smuzhiyun #else
30*4882a593Smuzhiyun #define debug(fmt, args...)
31*4882a593Smuzhiyun #endif /* MII_DEBUG */
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun static struct list_head mii_devs;
34*4882a593Smuzhiyun static struct mii_dev *current_mii;
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun /*
37*4882a593Smuzhiyun  * Lookup the mii_dev struct by the registered device name.
38*4882a593Smuzhiyun  */
miiphy_get_dev_by_name(const char * devname)39*4882a593Smuzhiyun struct mii_dev *miiphy_get_dev_by_name(const char *devname)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	struct list_head *entry;
42*4882a593Smuzhiyun 	struct mii_dev *dev;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	if (!devname) {
45*4882a593Smuzhiyun 		printf("NULL device name!\n");
46*4882a593Smuzhiyun 		return NULL;
47*4882a593Smuzhiyun 	}
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	list_for_each(entry, &mii_devs) {
50*4882a593Smuzhiyun 		dev = list_entry(entry, struct mii_dev, link);
51*4882a593Smuzhiyun 		if (strcmp(dev->name, devname) == 0)
52*4882a593Smuzhiyun 			return dev;
53*4882a593Smuzhiyun 	}
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	return NULL;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun /*****************************************************************************
59*4882a593Smuzhiyun  *
60*4882a593Smuzhiyun  * Initialize global data. Need to be called before any other miiphy routine.
61*4882a593Smuzhiyun  */
miiphy_init(void)62*4882a593Smuzhiyun void miiphy_init(void)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun 	INIT_LIST_HEAD(&mii_devs);
65*4882a593Smuzhiyun 	current_mii = NULL;
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun 
mdio_alloc(void)68*4882a593Smuzhiyun struct mii_dev *mdio_alloc(void)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	struct mii_dev *bus;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	bus = malloc(sizeof(*bus));
73*4882a593Smuzhiyun 	if (!bus)
74*4882a593Smuzhiyun 		return bus;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	memset(bus, 0, sizeof(*bus));
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	/* initalize mii_dev struct fields */
79*4882a593Smuzhiyun 	INIT_LIST_HEAD(&bus->link);
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	return bus;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun 
mdio_free(struct mii_dev * bus)84*4882a593Smuzhiyun void mdio_free(struct mii_dev *bus)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun 	free(bus);
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun 
mdio_register(struct mii_dev * bus)89*4882a593Smuzhiyun int mdio_register(struct mii_dev *bus)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun 	if (!bus || !bus->read || !bus->write)
92*4882a593Smuzhiyun 		return -1;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	/* check if we have unique name */
95*4882a593Smuzhiyun 	if (miiphy_get_dev_by_name(bus->name)) {
96*4882a593Smuzhiyun 		printf("mdio_register: non unique device name '%s'\n",
97*4882a593Smuzhiyun 			bus->name);
98*4882a593Smuzhiyun 		return -1;
99*4882a593Smuzhiyun 	}
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	/* add it to the list */
102*4882a593Smuzhiyun 	list_add_tail(&bus->link, &mii_devs);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	if (!current_mii)
105*4882a593Smuzhiyun 		current_mii = bus;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	return 0;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun 
mdio_register_seq(struct mii_dev * bus,int seq)110*4882a593Smuzhiyun int mdio_register_seq(struct mii_dev *bus, int seq)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	int ret;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	/* Setup a unique name for each mdio bus */
115*4882a593Smuzhiyun 	ret = snprintf(bus->name, MDIO_NAME_LEN, "eth%d", seq);
116*4882a593Smuzhiyun 	if (ret < 0)
117*4882a593Smuzhiyun 		return ret;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	return mdio_register(bus);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
mdio_unregister(struct mii_dev * bus)122*4882a593Smuzhiyun int mdio_unregister(struct mii_dev *bus)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	if (!bus)
125*4882a593Smuzhiyun 		return 0;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	/* delete it from the list */
128*4882a593Smuzhiyun 	list_del(&bus->link);
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	if (current_mii == bus)
131*4882a593Smuzhiyun 		current_mii = NULL;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	return 0;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun 
mdio_list_devices(void)136*4882a593Smuzhiyun void mdio_list_devices(void)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun 	struct list_head *entry;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	list_for_each(entry, &mii_devs) {
141*4882a593Smuzhiyun 		int i;
142*4882a593Smuzhiyun 		struct mii_dev *bus = list_entry(entry, struct mii_dev, link);
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 		printf("%s:\n", bus->name);
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 		for (i = 0; i < PHY_MAX_ADDR; i++) {
147*4882a593Smuzhiyun 			struct phy_device *phydev = bus->phymap[i];
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 			if (phydev) {
150*4882a593Smuzhiyun 				printf("%x - %s", i, phydev->drv->name);
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 				if (phydev->dev)
153*4882a593Smuzhiyun 					printf(" <--> %s\n", phydev->dev->name);
154*4882a593Smuzhiyun 				else
155*4882a593Smuzhiyun 					printf("\n");
156*4882a593Smuzhiyun 			}
157*4882a593Smuzhiyun 		}
158*4882a593Smuzhiyun 	}
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun 
miiphy_set_current_dev(const char * devname)161*4882a593Smuzhiyun int miiphy_set_current_dev(const char *devname)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	struct mii_dev *dev;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	dev = miiphy_get_dev_by_name(devname);
166*4882a593Smuzhiyun 	if (dev) {
167*4882a593Smuzhiyun 		current_mii = dev;
168*4882a593Smuzhiyun 		return 0;
169*4882a593Smuzhiyun 	}
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	printf("No such device: %s\n", devname);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	return 1;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun 
mdio_get_current_dev(void)176*4882a593Smuzhiyun struct mii_dev *mdio_get_current_dev(void)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun 	return current_mii;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
mdio_phydev_for_ethname(const char * ethname)181*4882a593Smuzhiyun struct phy_device *mdio_phydev_for_ethname(const char *ethname)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	struct list_head *entry;
184*4882a593Smuzhiyun 	struct mii_dev *bus;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	list_for_each(entry, &mii_devs) {
187*4882a593Smuzhiyun 		int i;
188*4882a593Smuzhiyun 		bus = list_entry(entry, struct mii_dev, link);
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 		for (i = 0; i < PHY_MAX_ADDR; i++) {
191*4882a593Smuzhiyun 			if (!bus->phymap[i] || !bus->phymap[i]->dev)
192*4882a593Smuzhiyun 				continue;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 			if (strcmp(bus->phymap[i]->dev->name, ethname) == 0)
195*4882a593Smuzhiyun 				return bus->phymap[i];
196*4882a593Smuzhiyun 		}
197*4882a593Smuzhiyun 	}
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	printf("%s is not a known ethernet\n", ethname);
200*4882a593Smuzhiyun 	return NULL;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun 
miiphy_get_current_dev(void)203*4882a593Smuzhiyun const char *miiphy_get_current_dev(void)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	if (current_mii)
206*4882a593Smuzhiyun 		return current_mii->name;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	return NULL;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun 
miiphy_get_active_dev(const char * devname)211*4882a593Smuzhiyun static struct mii_dev *miiphy_get_active_dev(const char *devname)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun 	/* If the current mii is the one we want, return it */
214*4882a593Smuzhiyun 	if (current_mii)
215*4882a593Smuzhiyun 		if (strcmp(current_mii->name, devname) == 0)
216*4882a593Smuzhiyun 			return current_mii;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	/* Otherwise, set the active one to the one we want */
219*4882a593Smuzhiyun 	if (miiphy_set_current_dev(devname))
220*4882a593Smuzhiyun 		return NULL;
221*4882a593Smuzhiyun 	else
222*4882a593Smuzhiyun 		return current_mii;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun /*****************************************************************************
226*4882a593Smuzhiyun  *
227*4882a593Smuzhiyun  * Read to variable <value> from the PHY attached to device <devname>,
228*4882a593Smuzhiyun  * use PHY address <addr> and register <reg>.
229*4882a593Smuzhiyun  *
230*4882a593Smuzhiyun  * This API is deprecated. Use phy_read on a phy_device found via phy_connect
231*4882a593Smuzhiyun  *
232*4882a593Smuzhiyun  * Returns:
233*4882a593Smuzhiyun  *   0 on success
234*4882a593Smuzhiyun  */
miiphy_read(const char * devname,unsigned char addr,unsigned char reg,unsigned short * value)235*4882a593Smuzhiyun int miiphy_read(const char *devname, unsigned char addr, unsigned char reg,
236*4882a593Smuzhiyun 		 unsigned short *value)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun 	struct mii_dev *bus;
239*4882a593Smuzhiyun 	int ret;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	bus = miiphy_get_active_dev(devname);
242*4882a593Smuzhiyun 	if (!bus)
243*4882a593Smuzhiyun 		return 1;
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	ret = bus->read(bus, addr, MDIO_DEVAD_NONE, reg);
246*4882a593Smuzhiyun 	if (ret < 0)
247*4882a593Smuzhiyun 		return 1;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	*value = (unsigned short)ret;
250*4882a593Smuzhiyun 	return 0;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun /*****************************************************************************
254*4882a593Smuzhiyun  *
255*4882a593Smuzhiyun  * Write <value> to the PHY attached to device <devname>,
256*4882a593Smuzhiyun  * use PHY address <addr> and register <reg>.
257*4882a593Smuzhiyun  *
258*4882a593Smuzhiyun  * This API is deprecated. Use phy_write on a phy_device found by phy_connect
259*4882a593Smuzhiyun  *
260*4882a593Smuzhiyun  * Returns:
261*4882a593Smuzhiyun  *   0 on success
262*4882a593Smuzhiyun  */
miiphy_write(const char * devname,unsigned char addr,unsigned char reg,unsigned short value)263*4882a593Smuzhiyun int miiphy_write(const char *devname, unsigned char addr, unsigned char reg,
264*4882a593Smuzhiyun 		  unsigned short value)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun 	struct mii_dev *bus;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	bus = miiphy_get_active_dev(devname);
269*4882a593Smuzhiyun 	if (bus)
270*4882a593Smuzhiyun 		return bus->write(bus, addr, MDIO_DEVAD_NONE, reg, value);
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	return 1;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun /*****************************************************************************
276*4882a593Smuzhiyun  *
277*4882a593Smuzhiyun  * Print out list of registered MII capable devices.
278*4882a593Smuzhiyun  */
miiphy_listdev(void)279*4882a593Smuzhiyun void miiphy_listdev(void)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun 	struct list_head *entry;
282*4882a593Smuzhiyun 	struct mii_dev *dev;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	puts("MII devices: ");
285*4882a593Smuzhiyun 	list_for_each(entry, &mii_devs) {
286*4882a593Smuzhiyun 		dev = list_entry(entry, struct mii_dev, link);
287*4882a593Smuzhiyun 		printf("'%s' ", dev->name);
288*4882a593Smuzhiyun 	}
289*4882a593Smuzhiyun 	puts("\n");
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	if (current_mii)
292*4882a593Smuzhiyun 		printf("Current device: '%s'\n", current_mii->name);
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun /*****************************************************************************
296*4882a593Smuzhiyun  *
297*4882a593Smuzhiyun  * Read the OUI, manufacture's model number, and revision number.
298*4882a593Smuzhiyun  *
299*4882a593Smuzhiyun  * OUI:     22 bits (unsigned int)
300*4882a593Smuzhiyun  * Model:    6 bits (unsigned char)
301*4882a593Smuzhiyun  * Revision: 4 bits (unsigned char)
302*4882a593Smuzhiyun  *
303*4882a593Smuzhiyun  * This API is deprecated.
304*4882a593Smuzhiyun  *
305*4882a593Smuzhiyun  * Returns:
306*4882a593Smuzhiyun  *   0 on success
307*4882a593Smuzhiyun  */
miiphy_info(const char * devname,unsigned char addr,unsigned int * oui,unsigned char * model,unsigned char * rev)308*4882a593Smuzhiyun int miiphy_info(const char *devname, unsigned char addr, unsigned int *oui,
309*4882a593Smuzhiyun 		 unsigned char *model, unsigned char *rev)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun 	unsigned int reg = 0;
312*4882a593Smuzhiyun 	unsigned short tmp;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	if (miiphy_read(devname, addr, MII_PHYSID2, &tmp) != 0) {
315*4882a593Smuzhiyun 		debug("PHY ID register 2 read failed\n");
316*4882a593Smuzhiyun 		return -1;
317*4882a593Smuzhiyun 	}
318*4882a593Smuzhiyun 	reg = tmp;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	debug("MII_PHYSID2 @ 0x%x = 0x%04x\n", addr, reg);
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	if (reg == 0xFFFF) {
323*4882a593Smuzhiyun 		/* No physical device present at this address */
324*4882a593Smuzhiyun 		return -1;
325*4882a593Smuzhiyun 	}
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	if (miiphy_read(devname, addr, MII_PHYSID1, &tmp) != 0) {
328*4882a593Smuzhiyun 		debug("PHY ID register 1 read failed\n");
329*4882a593Smuzhiyun 		return -1;
330*4882a593Smuzhiyun 	}
331*4882a593Smuzhiyun 	reg |= tmp << 16;
332*4882a593Smuzhiyun 	debug("PHY_PHYIDR[1,2] @ 0x%x = 0x%08x\n", addr, reg);
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	*oui = (reg >> 10);
335*4882a593Smuzhiyun 	*model = (unsigned char)((reg >> 4) & 0x0000003F);
336*4882a593Smuzhiyun 	*rev = (unsigned char)(reg & 0x0000000F);
337*4882a593Smuzhiyun 	return 0;
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun #ifndef CONFIG_PHYLIB
341*4882a593Smuzhiyun /*****************************************************************************
342*4882a593Smuzhiyun  *
343*4882a593Smuzhiyun  * Reset the PHY.
344*4882a593Smuzhiyun  *
345*4882a593Smuzhiyun  * This API is deprecated. Use PHYLIB.
346*4882a593Smuzhiyun  *
347*4882a593Smuzhiyun  * Returns:
348*4882a593Smuzhiyun  *   0 on success
349*4882a593Smuzhiyun  */
miiphy_reset(const char * devname,unsigned char addr)350*4882a593Smuzhiyun int miiphy_reset(const char *devname, unsigned char addr)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun 	unsigned short reg;
353*4882a593Smuzhiyun 	int timeout = 500;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	if (miiphy_read(devname, addr, MII_BMCR, &reg) != 0) {
356*4882a593Smuzhiyun 		debug("PHY status read failed\n");
357*4882a593Smuzhiyun 		return -1;
358*4882a593Smuzhiyun 	}
359*4882a593Smuzhiyun 	if (miiphy_write(devname, addr, MII_BMCR, reg | BMCR_RESET) != 0) {
360*4882a593Smuzhiyun 		debug("PHY reset failed\n");
361*4882a593Smuzhiyun 		return -1;
362*4882a593Smuzhiyun 	}
363*4882a593Smuzhiyun #ifdef CONFIG_PHY_RESET_DELAY
364*4882a593Smuzhiyun 	udelay(CONFIG_PHY_RESET_DELAY);	/* Intel LXT971A needs this */
365*4882a593Smuzhiyun #endif
366*4882a593Smuzhiyun 	/*
367*4882a593Smuzhiyun 	 * Poll the control register for the reset bit to go to 0 (it is
368*4882a593Smuzhiyun 	 * auto-clearing).  This should happen within 0.5 seconds per the
369*4882a593Smuzhiyun 	 * IEEE spec.
370*4882a593Smuzhiyun 	 */
371*4882a593Smuzhiyun 	reg = 0x8000;
372*4882a593Smuzhiyun 	while (((reg & 0x8000) != 0) && timeout--) {
373*4882a593Smuzhiyun 		if (miiphy_read(devname, addr, MII_BMCR, &reg) != 0) {
374*4882a593Smuzhiyun 			debug("PHY status read failed\n");
375*4882a593Smuzhiyun 			return -1;
376*4882a593Smuzhiyun 		}
377*4882a593Smuzhiyun 		udelay(1000);
378*4882a593Smuzhiyun 	}
379*4882a593Smuzhiyun 	if ((reg & 0x8000) == 0) {
380*4882a593Smuzhiyun 		return 0;
381*4882a593Smuzhiyun 	} else {
382*4882a593Smuzhiyun 		puts("PHY reset timed out\n");
383*4882a593Smuzhiyun 		return -1;
384*4882a593Smuzhiyun 	}
385*4882a593Smuzhiyun 	return 0;
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun #endif /* !PHYLIB */
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun /*****************************************************************************
390*4882a593Smuzhiyun  *
391*4882a593Smuzhiyun  * Determine the ethernet speed (10/100/1000).  Return 10 on error.
392*4882a593Smuzhiyun  */
miiphy_speed(const char * devname,unsigned char addr)393*4882a593Smuzhiyun int miiphy_speed(const char *devname, unsigned char addr)
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun 	u16 bmcr, anlpar, adv;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun #if defined(CONFIG_PHY_GIGE)
398*4882a593Smuzhiyun 	u16 btsr;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	/*
401*4882a593Smuzhiyun 	 * Check for 1000BASE-X.  If it is supported, then assume that the speed
402*4882a593Smuzhiyun 	 * is 1000.
403*4882a593Smuzhiyun 	 */
404*4882a593Smuzhiyun 	if (miiphy_is_1000base_x(devname, addr))
405*4882a593Smuzhiyun 		return _1000BASET;
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	/*
408*4882a593Smuzhiyun 	 * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set.
409*4882a593Smuzhiyun 	 */
410*4882a593Smuzhiyun 	/* Check for 1000BASE-T. */
411*4882a593Smuzhiyun 	if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) {
412*4882a593Smuzhiyun 		printf("PHY 1000BT status");
413*4882a593Smuzhiyun 		goto miiphy_read_failed;
414*4882a593Smuzhiyun 	}
415*4882a593Smuzhiyun 	if (btsr != 0xFFFF &&
416*4882a593Smuzhiyun 			(btsr & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)))
417*4882a593Smuzhiyun 		return _1000BASET;
418*4882a593Smuzhiyun #endif /* CONFIG_PHY_GIGE */
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	/* Check Basic Management Control Register first. */
421*4882a593Smuzhiyun 	if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) {
422*4882a593Smuzhiyun 		printf("PHY speed");
423*4882a593Smuzhiyun 		goto miiphy_read_failed;
424*4882a593Smuzhiyun 	}
425*4882a593Smuzhiyun 	/* Check if auto-negotiation is on. */
426*4882a593Smuzhiyun 	if (bmcr & BMCR_ANENABLE) {
427*4882a593Smuzhiyun 		/* Get auto-negotiation results. */
428*4882a593Smuzhiyun 		if (miiphy_read(devname, addr, MII_LPA, &anlpar)) {
429*4882a593Smuzhiyun 			printf("PHY AN speed");
430*4882a593Smuzhiyun 			goto miiphy_read_failed;
431*4882a593Smuzhiyun 		}
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 		if (miiphy_read(devname, addr, MII_ADVERTISE, &adv)) {
434*4882a593Smuzhiyun 			puts("PHY AN adv speed");
435*4882a593Smuzhiyun 			goto miiphy_read_failed;
436*4882a593Smuzhiyun 		}
437*4882a593Smuzhiyun 		return ((anlpar & adv) & LPA_100) ? _100BASET : _10BASET;
438*4882a593Smuzhiyun 	}
439*4882a593Smuzhiyun 	/* Get speed from basic control settings. */
440*4882a593Smuzhiyun 	return (bmcr & BMCR_SPEED100) ? _100BASET : _10BASET;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun miiphy_read_failed:
443*4882a593Smuzhiyun 	printf(" read failed, assuming 10BASE-T\n");
444*4882a593Smuzhiyun 	return _10BASET;
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun /*****************************************************************************
448*4882a593Smuzhiyun  *
449*4882a593Smuzhiyun  * Determine full/half duplex.  Return half on error.
450*4882a593Smuzhiyun  */
miiphy_duplex(const char * devname,unsigned char addr)451*4882a593Smuzhiyun int miiphy_duplex(const char *devname, unsigned char addr)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun 	u16 bmcr, anlpar, adv;
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun #if defined(CONFIG_PHY_GIGE)
456*4882a593Smuzhiyun 	u16 btsr;
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	/* Check for 1000BASE-X. */
459*4882a593Smuzhiyun 	if (miiphy_is_1000base_x(devname, addr)) {
460*4882a593Smuzhiyun 		/* 1000BASE-X */
461*4882a593Smuzhiyun 		if (miiphy_read(devname, addr, MII_LPA, &anlpar)) {
462*4882a593Smuzhiyun 			printf("1000BASE-X PHY AN duplex");
463*4882a593Smuzhiyun 			goto miiphy_read_failed;
464*4882a593Smuzhiyun 		}
465*4882a593Smuzhiyun 	}
466*4882a593Smuzhiyun 	/*
467*4882a593Smuzhiyun 	 * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set.
468*4882a593Smuzhiyun 	 */
469*4882a593Smuzhiyun 	/* Check for 1000BASE-T. */
470*4882a593Smuzhiyun 	if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) {
471*4882a593Smuzhiyun 		printf("PHY 1000BT status");
472*4882a593Smuzhiyun 		goto miiphy_read_failed;
473*4882a593Smuzhiyun 	}
474*4882a593Smuzhiyun 	if (btsr != 0xFFFF) {
475*4882a593Smuzhiyun 		if (btsr & PHY_1000BTSR_1000FD) {
476*4882a593Smuzhiyun 			return FULL;
477*4882a593Smuzhiyun 		} else if (btsr & PHY_1000BTSR_1000HD) {
478*4882a593Smuzhiyun 			return HALF;
479*4882a593Smuzhiyun 		}
480*4882a593Smuzhiyun 	}
481*4882a593Smuzhiyun #endif /* CONFIG_PHY_GIGE */
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	/* Check Basic Management Control Register first. */
484*4882a593Smuzhiyun 	if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) {
485*4882a593Smuzhiyun 		puts("PHY duplex");
486*4882a593Smuzhiyun 		goto miiphy_read_failed;
487*4882a593Smuzhiyun 	}
488*4882a593Smuzhiyun 	/* Check if auto-negotiation is on. */
489*4882a593Smuzhiyun 	if (bmcr & BMCR_ANENABLE) {
490*4882a593Smuzhiyun 		/* Get auto-negotiation results. */
491*4882a593Smuzhiyun 		if (miiphy_read(devname, addr, MII_LPA, &anlpar)) {
492*4882a593Smuzhiyun 			puts("PHY AN duplex");
493*4882a593Smuzhiyun 			goto miiphy_read_failed;
494*4882a593Smuzhiyun 		}
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 		if (miiphy_read(devname, addr, MII_ADVERTISE, &adv)) {
497*4882a593Smuzhiyun 			puts("PHY AN adv duplex");
498*4882a593Smuzhiyun 			goto miiphy_read_failed;
499*4882a593Smuzhiyun 		}
500*4882a593Smuzhiyun 		return ((anlpar & adv) & (LPA_10FULL | LPA_100FULL)) ?
501*4882a593Smuzhiyun 		    FULL : HALF;
502*4882a593Smuzhiyun 	}
503*4882a593Smuzhiyun 	/* Get speed from basic control settings. */
504*4882a593Smuzhiyun 	return (bmcr & BMCR_FULLDPLX) ? FULL : HALF;
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun miiphy_read_failed:
507*4882a593Smuzhiyun 	printf(" read failed, assuming half duplex\n");
508*4882a593Smuzhiyun 	return HALF;
509*4882a593Smuzhiyun }
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun /*****************************************************************************
512*4882a593Smuzhiyun  *
513*4882a593Smuzhiyun  * Return 1 if PHY supports 1000BASE-X, 0 if PHY supports 10BASE-T/100BASE-TX/
514*4882a593Smuzhiyun  * 1000BASE-T, or on error.
515*4882a593Smuzhiyun  */
miiphy_is_1000base_x(const char * devname,unsigned char addr)516*4882a593Smuzhiyun int miiphy_is_1000base_x(const char *devname, unsigned char addr)
517*4882a593Smuzhiyun {
518*4882a593Smuzhiyun #if defined(CONFIG_PHY_GIGE)
519*4882a593Smuzhiyun 	u16 exsr;
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 	if (miiphy_read(devname, addr, MII_ESTATUS, &exsr)) {
522*4882a593Smuzhiyun 		printf("PHY extended status read failed, assuming no "
523*4882a593Smuzhiyun 			"1000BASE-X\n");
524*4882a593Smuzhiyun 		return 0;
525*4882a593Smuzhiyun 	}
526*4882a593Smuzhiyun 	return 0 != (exsr & (ESTATUS_1000XF | ESTATUS_1000XH));
527*4882a593Smuzhiyun #else
528*4882a593Smuzhiyun 	return 0;
529*4882a593Smuzhiyun #endif
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun #ifdef CONFIG_SYS_FAULT_ECHO_LINK_DOWN
533*4882a593Smuzhiyun /*****************************************************************************
534*4882a593Smuzhiyun  *
535*4882a593Smuzhiyun  * Determine link status
536*4882a593Smuzhiyun  */
miiphy_link(const char * devname,unsigned char addr)537*4882a593Smuzhiyun int miiphy_link(const char *devname, unsigned char addr)
538*4882a593Smuzhiyun {
539*4882a593Smuzhiyun 	unsigned short reg;
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun 	/* dummy read; needed to latch some phys */
542*4882a593Smuzhiyun 	(void)miiphy_read(devname, addr, MII_BMSR, &reg);
543*4882a593Smuzhiyun 	if (miiphy_read(devname, addr, MII_BMSR, &reg)) {
544*4882a593Smuzhiyun 		puts("MII_BMSR read failed, assuming no link\n");
545*4882a593Smuzhiyun 		return 0;
546*4882a593Smuzhiyun 	}
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	/* Determine if a link is active */
549*4882a593Smuzhiyun 	if ((reg & BMSR_LSTATUS) != 0) {
550*4882a593Smuzhiyun 		return 1;
551*4882a593Smuzhiyun 	} else {
552*4882a593Smuzhiyun 		return 0;
553*4882a593Smuzhiyun 	}
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun #endif
556