1 /**
2  * @file IxEthMii.c
3  *
4  * @author Intel Corporation
5  * @date
6  *
7  * @brief  MII control functions
8  *
9  * Design Notes:
10  *
11  *
12  * @par
13  * IXP400 SW Release version 2.0
14  *
15  * -- Copyright Notice --
16  *
17  * @par
18  * Copyright 2001-2005, Intel Corporation.
19  * All rights reserved.
20  *
21  * @par
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the above copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  * 3. Neither the name of the Intel Corporation nor the names of its contributors
31  *    may be used to endorse or promote products derived from this software
32  *    without specific prior written permission.
33  *
34  * @par
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
36  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
39  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45  * SUCH DAMAGE.
46  *
47  * @par
48  * -- End of Copyright Notice --
49  */
50 
51 #include "IxOsal.h"
52 
53 #include "IxEthAcc.h"
54 #include "IxEthMii_p.h"
55 
56 #ifdef __wince
57 #include "IxOsPrintf.h"
58 #endif
59 
60 /* Array to store the phy IDs of the discovered phys */
61 PRIVATE UINT32 ixEthMiiPhyId[IXP425_ETH_ACC_MII_MAX_ADDR];
62 
63 /*********************************************************
64  *
65  * Scan for PHYs on the MII bus. This function returns
66  * an array of booleans, one for each PHY address.
67  * If a PHY is found at a particular address, the
68  * corresponding entry in the array is set to TRUE.
69  *
70  */
71 
72 PUBLIC IX_STATUS
ixEthMiiPhyScan(BOOL phyPresent[],UINT32 maxPhyCount)73 ixEthMiiPhyScan(BOOL phyPresent[], UINT32 maxPhyCount)
74 {
75     UINT32 i;
76     UINT16 regval, regvalId1, regvalId2;
77 
78     /*Search for PHYs on the MII*/
79     /*Search for existant phys on the MDIO bus*/
80 
81     if ((phyPresent == NULL) ||
82 	(maxPhyCount > IXP425_ETH_ACC_MII_MAX_ADDR))
83     {
84 	return IX_FAIL;
85     }
86 
87     /* fill the array */
88     for(i=0;
89         i<IXP425_ETH_ACC_MII_MAX_ADDR;
90 	i++)
91     {
92 	phyPresent[i] = FALSE;
93     }
94 
95     /* iterate through the PHY addresses */
96     for(i=0;
97 	maxPhyCount > 0 && i<IXP425_ETH_ACC_MII_MAX_ADDR;
98 	i++)
99     {
100 	ixEthMiiPhyId[i] = IX_ETH_MII_INVALID_PHY_ID;
101 	if(ixEthAccMiiReadRtn(i,
102 			      IX_ETH_MII_CTRL_REG,
103 			      &regval) == IX_ETH_ACC_SUCCESS)
104 	{
105 	    if((regval & 0xffff) != 0xffff)
106 	    {
107 		maxPhyCount--;
108 		/*Need to read the register twice here to flush PHY*/
109 		ixEthAccMiiReadRtn(i,  IX_ETH_MII_PHY_ID1_REG, &regvalId1);
110 		ixEthAccMiiReadRtn(i,  IX_ETH_MII_PHY_ID1_REG, &regvalId1);
111 		ixEthAccMiiReadRtn(i,  IX_ETH_MII_PHY_ID2_REG, &regvalId2);
112 		ixEthMiiPhyId[i] = (regvalId1 << IX_ETH_MII_REG_SHL) | regvalId2;
113 		if ((ixEthMiiPhyId[i] == IX_ETH_MII_KS8995_PHY_ID)
114 		    || (ixEthMiiPhyId[i] == IX_ETH_MII_LXT971_PHY_ID)
115 		    || (ixEthMiiPhyId[i] == IX_ETH_MII_LXT972_PHY_ID)
116 		    || (ixEthMiiPhyId[i] == IX_ETH_MII_LXT973_PHY_ID)
117 		    || (ixEthMiiPhyId[i] == IX_ETH_MII_LXT973A3_PHY_ID)
118 		    || (ixEthMiiPhyId[i] == IX_ETH_MII_LXT9785_PHY_ID)
119 		    )
120 		{
121 		    /* supported phy */
122 		    phyPresent[i] = TRUE;
123 		} /* end of if(ixEthMiiPhyId) */
124 		else
125 		{
126 		    if (ixEthMiiPhyId[i] != IX_ETH_MII_INVALID_PHY_ID)
127 		    {
128 			/* unsupported phy */
129                         ixOsalLog (IX_OSAL_LOG_LVL_ERROR,
130                                    IX_OSAL_LOG_DEV_STDOUT,
131 				    "ixEthMiiPhyScan : unexpected Mii PHY ID %8.8x\n",
132 				    ixEthMiiPhyId[i], 2, 3, 4, 5, 6);
133 			ixEthMiiPhyId[i] = IX_ETH_MII_UNKNOWN_PHY_ID;
134 			phyPresent[i] = TRUE;
135 		    }
136 		}
137 	    }
138 	}
139     }
140     return IX_SUCCESS;
141 }
142 
143 /************************************************************
144  *
145  * Configure the PHY at the specified address
146  *
147  */
148 PUBLIC IX_STATUS
ixEthMiiPhyConfig(UINT32 phyAddr,BOOL speed100,BOOL fullDuplex,BOOL autonegotiate)149 ixEthMiiPhyConfig(UINT32 phyAddr,
150 		  BOOL speed100,
151 		  BOOL fullDuplex,
152 		  BOOL autonegotiate)
153 {
154     UINT16 regval=0;
155 
156     /* parameter check */
157     if ((phyAddr < IXP425_ETH_ACC_MII_MAX_ADDR) &&
158 	(ixEthMiiPhyId[phyAddr] != IX_ETH_MII_INVALID_PHY_ID))
159     {
160     /*
161      * set the control register
162      */
163 	if(autonegotiate)
164 	{
165 	    regval |= IX_ETH_MII_CR_AUTO_EN | IX_ETH_MII_CR_RESTART;
166 	}
167 	else
168 	{
169 	    if(speed100)
170 	    {
171 		regval |= IX_ETH_MII_CR_100;
172 	    }
173 	    if(fullDuplex)
174 	    {
175 		regval |= IX_ETH_MII_CR_FDX;
176 	    }
177 	} /* end of if-else() */
178 	if (ixEthAccMiiWriteRtn(phyAddr,
179 				IX_ETH_MII_CTRL_REG,
180 				regval) == IX_ETH_ACC_SUCCESS)
181 	{
182 	    return IX_SUCCESS;
183 	}
184     } /* end of if(phyAddr) */
185     return IX_FAIL;
186 }
187 
188 /******************************************************************
189  *
190  *  Enable the PHY Loopback at the specified address
191  */
192 PUBLIC IX_STATUS
ixEthMiiPhyLoopbackEnable(UINT32 phyAddr)193 ixEthMiiPhyLoopbackEnable (UINT32 phyAddr)
194 {
195   UINT16 regval ;
196 
197   if ((phyAddr < IXP425_ETH_ACC_MII_MAX_ADDR) &&
198       (IX_ETH_MII_INVALID_PHY_ID != ixEthMiiPhyId[phyAddr]))
199   {
200       /* read/write the control register */
201       if(ixEthAccMiiReadRtn (phyAddr,
202 			     IX_ETH_MII_CTRL_REG,
203 			     &regval)
204 	 == IX_ETH_ACC_SUCCESS)
205       {
206 	  if(ixEthAccMiiWriteRtn (phyAddr,
207 				  IX_ETH_MII_CTRL_REG,
208 				  regval | IX_ETH_MII_CR_LOOPBACK)
209 	     == IX_ETH_ACC_SUCCESS)
210 	  {
211 	      return IX_SUCCESS;
212 	  }
213       }
214   }
215   return IX_FAIL;
216 }
217 
218 /******************************************************************
219  *
220  *  Disable the PHY Loopback at the specified address
221  */
222 PUBLIC IX_STATUS
ixEthMiiPhyLoopbackDisable(UINT32 phyAddr)223 ixEthMiiPhyLoopbackDisable (UINT32 phyAddr)
224 {
225   UINT16 regval ;
226 
227   if ((phyAddr < IXP425_ETH_ACC_MII_MAX_ADDR) &&
228       (IX_ETH_MII_INVALID_PHY_ID != ixEthMiiPhyId[phyAddr]))
229   {
230       /* read/write the control register */
231       if(ixEthAccMiiReadRtn (phyAddr,
232 			     IX_ETH_MII_CTRL_REG,
233 			     &regval)
234 	 == IX_ETH_ACC_SUCCESS)
235       {
236 	  if(ixEthAccMiiWriteRtn (phyAddr,
237 				  IX_ETH_MII_CTRL_REG,
238 				  regval & (~IX_ETH_MII_CR_LOOPBACK))
239 	     == IX_ETH_ACC_SUCCESS)
240 	  {
241 	      return IX_SUCCESS;
242 	  }
243       }
244   }
245   return IX_FAIL;
246 }
247 
248 /******************************************************************
249  *
250  *  Reset the PHY at the specified address
251  */
252 PUBLIC IX_STATUS
ixEthMiiPhyReset(UINT32 phyAddr)253 ixEthMiiPhyReset(UINT32 phyAddr)
254 {
255     UINT32 timeout;
256     UINT16 regval;
257 
258     if ((phyAddr < IXP425_ETH_ACC_MII_MAX_ADDR) &&
259 	(ixEthMiiPhyId[phyAddr] != IX_ETH_MII_INVALID_PHY_ID))
260     {
261 	if ((ixEthMiiPhyId[phyAddr] == IX_ETH_MII_LXT971_PHY_ID)	||
262 	    (ixEthMiiPhyId[phyAddr] == IX_ETH_MII_LXT972_PHY_ID)	||
263 	    (ixEthMiiPhyId[phyAddr] == IX_ETH_MII_LXT973_PHY_ID)	||
264 	    (ixEthMiiPhyId[phyAddr] == IX_ETH_MII_LXT973A3_PHY_ID)	||
265 		(ixEthMiiPhyId[phyAddr] == IX_ETH_MII_LXT9785_PHY_ID)
266 	    )
267 	{
268 	    /* use the control register to reset the phy */
269 	    ixEthAccMiiWriteRtn(phyAddr,
270 				IX_ETH_MII_CTRL_REG,
271 				IX_ETH_MII_CR_RESET);
272 
273 	    /* poll until the reset bit is cleared */
274 	    timeout = 0;
275 	    do
276 	    {
277 		ixOsalSleep (IX_ETH_MII_RESET_POLL_MS);
278 
279 		/* read the control register and check for timeout */
280 		ixEthAccMiiReadRtn(phyAddr,
281 				   IX_ETH_MII_CTRL_REG,
282 				   &regval);
283 		if ((regval & IX_ETH_MII_CR_RESET) == 0)
284 		{
285 		    /* timeout bit is self-cleared */
286 		    break;
287 		}
288 		timeout += IX_ETH_MII_RESET_POLL_MS;
289 	    }
290 	    while (timeout < IX_ETH_MII_RESET_DELAY_MS);
291 
292 	    /* check for timeout */
293 	    if (timeout >= IX_ETH_MII_RESET_DELAY_MS)
294 	    {
295 		ixEthAccMiiWriteRtn(phyAddr, IX_ETH_MII_CTRL_REG,
296 				    IX_ETH_MII_CR_NORM_EN);
297 		return IX_FAIL;
298 	    }
299 
300 	    return IX_SUCCESS;
301 	} /* end of if(ixEthMiiPhyId) */
302 	else if (ixEthMiiPhyId[phyAddr] == IX_ETH_MII_KS8995_PHY_ID)
303 	{
304 	    /* reset bit is reserved, just reset the control register */
305 	    ixEthAccMiiWriteRtn(phyAddr, IX_ETH_MII_CTRL_REG,
306 				IX_ETH_MII_CR_NORM_EN);
307 	    return IX_SUCCESS;
308 	}
309 	else
310 	{
311 	    /* unknown PHY, set the control register reset bit,
312 	     * wait 2 s. and clear the control register.
313 	     */
314 	    ixEthAccMiiWriteRtn(phyAddr, IX_ETH_MII_CTRL_REG,
315 				IX_ETH_MII_CR_RESET);
316 
317 	    ixOsalSleep (IX_ETH_MII_RESET_DELAY_MS);
318 
319 	    ixEthAccMiiWriteRtn(phyAddr, IX_ETH_MII_CTRL_REG,
320 				IX_ETH_MII_CR_NORM_EN);
321 	    return IX_SUCCESS;
322 	} /* end of if-else(ixEthMiiPhyId) */
323     } /* end of if(phyAddr) */
324     return IX_FAIL;
325 }
326 
327 /*****************************************************************
328  *
329  *  Link state query functions
330  */
331 
332 PUBLIC IX_STATUS
ixEthMiiLinkStatus(UINT32 phyAddr,BOOL * linkUp,BOOL * speed100,BOOL * fullDuplex,BOOL * autoneg)333 ixEthMiiLinkStatus(UINT32 phyAddr,
334            BOOL *linkUp,
335            BOOL *speed100,
336            BOOL *fullDuplex,
337            BOOL *autoneg)
338 {
339     UINT16 ctrlRegval, statRegval, regval, regval4, regval5;
340 
341     /* check the parameters */
342     if ((linkUp == NULL) ||
343 	(speed100 == NULL) ||
344 	(fullDuplex == NULL) ||
345 	(autoneg == NULL))
346     {
347 	return IX_FAIL;
348     }
349 
350     *linkUp = FALSE;
351     *speed100 = FALSE;
352     *fullDuplex = FALSE;
353     *autoneg = FALSE;
354 
355     if ((phyAddr < IXP425_ETH_ACC_MII_MAX_ADDR) &&
356 	(ixEthMiiPhyId[phyAddr] != IX_ETH_MII_INVALID_PHY_ID))
357     {
358 	if ((ixEthMiiPhyId[phyAddr] == IX_ETH_MII_LXT971_PHY_ID)	||
359 	    (ixEthMiiPhyId[phyAddr] == IX_ETH_MII_LXT972_PHY_ID)	||
360 	    (ixEthMiiPhyId[phyAddr] == IX_ETH_MII_LXT9785_PHY_ID)
361 		)
362 	{
363 	    /* --------------------------------------------------*/
364 	    /* Retrieve information from PHY specific register   */
365 	    /* --------------------------------------------------*/
366 	    if (ixEthAccMiiReadRtn(phyAddr,
367 				   IX_ETH_MII_STAT2_REG,
368 				   &regval) != IX_ETH_ACC_SUCCESS)
369 	    {
370 		return IX_FAIL;
371 	    }
372 	    *linkUp = ((regval & IX_ETH_MII_SR2_LINK) != 0);
373 	    *speed100 = ((regval & IX_ETH_MII_SR2_100) != 0);
374 	    *fullDuplex = ((regval & IX_ETH_MII_SR2_FD) != 0);
375 	    *autoneg = ((regval & IX_ETH_MII_SR2_AUTO) != 0);
376 	    return IX_SUCCESS;
377 	} /* end of if(ixEthMiiPhyId) */
378 	else
379 	{
380 	    /* ----------------------------------------------------*/
381 	    /* Retrieve information from status and ctrl registers */
382 	    /* ----------------------------------------------------*/
383 	    if (ixEthAccMiiReadRtn(phyAddr,
384 				   IX_ETH_MII_CTRL_REG,
385 				   &ctrlRegval) != IX_ETH_ACC_SUCCESS)
386 	    {
387 		return IX_FAIL;
388 	    }
389 	    ixEthAccMiiReadRtn(phyAddr,  IX_ETH_MII_STAT_REG, &statRegval);
390 
391 	    *linkUp = ((statRegval & IX_ETH_MII_SR_LINK_STATUS) != 0);
392 	    if (*linkUp)
393 	    {
394 		*autoneg = ((ctrlRegval & IX_ETH_MII_CR_AUTO_EN) != 0) &&
395 		    ((statRegval &  IX_ETH_MII_SR_AUTO_SEL) != 0) &&
396 		    ((statRegval & IX_ETH_MII_SR_AUTO_NEG) != 0);
397 
398 		if (*autoneg)
399 		{
400 		    /* mask the current stat values with the capabilities */
401 		    ixEthAccMiiReadRtn(phyAddr, IX_ETH_MII_AN_ADS_REG, &regval4);
402 		    ixEthAccMiiReadRtn(phyAddr, IX_ETH_MII_AN_PRTN_REG, &regval5);
403 		    /* merge the flags from the 3 registers */
404 		    regval = (statRegval & ((regval4 & regval5) << 6));
405 		    /* initialise from status register values */
406 		    if ((regval & IX_ETH_MII_SR_TX_FULL_DPX) != 0)
407 		    {
408 			/* 100 Base X full dplx */
409 			*speed100 = TRUE;
410 			*fullDuplex = TRUE;
411 			return IX_SUCCESS;
412 		    }
413 		    if ((regval & IX_ETH_MII_SR_TX_HALF_DPX) != 0)
414 		    {
415 			/* 100 Base X half dplx */
416 			*speed100 = TRUE;
417 			return IX_SUCCESS;
418 		    }
419 		    if ((regval & IX_ETH_MII_SR_10T_FULL_DPX) != 0)
420 		    {
421 			/* 10 mb full dplx */
422 			*fullDuplex = TRUE;
423 			return IX_SUCCESS;
424 		    }
425 		    if ((regval & IX_ETH_MII_SR_10T_HALF_DPX) != 0)
426 		    {
427 			/* 10 mb half dplx */
428 			return IX_SUCCESS;
429 		    }
430 		} /* end of if(autoneg) */
431 		else
432 		{
433 		    /* autonegotiate not complete, return setup parameters */
434 		    *speed100 = ((ctrlRegval & IX_ETH_MII_CR_100) != 0);
435 		    *fullDuplex = ((ctrlRegval & IX_ETH_MII_CR_FDX) != 0);
436 		}
437 	    } /* end of if(linkUp) */
438 	} /* end of if-else(ixEthMiiPhyId) */
439     } /* end of if(phyAddr) */
440     else
441     {
442 	return IX_FAIL;
443     } /* end of if-else(phyAddr) */
444     return IX_SUCCESS;
445 }
446 
447 /*****************************************************************
448  *
449  *  Link state display functions
450  */
451 
452 PUBLIC IX_STATUS
ixEthMiiPhyShow(UINT32 phyAddr)453 ixEthMiiPhyShow (UINT32 phyAddr)
454 {
455     BOOL linkUp, speed100, fullDuplex, autoneg;
456     UINT16 cregval;
457     UINT16 sregval;
458 
459 
460     ixEthAccMiiReadRtn(phyAddr,  IX_ETH_MII_STAT_REG, &sregval);
461     ixEthAccMiiReadRtn(phyAddr,  IX_ETH_MII_CTRL_REG, &cregval);
462 
463     /* get link information */
464     if (ixEthMiiLinkStatus(phyAddr,
465 			   &linkUp,
466 			   &speed100,
467 			   &fullDuplex,
468 			   &autoneg) != IX_ETH_ACC_SUCCESS)
469     {
470 	printf("PHY Status unknown\n");
471 	return IX_FAIL;
472     }
473 
474     printf("PHY ID [phyAddr]: %8.8x\n",ixEthMiiPhyId[phyAddr]);
475     printf( " Status reg:  %4.4x\n",sregval);
476     printf( " control reg: %4.4x\n",cregval);
477     /* display link information */
478     printf("PHY Status:\n");
479     printf("    Link is %s\n",
480 	   (linkUp ? "Up" : "Down"));
481     if((sregval & IX_ETH_MII_SR_REMOTE_FAULT) != 0)
482     {
483 	printf("    Remote fault detected\n");
484     }
485     printf("    Auto Negotiation %s\n",
486 	   (autoneg ? "Completed" : "Not Completed"));
487 
488     printf("PHY Configuration:\n");
489     printf("    Speed %sMb/s\n",
490 	   (speed100 ? "100" : "10"));
491     printf("    %s Duplex\n",
492 	   (fullDuplex ? "Full" : "Half"));
493     printf("    Auto Negotiation %s\n",
494 	   (autoneg ? "Enabled" : "Disabled"));
495     return IX_SUCCESS;
496 }
497 
498