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