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