xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/Common/phyHandling.c (revision b23fa86ac476770d3224c07213bec32f5b1628bd)
1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 /**
29  * @brief
30  * Handling of Ethernet PHY's
31  * PHY's communicate with an EMAC either through
32  * a Media-Independent Interface (MII), or a Reduced Media-Independent Interface (RMII).
33  * The EMAC can poll for PHY ports on 32 different addresses. Each of the PHY ports
34  * shall be treated independently.
35  *
36  */
37 
38 /* Standard includes. */
39 #include <stdint.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 
43 /* FreeRTOS includes. */
44 #include "FreeRTOS.h"
45 #include "task.h"
46 #include "queue.h"
47 #include "semphr.h"
48 
49 /* FreeRTOS+TCP includes. */
50 #include "FreeRTOS_IP.h"
51 #include "FreeRTOS_Sockets.h"
52 
53 #include "phyHandling.h"
54 
55 #define phyMIN_PHY_ADDRESS    0
56 #define phyMAX_PHY_ADDRESS    31
57 
58 #if defined( PHY_LS_HIGH_CHECK_TIME_MS ) || defined( PHY_LS_LOW_CHECK_TIME_MS )
59     #error please use the new defines with 'ipconfig' prefix
60 #endif
61 
62 #ifndef ipconfigPHY_LS_HIGH_CHECK_TIME_MS
63 
64 /* Check if the LinkStatus in the PHY is still high after 15 seconds of not
65  * receiving packets. */
66     #define ipconfigPHY_LS_HIGH_CHECK_TIME_MS    15000U
67 #endif
68 
69 #ifndef ipconfigPHY_LS_LOW_CHECK_TIME_MS
70     /* Check if the LinkStatus in the PHY is still low every second. */
71     #define ipconfigPHY_LS_LOW_CHECK_TIME_MS    1000U
72 #endif
73 
74 /* As the following 3 macro's are OK in most situations, and so they're not
75  * included in 'FreeRTOSIPConfigDefaults.h'.
76  * Users can change their values in the project's 'FreeRTOSIPConfig.h'. */
77 #ifndef phyPHY_MAX_RESET_TIME_MS
78     #define phyPHY_MAX_RESET_TIME_MS    1000U
79 #endif
80 
81 #ifndef phyPHY_MAX_NEGOTIATE_TIME_MS
82     #define phyPHY_MAX_NEGOTIATE_TIME_MS    3000U
83 #endif
84 
85 #ifndef phySHORT_DELAY_MS
86     #define phySHORT_DELAY_MS    50U
87 #endif
88 
89 /* Naming and numbering of basic PHY registers. */
90 #define phyREG_00_BMCR             0x00U    /* Basic Mode Control Register. */
91 #define phyREG_01_BMSR             0x01U    /* Basic Mode Status Register. */
92 #define phyREG_02_PHYSID1          0x02U    /* PHYS ID 1 */
93 #define phyREG_03_PHYSID2          0x03U    /* PHYS ID 2 */
94 #define phyREG_04_ADVERTISE        0x04U    /* Advertisement control reg */
95 
96 /* Naming and numbering of extended PHY registers. */
97 #define PHYREG_10_PHYSTS           0x10U    /* 16 PHY status register Offset */
98 #define phyREG_19_PHYCR            0x19U    /* 25 RW PHY Control Register */
99 #define phyREG_1F_PHYSPCS          0x1FU    /* 31 RW PHY Special Control Status */
100 
101 /* Bit fields for 'phyREG_00_BMCR', the 'Basic Mode Control Register'. */
102 #define phyBMCR_FULL_DUPLEX        0x0100U  /* Full duplex. */
103 #define phyBMCR_AN_RESTART         0x0200U  /* Auto negotiation restart. */
104 #define phyBMCR_ISOLATE            0x0400U  /* 1 = Isolates 0 = Normal operation. */
105 #define phyBMCR_AN_ENABLE          0x1000U  /* Enable auto negotiation. */
106 #define phyBMCR_SPEED_100          0x2000U  /* Select 100Mbps. */
107 #define phyBMCR_RESET              0x8000U  /* Reset the PHY. */
108 
109 /* Bit fields for 'phyREG_19_PHYCR', the 'PHY Control Register'. */
110 #define PHYCR_MDIX_EN              0x8000U  /* Enable Auto MDIX. */
111 #define PHYCR_MDIX_FORCE           0x4000U  /* Force MDIX crossed. */
112 
113 #define phyBMSR_AN_COMPLETE        0x0020U  /* Auto-Negotiation process completed */
114 
115 #define phyBMSR_LINK_STATUS        0x0004U
116 
117 #define phyPHYSTS_LINK_STATUS      0x0001U  /* PHY Link mask */
118 #define phyPHYSTS_SPEED_STATUS     0x0002U  /* PHY Speed mask */
119 #define phyPHYSTS_DUPLEX_STATUS    0x0004U  /* PHY Duplex mask */
120 
121 /* Bit fields for 'phyREG_1F_PHYSPCS
122  *  001 = 10BASE-T half-duplex
123  *  101 = 10BASE-T full-duplex
124  *  010 = 100BASE-TX half-duplex
125  *  110 = 100BASE-TX full-duplex
126  */
127 #define phyPHYSPCS_SPEED_MASK      0x000CU
128 #define phyPHYSPCS_SPEED_10        0x0004U
129 #define phyPHYSPCS_FULL_DUPLEX     0x0010U
130 
131 /*
132  * Description of all capabilities that can be advertised to
133  * the peer (usually a switch or router).
134  */
135 
136 #define phyADVERTISE_CSMA       0x0001U     /* Supports IEEE 802.3u: Fast Ethernet at 100 Mbit/s */
137 #define phyADVERTISE_10HALF     0x0020U     /* Try for 10mbps half-duplex. */
138 #define phyADVERTISE_10FULL     0x0040U     /* Try for 10mbps full-duplex. */
139 #define phyADVERTISE_100HALF    0x0080U     /* Try for 100mbps half-duplex. */
140 #define phyADVERTISE_100FULL    0x0100U     /* Try for 100mbps full-duplex. */
141 
142 #define phyADVERTISE_ALL                            \
143     ( phyADVERTISE_10HALF | phyADVERTISE_10FULL |   \
144       phyADVERTISE_100HALF | phyADVERTISE_100FULL | \
145       phyADVERTISE_CSMA )
146 
147 /* Send a reset command to a set of PHY-ports. */
148 static uint32_t xPhyReset( EthernetPhy_t * pxPhyObject,
149                            uint32_t ulPhyMask );
150 
xHas_1F_PHYSPCS(uint32_t ulPhyID)151 static BaseType_t xHas_1F_PHYSPCS( uint32_t ulPhyID )
152 {
153     BaseType_t xResult = pdFALSE;
154 
155     switch( ulPhyID )
156     {
157         case PHY_ID_LAN8720:
158         case PHY_ID_LAN8742A:
159         case PHY_ID_KSZ8041:
160 
161         /*
162          *      case PHY_ID_KSZ8051: // same ID as 8041
163          *      case PHY_ID_KSZ8081: // same ID as 8041
164          */
165         case PHY_ID_KSZ8081MNXIA:
166 
167         case PHY_ID_KSZ8863:
168         default:
169             /* Most PHY's have a 1F_PHYSPCS */
170             xResult = pdTRUE;
171             break;
172 
173         case PHY_ID_DP83848I:
174         case PHY_ID_DP83TC811S:
175         case PHY_ID_TM4C129X:
176         case PHY_ID_MV88E6071:
177             /* Has no 0x1F register "PHY Special Control Status". */
178             break;
179     }
180 
181     return xResult;
182 }
183 /*-----------------------------------------------------------*/
184 
xHas_19_PHYCR(uint32_t ulPhyID)185 static BaseType_t xHas_19_PHYCR( uint32_t ulPhyID )
186 {
187     BaseType_t xResult = pdFALSE;
188 
189     switch( ulPhyID )
190     {
191         case PHY_ID_LAN8742A:
192         case PHY_ID_DP83848I:
193         case PHY_ID_TM4C129X:
194             xResult = pdTRUE;
195             break;
196 
197         case PHY_ID_MV88E6071: /* Marvell 88E6071 */
198         default:
199             /* Most PHY's do not have a 19_PHYCR */
200             break;
201     }
202 
203     return xResult;
204 }
205 /*-----------------------------------------------------------*/
206 
207 /* Initialise the struct and assign a PHY-read and -write function. */
vPhyInitialise(EthernetPhy_t * pxPhyObject,xApplicationPhyReadHook_t fnPhyRead,xApplicationPhyWriteHook_t fnPhyWrite)208 void vPhyInitialise( EthernetPhy_t * pxPhyObject,
209                      xApplicationPhyReadHook_t fnPhyRead,
210                      xApplicationPhyWriteHook_t fnPhyWrite )
211 {
212     memset( ( void * ) pxPhyObject, 0, sizeof( *pxPhyObject ) );
213 
214     pxPhyObject->fnPhyRead = fnPhyRead;
215     pxPhyObject->fnPhyWrite = fnPhyWrite;
216 }
217 /*-----------------------------------------------------------*/
218 
219 /* Discover all PHY's connected by polling 32 indexes ( zero-based ) */
xPhyDiscover(EthernetPhy_t * pxPhyObject)220 BaseType_t xPhyDiscover( EthernetPhy_t * pxPhyObject )
221 {
222     BaseType_t xPhyAddress;
223 
224     pxPhyObject->xPortCount = 0;
225 
226     for( xPhyAddress = phyMIN_PHY_ADDRESS; xPhyAddress <= phyMAX_PHY_ADDRESS; xPhyAddress++ )
227     {
228         uint32_t ulLowerID = 0U;
229 
230         pxPhyObject->fnPhyRead( xPhyAddress, phyREG_03_PHYSID2, &ulLowerID );
231 
232         /* A valid PHY id can not be all zeros or all ones. */
233         if( ( ulLowerID != ( uint16_t ) ~0U ) && ( ulLowerID != ( uint16_t ) 0U ) )
234         {
235             uint32_t ulUpperID;
236             uint32_t ulPhyID;
237 
238             pxPhyObject->fnPhyRead( xPhyAddress, phyREG_02_PHYSID1, &ulUpperID );
239             ulPhyID = ( ( ( uint32_t ) ulUpperID ) << 16 ) | ( ulLowerID & 0xFFF0U );
240 
241             pxPhyObject->ucPhyIndexes[ pxPhyObject->xPortCount ] = ( uint8_t ) xPhyAddress;
242             pxPhyObject->ulPhyIDs[ pxPhyObject->xPortCount ] = ulPhyID;
243 
244             pxPhyObject->xPortCount++;
245 
246             /* See if there is more storage space. */
247             if( pxPhyObject->xPortCount == ipconfigPHY_MAX_PORTS )
248             {
249                 break;
250             }
251         }
252     }
253 
254     if( pxPhyObject->xPortCount > 0 )
255     {
256         FreeRTOS_printf( ( "PHY ID %X\n", ( unsigned int ) pxPhyObject->ulPhyIDs[ 0 ] ) );
257     }
258 
259     return pxPhyObject->xPortCount;
260 }
261 /*-----------------------------------------------------------*/
262 
263 /* Send a reset command to a set of PHY-ports. */
xPhyReset(EthernetPhy_t * pxPhyObject,uint32_t ulPhyMask)264 static uint32_t xPhyReset( EthernetPhy_t * pxPhyObject,
265                            uint32_t ulPhyMask )
266 {
267     uint32_t ulDoneMask, ulConfig;
268     TickType_t xRemainingTime;
269     TimeOut_t xTimer;
270     BaseType_t xPhyIndex;
271 
272     /* A bit-mask of PHY ports that are ready. */
273     ulDoneMask = 0U;
274 
275     /* Set the RESET bits high. */
276     for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ )
277     {
278         BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
279 
280         /* Read Control register. */
281         pxPhyObject->fnPhyRead( xPhyAddress, phyREG_00_BMCR, &ulConfig );
282         pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, ulConfig | phyBMCR_RESET );
283     }
284 
285     xRemainingTime = ( TickType_t ) pdMS_TO_TICKS( phyPHY_MAX_RESET_TIME_MS );
286     vTaskSetTimeOutState( &xTimer );
287 
288     /* The reset should last less than a second. */
289     for( ; ; )
290     {
291         for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ )
292         {
293             BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
294 
295             pxPhyObject->fnPhyRead( xPhyAddress, phyREG_00_BMCR, &ulConfig );
296 
297             if( ( ulConfig & phyBMCR_RESET ) == 0 )
298             {
299                 FreeRTOS_printf( ( "xPhyReset: phyBMCR_RESET %d ready\n", ( int ) xPhyIndex ) );
300                 ulDoneMask |= ( 1U << xPhyIndex );
301             }
302         }
303 
304         if( ulDoneMask == ulPhyMask )
305         {
306             break;
307         }
308 
309         if( xTaskCheckForTimeOut( &xTimer, &xRemainingTime ) != pdFALSE )
310         {
311             FreeRTOS_printf( ( "xPhyReset: phyBMCR_RESET timed out ( done 0x%02X )\n", ( unsigned int ) ulDoneMask ) );
312             break;
313         }
314 
315         /* Block for a while */
316         vTaskDelay( pdMS_TO_TICKS( phySHORT_DELAY_MS ) );
317     }
318 
319     /* Clear the reset bits. */
320     for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ )
321     {
322         if( ( ulDoneMask & ( 1U << xPhyIndex ) ) == 0U )
323         {
324             BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
325 
326             /* The reset operation timed out, clear the bit manually. */
327             pxPhyObject->fnPhyRead( xPhyAddress, phyREG_00_BMCR, &ulConfig );
328             pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, ulConfig & ~phyBMCR_RESET );
329         }
330     }
331 
332     vTaskDelay( pdMS_TO_TICKS( phySHORT_DELAY_MS ) );
333 
334     return ulDoneMask;
335 }
336 /*-----------------------------------------------------------*/
337 
xPhyConfigure(EthernetPhy_t * pxPhyObject,const PhyProperties_t * pxPhyProperties)338 BaseType_t xPhyConfigure( EthernetPhy_t * pxPhyObject,
339                           const PhyProperties_t * pxPhyProperties )
340 {
341     uint32_t ulConfig, ulAdvertise;
342     BaseType_t xPhyIndex;
343 
344     if( pxPhyObject->xPortCount < 1 )
345     {
346         FreeRTOS_printf( ( "xPhyConfigure: No PHY's detected.\n" ) );
347         return -1;
348     }
349 
350     /* The expected ID for the 'LAN8742A'  is 0x0007c130. */
351     /* The expected ID for the 'LAN8720'   is 0x0007c0f0. */
352     /* The expected ID for the 'DP83848I'  is 0x20005C90. */
353 
354     /* Set advertise register. */
355     if( ( pxPhyProperties->ucSpeed == ( uint8_t ) PHY_SPEED_AUTO ) && ( pxPhyProperties->ucDuplex == ( uint8_t ) PHY_DUPLEX_AUTO ) )
356     {
357         ulAdvertise = phyADVERTISE_ALL;
358         /* Reset auto-negotiation capability. */
359     }
360     else
361     {
362         /* Always select protocol 802.3u. */
363         ulAdvertise = phyADVERTISE_CSMA;
364 
365         if( pxPhyProperties->ucSpeed == ( uint8_t ) PHY_SPEED_AUTO )
366         {
367             if( pxPhyProperties->ucDuplex == ( uint8_t ) PHY_DUPLEX_FULL )
368             {
369                 ulAdvertise |= phyADVERTISE_10FULL | phyADVERTISE_100FULL;
370             }
371             else
372             {
373                 ulAdvertise |= phyADVERTISE_10HALF | phyADVERTISE_100HALF;
374             }
375         }
376         else if( pxPhyProperties->ucDuplex == ( uint8_t ) PHY_DUPLEX_AUTO )
377         {
378             if( pxPhyProperties->ucSpeed == ( uint8_t ) PHY_SPEED_10 )
379             {
380                 ulAdvertise |= phyADVERTISE_10FULL | phyADVERTISE_10HALF;
381             }
382             else
383             {
384                 ulAdvertise |= phyADVERTISE_100FULL | phyADVERTISE_100HALF;
385             }
386         }
387         else if( pxPhyProperties->ucSpeed == ( uint8_t ) PHY_SPEED_100 )
388         {
389             if( pxPhyProperties->ucDuplex == ( uint8_t ) PHY_DUPLEX_FULL )
390             {
391                 ulAdvertise |= phyADVERTISE_100FULL;
392             }
393             else
394             {
395                 ulAdvertise |= phyADVERTISE_100HALF;
396             }
397         }
398         else
399         {
400             if( pxPhyProperties->ucDuplex == ( uint8_t ) PHY_DUPLEX_FULL )
401             {
402                 ulAdvertise |= phyADVERTISE_10FULL;
403             }
404             else
405             {
406                 ulAdvertise |= phyADVERTISE_10HALF;
407             }
408         }
409     }
410 
411     /* Send a reset command to a set of PHY-ports. */
412     xPhyReset( pxPhyObject, xPhyGetMask( pxPhyObject ) );
413 
414     for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++ )
415     {
416         BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
417         uint32_t ulPhyID = pxPhyObject->ulPhyIDs[ xPhyIndex ];
418 
419         /* Write advertise register. */
420         pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_04_ADVERTISE, ulAdvertise );
421 
422         /*
423          *      AN_EN        AN1         AN0       Forced Mode
424          *        0           0           0        10BASE-T, Half-Duplex
425          *        0           0           1        10BASE-T, Full-Duplex
426          *        0           1           0        100BASE-TX, Half-Duplex
427          *        0           1           1        100BASE-TX, Full-Duplex
428          *      AN_EN        AN1         AN0       Advertised Mode
429          *        1           0           0        10BASE-T, Half/Full-Duplex
430          *        1           0           1        100BASE-TX, Half/Full-Duplex
431          *        1           1           0        10BASE-T Half-Duplex
432          *                                         100BASE-TX, Half-Duplex
433          *        1           1           1        10BASE-T, Half/Full-Duplex
434          *                                         100BASE-TX, Half/Full-Duplex
435          */
436 
437         /* Read Control register. */
438         pxPhyObject->fnPhyRead( xPhyAddress, phyREG_00_BMCR, &ulConfig );
439 
440         ulConfig &= ~( phyBMCR_SPEED_100 | phyBMCR_FULL_DUPLEX );
441 
442         ulConfig |= phyBMCR_AN_ENABLE;
443 
444         if( ( pxPhyProperties->ucSpeed == ( uint8_t ) PHY_SPEED_100 ) || ( pxPhyProperties->ucSpeed == ( uint8_t ) PHY_SPEED_AUTO ) )
445         {
446             ulConfig |= phyBMCR_SPEED_100;
447         }
448         else if( pxPhyProperties->ucSpeed == ( uint8_t ) PHY_SPEED_10 )
449         {
450             ulConfig &= ~phyBMCR_SPEED_100;
451         }
452 
453         if( ( pxPhyProperties->ucDuplex == ( uint8_t ) PHY_DUPLEX_FULL ) || ( pxPhyProperties->ucDuplex == ( uint8_t ) PHY_DUPLEX_AUTO ) )
454         {
455             ulConfig |= phyBMCR_FULL_DUPLEX;
456         }
457         else if( pxPhyProperties->ucDuplex == ( uint8_t ) PHY_DUPLEX_HALF )
458         {
459             ulConfig &= ~phyBMCR_FULL_DUPLEX;
460         }
461 
462         if( xHas_19_PHYCR( ulPhyID ) )
463         {
464             uint32_t ulPhyControl;
465             /* Read PHY Control register. */
466             pxPhyObject->fnPhyRead( xPhyAddress, phyREG_19_PHYCR, &ulPhyControl );
467 
468             /* Clear bits which might get set: */
469             ulPhyControl &= ~( PHYCR_MDIX_EN | PHYCR_MDIX_FORCE );
470 
471             if( pxPhyProperties->ucMDI_X == PHY_MDIX_AUTO )
472             {
473                 ulPhyControl |= PHYCR_MDIX_EN;
474             }
475             else if( pxPhyProperties->ucMDI_X == PHY_MDIX_CROSSED )
476             {
477                 /* Force direct link = Use crossed RJ45 cable. */
478                 ulPhyControl &= ~PHYCR_MDIX_FORCE;
479             }
480             else
481             {
482                 /* Force crossed link = Use direct RJ45 cable. */
483                 ulPhyControl |= PHYCR_MDIX_FORCE;
484             }
485 
486             /* update PHY Control Register. */
487             pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_19_PHYCR, ulPhyControl );
488         }
489 
490         FreeRTOS_printf( ( "+TCP: advertise: %04X config %04X\n", ( unsigned int ) ulAdvertise, ( unsigned int ) ulConfig ) );
491     }
492 
493     /* Keep these values for later use. */
494     pxPhyObject->ulBCRValue = ulConfig & ~phyBMCR_ISOLATE;
495     pxPhyObject->ulACRValue = ulAdvertise;
496 
497     return 0;
498 }
499 /*-----------------------------------------------------------*/
500 
501 /* xPhyFixedValue(): this function is called in case auto-negotiation is disabled.
502  * The caller has set the values in 'xPhyPreferences' (ucDuplex and ucSpeed).
503  * The PHY register phyREG_00_BMCR will be set for every connected PHY that matches
504  * with ulPhyMask. */
xPhyFixedValue(EthernetPhy_t * pxPhyObject,uint32_t ulPhyMask)505 BaseType_t xPhyFixedValue( EthernetPhy_t * pxPhyObject,
506                            uint32_t ulPhyMask )
507 {
508     BaseType_t xPhyIndex;
509     uint32_t ulValue, ulBitMask = ( uint32_t ) 1U;
510 
511     ulValue = ( uint32_t ) 0U;
512 
513     if( pxPhyObject->xPhyPreferences.ucDuplex == PHY_DUPLEX_FULL )
514     {
515         ulValue |= phyBMCR_FULL_DUPLEX;
516     }
517 
518     if( pxPhyObject->xPhyPreferences.ucSpeed == PHY_SPEED_100 )
519     {
520         ulValue |= phyBMCR_SPEED_100;
521     }
522 
523     for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 )
524     {
525         if( ( ulPhyMask & ulBitMask ) != 0lu )
526         {
527             BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
528 
529             /* Enable Auto-Negotiation. */
530             pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, ulValue );
531         }
532     }
533 
534     return 0;
535 }
536 /*-----------------------------------------------------------*/
537 
538 /* xPhyStartAutoNegotiation() is the alternative xPhyFixedValue():
539  * It sets the BMCR_AN_RESTART bit and waits for the auto-negotiation completion
540  * ( phyBMSR_AN_COMPLETE ). */
xPhyStartAutoNegotiation(EthernetPhy_t * pxPhyObject,uint32_t ulPhyMask)541 BaseType_t xPhyStartAutoNegotiation( EthernetPhy_t * pxPhyObject,
542                                      uint32_t ulPhyMask )
543 {
544     uint32_t xPhyIndex, ulDoneMask, ulBitMask;
545     uint32_t ulPHYLinkStatus, ulRegValue;
546     TickType_t xRemainingTime;
547     TimeOut_t xTimer;
548 
549     if( ulPhyMask == ( uint32_t ) 0U )
550     {
551         return 0;
552     }
553 
554     for( xPhyIndex = 0; xPhyIndex < ( uint32_t ) pxPhyObject->xPortCount; xPhyIndex++ )
555     {
556         if( ( ulPhyMask & ( 1lu << xPhyIndex ) ) != 0lu )
557         {
558             BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
559 
560             /* Enable Auto-Negotiation. */
561             pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_04_ADVERTISE, pxPhyObject->ulACRValue );
562             pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, pxPhyObject->ulBCRValue | phyBMCR_AN_RESTART );
563         }
564     }
565 
566     xRemainingTime = ( TickType_t ) pdMS_TO_TICKS( phyPHY_MAX_NEGOTIATE_TIME_MS );
567     vTaskSetTimeOutState( &xTimer );
568     ulDoneMask = 0;
569 
570     /* Wait until the auto-negotiation will be completed */
571     for( ; ; )
572     {
573         ulBitMask = ( uint32_t ) 1U;
574 
575         for( xPhyIndex = 0; xPhyIndex < ( uint32_t ) pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 )
576         {
577             if( ( ulPhyMask & ulBitMask ) != 0lu )
578             {
579                 if( ( ulDoneMask & ulBitMask ) == 0lu )
580                 {
581                     BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
582 
583                     pxPhyObject->fnPhyRead( xPhyAddress, phyREG_01_BMSR, &ulRegValue );
584 
585                     if( ( ulRegValue & phyBMSR_AN_COMPLETE ) != 0 )
586                     {
587                         ulDoneMask |= ulBitMask;
588                     }
589                 }
590             }
591         }
592 
593         if( ulPhyMask == ulDoneMask )
594         {
595             break;
596         }
597 
598         if( xTaskCheckForTimeOut( &xTimer, &xRemainingTime ) != pdFALSE )
599         {
600             FreeRTOS_printf( ( "xPhyStartAutoNegotiation: phyBMSR_AN_COMPLETE timed out ( done 0x%02X )\n", ( unsigned int ) ulDoneMask ) );
601             break;
602         }
603 
604         vTaskDelay( pdMS_TO_TICKS( phySHORT_DELAY_MS ) );
605     }
606 
607     if( ulDoneMask != ( uint32_t ) 0U )
608     {
609         ulBitMask = ( uint32_t ) 1U;
610         pxPhyObject->ulLinkStatusMask &= ~( ulDoneMask );
611 
612         for( xPhyIndex = 0; xPhyIndex < ( uint32_t ) pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 )
613         {
614             BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
615             uint32_t ulPhyID = pxPhyObject->ulPhyIDs[ xPhyIndex ];
616 
617             if( ( ulDoneMask & ulBitMask ) == ( uint32_t ) 0U )
618             {
619                 continue;
620             }
621 
622             /* Clear the 'phyBMCR_AN_RESTART'  bit. */
623             pxPhyObject->fnPhyWrite( xPhyAddress, phyREG_00_BMCR, pxPhyObject->ulBCRValue );
624 
625             pxPhyObject->fnPhyRead( xPhyAddress, phyREG_01_BMSR, &ulRegValue );
626 
627             if( ( ulRegValue & phyBMSR_LINK_STATUS ) != 0 )
628             {
629                 ulPHYLinkStatus |= phyBMSR_LINK_STATUS;
630                 pxPhyObject->ulLinkStatusMask |= ulBitMask;
631             }
632             else
633             {
634                 ulPHYLinkStatus &= ~( phyBMSR_LINK_STATUS );
635             }
636 
637             if( ulPhyID == PHY_ID_KSZ8081MNXIA )
638             {
639                 uint32_t ulControlStatus;
640 
641                 pxPhyObject->fnPhyRead( xPhyAddress, 0x1E, &ulControlStatus );
642 
643                 switch( ulControlStatus & 0x07 )
644                 {
645                     case 0x01:
646                     case 0x05:
647 /*	[001] = 10BASE-T half-duplex */
648 /*	[101] = 10BASE-T full-duplex */
649                         /* 10 Mbps. */
650                         ulRegValue |= phyPHYSTS_SPEED_STATUS;
651                         break;
652 
653                     case 0x02:
654                     case 0x06:
655 /*	[010] = 100BASE-TX half-duplex */
656 /*	[110] = 100BASE-TX full-duplex */
657                         break;
658                 }
659 
660                 switch( ulControlStatus & 0x07 )
661                 {
662                     case 0x05:
663                     case 0x06:
664 /*	[101] = 10BASE-T full-duplex */
665 /*	[110] = 100BASE-TX full-duplex */
666                         /* Full duplex. */
667                         ulRegValue |= phyPHYSTS_DUPLEX_STATUS;
668                         break;
669 
670                     case 0x01:
671                     case 0x02:
672 /*	[001] = 10BASE-T half-duplex */
673 /*	[010] = 100BASE-TX half-duplex */
674                         break;
675                 }
676             }
677             else if( ulPhyID == PHY_ID_KSZ8795 )
678             {
679                 /* KSZ8795 has a different mapping for the Port Operation Mode Indication field
680                  *   in the phyREG_1F_PHYSPCS than other similar PHYs:
681                  *     010 = 10BASE-T half-duplex
682                  *     101 = 10BASE-T full-duplex
683                  *     011 = 100BASE-TX half-duplex
684                  *     110 = 100BASE-TX full-duplex
685                  */
686                 uint32_t ulControlStatus = 0u;
687                 uint32_t ulPortOperationMode = 0u;
688                 pxPhyObject->fnPhyRead( xPhyAddress, phyREG_1F_PHYSPCS, &ulControlStatus );
689                 ulPortOperationMode = ( ulControlStatus >> 8u ) & 0x07u;
690 
691                 ulRegValue = 0;
692 
693                 /* Detect 10baseT operation */
694                 if( ( 0x02u == ulPortOperationMode ) || ( 0x05u == ulPortOperationMode ) )
695                 {
696                     ulRegValue |= phyPHYSTS_SPEED_STATUS;
697                 }
698 
699                 /* Detect full duplex operation */
700                 if( ( 0x05u == ulPortOperationMode ) || ( 0x06u == ulPortOperationMode ) )
701                 {
702                     ulRegValue |= phyPHYSTS_DUPLEX_STATUS;
703                 }
704             }
705             else if( xHas_1F_PHYSPCS( ulPhyID ) )
706             {
707                 /* 31 RW PHY Special Control Status */
708                 uint32_t ulControlStatus;
709 
710                 pxPhyObject->fnPhyRead( xPhyAddress, phyREG_1F_PHYSPCS, &ulControlStatus );
711                 ulRegValue = 0;
712 
713                 if( ( ulControlStatus & phyPHYSPCS_FULL_DUPLEX ) != 0 )
714                 {
715                     ulRegValue |= phyPHYSTS_DUPLEX_STATUS;
716                 }
717 
718                 if( ( ulControlStatus & phyPHYSPCS_SPEED_MASK ) == phyPHYSPCS_SPEED_10 )
719                 {
720                     ulRegValue |= phyPHYSTS_SPEED_STATUS;
721                 }
722             }
723             else
724             {
725                 /* Read the result of the auto-negotiation. */
726                 pxPhyObject->fnPhyRead( xPhyAddress, PHYREG_10_PHYSTS, &ulRegValue );
727             }
728 
729             FreeRTOS_printf( ( "Autonego ready: %08x: %s duplex %u mbit %s status\n",
730                                ( unsigned int ) ulRegValue,
731                                ( ulRegValue & phyPHYSTS_DUPLEX_STATUS ) ? "full" : "half",
732                                ( ulRegValue & phyPHYSTS_SPEED_STATUS ) ? 10 : 100,
733                                ( ( ulPHYLinkStatus |= phyBMSR_LINK_STATUS ) != 0 ) ? "high" : "low" ) );
734 
735             if( ( ulRegValue & phyPHYSTS_DUPLEX_STATUS ) != ( uint32_t ) 0U )
736             {
737                 pxPhyObject->xPhyProperties.ucDuplex = PHY_DUPLEX_FULL;
738             }
739             else
740             {
741                 pxPhyObject->xPhyProperties.ucDuplex = PHY_DUPLEX_HALF;
742             }
743 
744             if( ( ulRegValue & phyPHYSTS_SPEED_STATUS ) != 0 )
745             {
746                 pxPhyObject->xPhyProperties.ucSpeed = PHY_SPEED_10;
747             }
748             else
749             {
750                 pxPhyObject->xPhyProperties.ucSpeed = PHY_SPEED_100;
751             }
752         }
753     } /* if( ulDoneMask != ( uint32_t) 0U ) */
754 
755     return 0;
756 }
757 /*-----------------------------------------------------------*/
758 
xPhyCheckLinkStatus(EthernetPhy_t * pxPhyObject,BaseType_t xHadReception)759 BaseType_t xPhyCheckLinkStatus( EthernetPhy_t * pxPhyObject,
760                                 BaseType_t xHadReception )
761 {
762     uint32_t ulStatus, ulBitMask = 1U;
763     BaseType_t xPhyIndex;
764     BaseType_t xNeedCheck = pdFALSE;
765 
766     if( xHadReception > 0 )
767     {
768         /* A packet was received. No need to check for the PHY status now,
769          * but set a timer to check it later on. */
770         vTaskSetTimeOutState( &( pxPhyObject->xLinkStatusTimer ) );
771         pxPhyObject->xLinkStatusRemaining = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
772 
773         for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 )
774         {
775             if( ( pxPhyObject->ulLinkStatusMask & ulBitMask ) == 0UL )
776             {
777                 pxPhyObject->ulLinkStatusMask |= ulBitMask;
778                 FreeRTOS_printf( ( "xPhyCheckLinkStatus: PHY LS now %02X\n", ( unsigned int ) pxPhyObject->ulLinkStatusMask ) );
779                 xNeedCheck = pdTRUE;
780             }
781         }
782     }
783     else if( xTaskCheckForTimeOut( &( pxPhyObject->xLinkStatusTimer ), &( pxPhyObject->xLinkStatusRemaining ) ) != pdFALSE )
784     {
785         /* Frequent checking the PHY Link Status can affect for the performance of Ethernet controller.
786          * As long as packets are received, no polling is needed.
787          * Otherwise, polling will be done when the 'xLinkStatusTimer' expires. */
788         for( xPhyIndex = 0; xPhyIndex < pxPhyObject->xPortCount; xPhyIndex++, ulBitMask <<= 1 )
789         {
790             BaseType_t xPhyAddress = pxPhyObject->ucPhyIndexes[ xPhyIndex ];
791 
792             if( pxPhyObject->fnPhyRead( xPhyAddress, phyREG_01_BMSR, &ulStatus ) == 0 )
793             {
794                 if( !!( pxPhyObject->ulLinkStatusMask & ulBitMask ) != !!( ulStatus & phyBMSR_LINK_STATUS ) )
795                 {
796                     if( ( ulStatus & phyBMSR_LINK_STATUS ) != 0 )
797                     {
798                         pxPhyObject->ulLinkStatusMask |= ulBitMask;
799                     }
800                     else
801                     {
802                         pxPhyObject->ulLinkStatusMask &= ~( ulBitMask );
803                     }
804 
805                     FreeRTOS_printf( ( "xPhyCheckLinkStatus: PHY LS now %02X\n", ( unsigned int ) pxPhyObject->ulLinkStatusMask ) );
806                     xNeedCheck = pdTRUE;
807                 }
808             }
809         }
810 
811         vTaskSetTimeOutState( &( pxPhyObject->xLinkStatusTimer ) );
812 
813         if( ( pxPhyObject->ulLinkStatusMask & ( ulBitMask >> 1 ) ) != 0 )
814         {
815             /* The link status is high, so don't poll the PHY too often. */
816             pxPhyObject->xLinkStatusRemaining = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
817         }
818         else
819         {
820             /* The link status is low, polling may be done more frequently. */
821             pxPhyObject->xLinkStatusRemaining = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS );
822         }
823     }
824 
825     return xNeedCheck;
826 }
827 /*-----------------------------------------------------------*/
828