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