xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/ATSAM4E/ethernet_phy.c (revision 245d5879903cfc6e52a466fc1067fe54f869740c)
1 /**
2  * \file
3  *
4  * \brief API driver for KSZ8051MNL PHY component.
5  *
6  * Copyright (c) 2013 Atmel Corporation. All rights reserved.
7  *
8  * \asf_license_start
9  *
10  * \page License
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  *    this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright notice,
19  *    this list of conditions and the following disclaimer in the documentation
20  *    and/or other materials provided with the distribution.
21  *
22  * 3. The name of Atmel may not be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * 4. This software may only be redistributed and used in connection with an
26  *    Atmel microcontroller product.
27  *
28  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  *
40  * \asf_license_stop
41  *
42  */
43 
44 /* Standard includes. */
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 
49 /* FreeRTOS includes. */
50 #include "FreeRTOS.h"
51 #include "FreeRTOSIPConfig.h"
52 
53 #include "ethernet_phy.h"
54 #include "instance/gmac.h"
55 
56 /*/ @cond 0 */
57 /**INDENT-OFF**/
58 #ifdef __cplusplus
59     extern "C" {
60 #endif
61 /**INDENT-ON**/
62 /*/ @endcond */
63 
64 /**
65  * \defgroup ksz8051mnl_ethernet_phy_group PHY component (KSZ8051MNL)
66  *
67  * Driver for the ksz8051mnl component. This driver provides access to the main
68  * features of the PHY.
69  *
70  * \section dependencies Dependencies
71  * This driver depends on the following modules:
72  * - \ref gmac_group Ethernet Media Access Controller (GMAC) module.
73  *
74  * @{
75  */
76 
77 SPhyProps phyProps;
78 
79 /* Max PHY number */
80 #define ETH_PHY_MAX_ADDR     31
81 
82 /* Ethernet PHY operation max retry count */
83 #define ETH_PHY_RETRY_MAX    1000000
84 
85 /* Ethernet PHY operation timeout */
86 #define ETH_PHY_TIMEOUT      10
87 
88 /**
89  * \brief Find a valid PHY Address ( from addrStart to 31 ).
90  *
91  * \param p_gmac   Pointer to the GMAC instance.
92  * \param uc_phy_addr PHY address.
93  * \param uc_start_addr Start address of the PHY to be searched.
94  *
95  * \return 0xFF when no valid PHY address is found.
96  */
97 int ethernet_phy_addr = 0;
ethernet_phy_find_valid(Gmac * p_gmac,uint8_t uc_phy_addr,uint8_t uc_start_addr)98 static uint8_t ethernet_phy_find_valid( Gmac * p_gmac,
99                                         uint8_t uc_phy_addr,
100                                         uint8_t uc_start_addr )
101 {
102     uint32_t ul_value = 0;
103     uint8_t uc_cnt;
104     uint8_t uc_phy_address = uc_phy_addr;
105 
106     gmac_enable_management( p_gmac, true );
107 
108 /*
109  #define GMII_OUI_MSB            0x0022
110  #define GMII_OUI_LSB            0x05
111  *
112  * PHYID1 = 0x0022
113  * PHYID2 = 0x1550
114  * 0001_0101_0101_0000 = 0x1550 <= mask should be 0xFFF0
115  */
116     /* Check the current PHY address */
117     gmac_phy_read( p_gmac, uc_phy_addr, GMII_PHYID1, &ul_value );
118 
119     /* Find another one */
120     if( ul_value != GMII_OUI_MSB )
121     {
122         ethernet_phy_addr = 0xFF;
123 
124         for( uc_cnt = uc_start_addr; uc_cnt <= ETH_PHY_MAX_ADDR; uc_cnt++ )
125         {
126             uc_phy_address = ( uc_phy_address + 1 ) & 0x1F;
127             ul_value = 0;
128             gmac_phy_read( p_gmac, uc_phy_address, GMII_PHYID1, &ul_value );
129 
130             if( ul_value == GMII_OUI_MSB )
131             {
132                 ethernet_phy_addr = uc_phy_address;
133                 break;
134             }
135         }
136     }
137 
138     gmac_enable_management( p_gmac, false );
139 
140     if( ethernet_phy_addr != 0xFF )
141     {
142         gmac_phy_read( p_gmac, uc_phy_address, GMII_BMSR, &ul_value );
143     }
144 
145     return ethernet_phy_addr;
146 }
147 
148 
149 /**
150  * \brief Perform a HW initialization to the PHY and set up clocks.
151  *
152  * This should be called only once to initialize the PHY pre-settings.
153  * The PHY address is the reset status of CRS, RXD[3:0] (the emacPins' pullups).
154  * The COL pin is used to select MII mode on reset (pulled up for Reduced MII).
155  * The RXDV pin is used to select test mode on reset (pulled up for test mode).
156  * The above pins should be predefined for corresponding settings in resetPins.
157  * The GMAC peripheral pins are configured after the reset is done.
158  *
159  * \param p_gmac   Pointer to the GMAC instance.
160  * \param uc_phy_addr PHY address.
161  * \param ul_mck GMAC MCK.
162  *
163  * Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout.
164  */
ethernet_phy_init(Gmac * p_gmac,uint8_t uc_phy_addr,uint32_t mck)165 uint8_t ethernet_phy_init( Gmac * p_gmac,
166                            uint8_t uc_phy_addr,
167                            uint32_t mck )
168 {
169     uint8_t uc_rc = GMAC_TIMEOUT;
170     uint8_t uc_phy;
171 
172     ethernet_phy_reset( GMAC, uc_phy_addr );
173 
174     /* Configure GMAC runtime clock */
175     uc_rc = gmac_set_mdc_clock( p_gmac, mck );
176 
177     if( uc_rc != GMAC_OK )
178     {
179         return 0;
180     }
181 
182     /* Check PHY Address */
183     uc_phy = ethernet_phy_find_valid( p_gmac, uc_phy_addr, 0 );
184 
185     if( uc_phy == 0xFF )
186     {
187         return 0;
188     }
189 
190     if( uc_phy != uc_phy_addr )
191     {
192         ethernet_phy_reset( p_gmac, uc_phy_addr );
193     }
194 
195     phy_props.phy_chn = uc_phy;
196     return uc_phy;
197 }
198 
199 
200 /**
201  * \brief Get the Link & speed settings, and automatically set up the GMAC with the
202  * settings.
203  *
204  * \param p_gmac   Pointer to the GMAC instance.
205  * \param uc_phy_addr PHY address.
206  * \param uc_apply_setting_flag Set to 0 to not apply the PHY configurations, else to apply.
207  *
208  * Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout.
209  */
ethernet_phy_set_link(Gmac * p_gmac,uint8_t uc_phy_addr,uint8_t uc_apply_setting_flag)210 uint8_t ethernet_phy_set_link( Gmac * p_gmac,
211                                uint8_t uc_phy_addr,
212                                uint8_t uc_apply_setting_flag )
213 {
214     uint32_t ul_stat1;
215     uint32_t ul_stat2;
216     uint8_t uc_phy_address, uc_speed = true, uc_fd = true;
217     uint8_t uc_rc = GMAC_TIMEOUT;
218 
219     gmac_enable_management( p_gmac, true );
220 
221     uc_phy_address = uc_phy_addr;
222 
223     uc_rc = gmac_phy_read( p_gmac, uc_phy_address, GMII_BMSR, &ul_stat1 );
224 
225     if( uc_rc != GMAC_OK )
226     {
227         /* Disable PHY management and start the GMAC transfer */
228         gmac_enable_management( p_gmac, false );
229 
230         return uc_rc;
231     }
232 
233     if( ( ul_stat1 & GMII_LINK_STATUS ) == 0 )
234     {
235         /* Disable PHY management and start the GMAC transfer */
236         gmac_enable_management( p_gmac, false );
237 
238         return GMAC_INVALID;
239     }
240 
241     if( uc_apply_setting_flag == 0 )
242     {
243         /* Disable PHY management and start the GMAC transfer */
244         gmac_enable_management( p_gmac, false );
245 
246         return uc_rc;
247     }
248 
249     /* Read advertisement */
250     uc_rc = gmac_phy_read( p_gmac, uc_phy_address, GMII_ANAR, &ul_stat2 );
251     phy_props.phy_stat1 = ul_stat1;
252     phy_props.phy_stat2 = ul_stat2;
253 
254     if( uc_rc != GMAC_OK )
255     {
256         /* Disable PHY management and start the GMAC transfer */
257         gmac_enable_management( p_gmac, false );
258 
259         return uc_rc;
260     }
261 
262     if( ( ul_stat1 & GMII_100BASE_TX_FD ) && ( ul_stat2 & GMII_100TX_FDX ) )
263     {
264         /* Set GMAC for 100BaseTX and Full Duplex */
265         uc_speed = true;
266         uc_fd = true;
267     }
268     else
269     if( ( ul_stat1 & GMII_100BASE_T4_HD ) && ( ul_stat2 & GMII_100TX_HDX ) )
270     {
271         /* Set MII for 100BaseTX and Half Duplex */
272         uc_speed = true;
273         uc_fd = false;
274     }
275     else
276     if( ( ul_stat1 & GMII_10BASE_T_FD ) && ( ul_stat2 & GMII_10_FDX ) )
277     {
278         /* Set MII for 10BaseT and Full Duplex */
279         uc_speed = false;
280         uc_fd = true;
281     }
282     else
283     if( ( ul_stat1 & GMII_10BASE_T_HD ) && ( ul_stat2 & GMII_10_HDX ) )
284     {
285         /* Set MII for 10BaseT and Half Duplex */
286         uc_speed = false;
287         uc_fd = false;
288     }
289 
290     gmac_set_speed( p_gmac, uc_speed );
291     gmac_enable_full_duplex( p_gmac, uc_fd );
292 
293     /* Start the GMAC transfers */
294     gmac_enable_management( p_gmac, false );
295     return uc_rc;
296 }
297 
298 PhyProps_t phy_props;
299 
300 /**
301  * \brief Issue an auto negotiation of the PHY.
302  *
303  * \param p_gmac   Pointer to the GMAC instance.
304  * \param uc_phy_addr PHY address.
305  *
306  * Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout.
307  */
ethernet_phy_auto_negotiate(Gmac * p_gmac,uint8_t uc_phy_addr)308 uint8_t ethernet_phy_auto_negotiate( Gmac * p_gmac,
309                                      uint8_t uc_phy_addr )
310 {
311     uint32_t ul_retry_max = ETH_PHY_RETRY_MAX;
312     uint32_t ul_value;
313     uint32_t ul_phy_anar;
314     uint32_t ul_retry_count = 0;
315     uint8_t uc_speed = 0;
316     uint8_t uc_fd = 0;
317     uint8_t uc_rc = GMAC_TIMEOUT;
318 
319     gmac_enable_management( p_gmac, true );
320 
321     /* Set up control register */
322     uc_rc = gmac_phy_read( p_gmac, uc_phy_addr, GMII_BMCR, &ul_value );
323 
324     if( uc_rc != GMAC_OK )
325     {
326         gmac_enable_management( p_gmac, false );
327         phy_props.phy_result = -1;
328         return uc_rc;
329     }
330 
331     ul_value &= ~( uint32_t ) GMII_AUTONEG; /* Remove auto-negotiation enable */
332     ul_value &= ~( uint32_t ) ( GMII_LOOPBACK | GMII_POWER_DOWN );
333     ul_value |= ( uint32_t ) GMII_ISOLATE;  /* Electrically isolate PHY */
334     uc_rc = gmac_phy_write( p_gmac, uc_phy_addr, GMII_BMCR, ul_value );
335 
336     if( uc_rc != GMAC_OK )
337     {
338         gmac_enable_management( p_gmac, false );
339         phy_props.phy_result = -2;
340         return uc_rc;
341     }
342 
343     /*
344      * Set the Auto_negotiation Advertisement Register.
345      * MII advertising for Next page.
346      * 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3.
347      */
348     ul_phy_anar = GMII_100TX_FDX | GMII_100TX_HDX | GMII_10_FDX | GMII_10_HDX |
349                   GMII_AN_IEEE_802_3;
350     uc_rc = gmac_phy_write( p_gmac, uc_phy_addr, GMII_ANAR, ul_phy_anar );
351 
352     if( uc_rc != GMAC_OK )
353     {
354         gmac_enable_management( p_gmac, false );
355         phy_props.phy_result = -3;
356         return uc_rc;
357     }
358 
359     /* Read & modify control register */
360     uc_rc = gmac_phy_read( p_gmac, uc_phy_addr, GMII_BMCR, &ul_value );
361 
362     if( uc_rc != GMAC_OK )
363     {
364         gmac_enable_management( p_gmac, false );
365         phy_props.phy_result = -4;
366         return uc_rc;
367     }
368 
369     ul_value |= GMII_SPEED_SELECT | GMII_AUTONEG | GMII_DUPLEX_MODE;
370     uc_rc = gmac_phy_write( p_gmac, uc_phy_addr, GMII_BMCR, ul_value );
371 
372     if( uc_rc != GMAC_OK )
373     {
374         gmac_enable_management( p_gmac, false );
375         phy_props.phy_result = -5;
376         return uc_rc;
377     }
378 
379     /* Restart auto negotiation */
380     ul_value |= ( uint32_t ) GMII_RESTART_AUTONEG;
381     ul_value &= ~( uint32_t ) GMII_ISOLATE;
382     uc_rc = gmac_phy_write( p_gmac, uc_phy_addr, GMII_BMCR, ul_value );
383 
384     if( uc_rc != GMAC_OK )
385     {
386         gmac_enable_management( p_gmac, false );
387         phy_props.phy_result = -6;
388         return uc_rc;
389     }
390 
391     /* Check if auto negotiation is completed */
392     while( 1 )
393     {
394         uc_rc = gmac_phy_read( p_gmac, uc_phy_addr, GMII_BMSR, &ul_value );
395 
396         if( uc_rc != GMAC_OK )
397         {
398             gmac_enable_management( p_gmac, false );
399             phy_props.phy_result = -7;
400             return uc_rc;
401         }
402 
403         /* Done successfully */
404         if( ul_value & GMII_AUTONEG_COMP )
405         {
406             break;
407         }
408 
409         /* Timeout check */
410         if( ul_retry_max )
411         {
412             if( ++ul_retry_count >= ul_retry_max )
413             {
414                 gmac_enable_management( p_gmac, false );
415                 phy_props.phy_result = -8;
416                 return GMAC_TIMEOUT;
417             }
418         }
419     }
420 
421     /* Get the auto negotiate link partner base page */
422     uc_rc = gmac_phy_read( p_gmac, uc_phy_addr, GMII_PCR1, &phy_props.phy_params );
423 
424     if( uc_rc != GMAC_OK )
425     {
426         gmac_enable_management( p_gmac, false );
427         phy_props.phy_result = -9;
428         return uc_rc;
429     }
430 
431     /* Set up the GMAC link speed */
432     if( ( ul_phy_anar & phy_props.phy_params ) & GMII_100TX_FDX )
433     {
434         /* Set MII for 100BaseTX and Full Duplex */
435         uc_speed = true;
436         uc_fd = true;
437     }
438     else if( ( ul_phy_anar & phy_props.phy_params ) & GMII_10_FDX )
439     {
440         /* Set MII for 10BaseT and Full Duplex */
441         uc_speed = false;
442         uc_fd = true;
443     }
444     else if( ( ul_phy_anar & phy_props.phy_params ) & GMII_100TX_HDX )
445     {
446         /* Set MII for 100BaseTX and half Duplex */
447         uc_speed = true;
448         uc_fd = false;
449     }
450     else if( ( ul_phy_anar & phy_props.phy_params ) & GMII_10_HDX )
451     {
452         /* Set MII for 10BaseT and half Duplex */
453         uc_speed = false;
454         uc_fd = false;
455     }
456 
457     gmac_set_speed( p_gmac, uc_speed );
458     gmac_enable_full_duplex( p_gmac, uc_fd );
459 
460     /* Select Media Independent Interface type */
461     gmac_select_mii_mode( p_gmac, ETH_PHY_MODE );
462 
463     gmac_enable_transmit( GMAC, true );
464     gmac_enable_receive( GMAC, true );
465 
466     gmac_enable_management( p_gmac, false );
467     phy_props.phy_result = 1;
468     return uc_rc;
469 }
470 
471 /**
472  * \brief Issue a SW reset to reset all registers of the PHY.
473  *
474  * \param p_gmac   Pointer to the GMAC instance.
475  * \param uc_phy_addr PHY address.
476  *
477  * \Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout.
478  */
ethernet_phy_reset(Gmac * p_gmac,uint8_t uc_phy_addr)479 uint8_t ethernet_phy_reset( Gmac * p_gmac,
480                             uint8_t uc_phy_addr )
481 {
482     uint32_t ul_bmcr = GMII_RESET;
483     uint8_t uc_phy_address = uc_phy_addr;
484     uint32_t ul_timeout = ETH_PHY_TIMEOUT;
485     uint8_t uc_rc = GMAC_TIMEOUT;
486 
487     gmac_enable_management( p_gmac, true );
488 
489     ul_bmcr = GMII_RESET;
490     gmac_phy_write( p_gmac, uc_phy_address, GMII_BMCR, ul_bmcr );
491 
492     do
493     {
494         gmac_phy_read( p_gmac, uc_phy_address, GMII_BMCR, &ul_bmcr );
495         ul_timeout--;
496     } while( ( ul_bmcr & GMII_RESET ) && ul_timeout );
497 
498     gmac_enable_management( p_gmac, false );
499 
500     if( !ul_timeout )
501     {
502         uc_rc = GMAC_OK;
503     }
504 
505     return( uc_rc );
506 }
507 
508 /*/ @cond 0 */
509 /**INDENT-OFF**/
510 #ifdef __cplusplus
511 }
512 #endif
513 /**INDENT-ON**/
514 /*/ @endcond */
515 
516 /**
517  * \}
518  */
519