]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ATSAM4E/gmac.c
Sync FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP with the version in GitHub at (23665258ca...
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / portable / NetworkInterface / ATSAM4E / gmac.c
1  /**\r
2  * \file\r
3  *\r
4  * \brief GMAC (Ethernet MAC) driver for SAM.\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 <string.h>\r
48 #include <stdlib.h>\r
49 \r
50 /* FreeRTOS includes. */\r
51 #include "FreeRTOS.h"\r
52 #include "task.h"\r
53 \r
54 #include "FreeRTOSIPConfig.h"\r
55 \r
56 #include "compiler.h"\r
57 #include "instance/gmac.h"\r
58 #include "ethernet_phy.h"\r
59 \r
60 /// @cond 0\r
61 /**INDENT-OFF**/\r
62 #ifdef __cplusplus\r
63 extern "C" {\r
64 #endif\r
65 /**INDENT-ON**/\r
66 /// @endcond\r
67 \r
68 #ifndef ARRAY_SIZE\r
69 #define ARRAY_SIZE(x)  (int)( sizeof(x) / sizeof(x)[0] )\r
70 #endif\r
71 /**\r
72  * \defgroup gmac_group Ethernet Media Access Controller\r
73  *\r
74  * See \ref gmac_quickstart.\r
75  *\r
76  * Driver for the GMAC (Ethernet Media Access Controller).\r
77  * This file contains basic functions for the GMAC, with support for all modes, settings\r
78  * and clock speeds.\r
79  *\r
80  * \section dependencies Dependencies\r
81  * This driver does not depend on other modules.\r
82  *\r
83  * @{\r
84  */\r
85 \r
86 /** TX descriptor lists */\r
87 COMPILER_ALIGNED(8)\r
88 static gmac_tx_descriptor_t gs_tx_desc[ GMAC_TX_BUFFERS ];\r
89 #if( GMAC_USES_TX_CALLBACK != 0 )\r
90 /** TX callback lists */\r
91 static gmac_dev_tx_cb_t gs_tx_callback[ GMAC_TX_BUFFERS ];\r
92 #endif\r
93 /** RX descriptors lists */\r
94 COMPILER_ALIGNED(8)\r
95 static gmac_rx_descriptor_t gs_rx_desc[ GMAC_RX_BUFFERS ];\r
96 \r
97 #if( ipconfigZERO_COPY_TX_DRIVER == 0 )\r
98         /** Send Buffer. Section 3.6 of AMBA 2.0 spec states that burst should not cross the\r
99          * 1K Boundaries. Receive buffer manager write operations are burst of 2 words => 3 lsb bits\r
100          * of the address shall be set to 0.\r
101          */\r
102         COMPILER_ALIGNED(8)\r
103         static uint8_t gs_uc_tx_buffer[ GMAC_TX_BUFFERS * GMAC_TX_UNITSIZE ];\r
104 #endif /* ipconfigZERO_COPY_TX_DRIVER */\r
105 \r
106 /** Receive Buffer */\r
107 COMPILER_ALIGNED(8)\r
108 static uint8_t gs_uc_rx_buffer[ GMAC_RX_BUFFERS * GMAC_RX_UNITSIZE ];\r
109 \r
110 /**\r
111  * GMAC device memory management struct.\r
112  */\r
113 typedef struct gmac_dev_mem {\r
114         /* Pointer to allocated buffer for RX. The address should be 8-byte aligned\r
115         and the size should be GMAC_RX_UNITSIZE * wRxSize. */\r
116         uint8_t *p_rx_buffer;\r
117         /* Pointer to allocated RX descriptor list. */\r
118         gmac_rx_descriptor_t *p_rx_dscr;\r
119         /* RX size, in number of registered units (RX descriptors). */\r
120         /* Increased size from 16- to 32-bits, because it's more efficient */\r
121         uint32_t us_rx_size;\r
122         /* Pointer to allocated buffer for TX. The address should be 8-byte aligned\r
123         and the size should be GMAC_TX_UNITSIZE * wTxSize. */\r
124         uint8_t *p_tx_buffer;\r
125         /* Pointer to allocated TX descriptor list. */\r
126         gmac_tx_descriptor_t *p_tx_dscr;\r
127         /* TX size, in number of registered units (TX descriptors). */\r
128         uint32_t us_tx_size;\r
129 } gmac_dev_mem_t;\r
130 \r
131 /** Return count in buffer */\r
132 #define CIRC_CNT( head, tail, size )            ( ( ( head ) - ( tail ) ) % ( size ) )\r
133 \r
134 /*\r
135  * Return space available, from 0 to size-1.\r
136  * Always leave one free char as a completely full buffer that has (head == tail),\r
137  * which is the same as empty.\r
138  */\r
139 #define CIRC_SPACE( head, tail, size )          CIRC_CNT( ( tail ), ( ( head ) + 1 ), ( size ) )\r
140 \r
141 /** Circular buffer is empty ? */\r
142 #define CIRC_EMPTY( head, tail )                        ( head == tail )\r
143 /** Clear circular buffer */\r
144 #define CIRC_CLEAR( head, tail )                        do { ( head ) = 0; ( tail ) = 0; } while( 0 )\r
145 \r
146 /** Increment head or tail */\r
147 static __inline void circ_inc32( int32_t *lHeadOrTail, uint32_t ulSize )\r
148 {\r
149         ( *lHeadOrTail ) ++;\r
150     if( ( *lHeadOrTail ) >= ( int32_t )ulSize )\r
151         {\r
152                 ( *lHeadOrTail ) = 0;\r
153         }\r
154 }\r
155 \r
156 /**\r
157  * \brief Wait PHY operation to be completed.\r
158  *\r
159  * \param p_gmac HW controller address.\r
160  * \param ul_retry The retry times, 0 to wait forever until completeness.\r
161  *\r
162  * Return GMAC_OK if the operation is completed successfully.\r
163  */\r
164 static uint8_t gmac_wait_phy(Gmac* p_gmac, const uint32_t ul_retry)\r
165 {\r
166         volatile uint32_t ul_retry_count = 0;\r
167         const uint32_t xPHYPollDelay = pdMS_TO_TICKS( 1ul );\r
168 \r
169         while (!gmac_is_phy_idle(p_gmac)) {\r
170                 if (ul_retry == 0) {\r
171                         continue;\r
172                 }\r
173 \r
174                 ul_retry_count++;\r
175 \r
176                 if (ul_retry_count >= ul_retry) {\r
177                         return GMAC_TIMEOUT;\r
178                 }\r
179 \r
180                 /* Block the task to allow other tasks to execute while the PHY\r
181                 is not connected. */\r
182                 vTaskDelay( xPHYPollDelay );\r
183         }\r
184         return GMAC_OK;\r
185 }\r
186 \r
187 /**\r
188  * \brief Disable transfer, reset registers and descriptor lists.\r
189  *\r
190  * \param p_dev Pointer to GMAC driver instance.\r
191  *\r
192  */\r
193 static void gmac_reset_tx_mem(gmac_device_t* p_dev)\r
194 {\r
195         Gmac *p_hw = p_dev->p_hw;\r
196         uint8_t *p_tx_buff = p_dev->p_tx_buffer;\r
197         gmac_tx_descriptor_t *p_td = p_dev->p_tx_dscr;\r
198 \r
199         uint32_t ul_index;\r
200         uint32_t ul_address;\r
201 \r
202         /* Disable TX */\r
203         gmac_enable_transmit(p_hw, 0);\r
204 \r
205         /* Set up the TX descriptors */\r
206         CIRC_CLEAR(p_dev->l_tx_head, p_dev->l_tx_tail);\r
207         for( ul_index = 0; ul_index < p_dev->ul_tx_list_size; ul_index++ )\r
208         {\r
209                 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )\r
210                 {\r
211                         ul_address = (uint32_t) 0u;\r
212                 }\r
213                 #else\r
214                 {\r
215                         ul_address = (uint32_t) (&(p_tx_buff[ul_index * GMAC_TX_UNITSIZE]));\r
216                 }\r
217                 #endif /* ipconfigZERO_COPY_TX_DRIVER */\r
218                 p_td[ul_index].addr = ul_address;\r
219                 p_td[ul_index].status.val = GMAC_TXD_USED;\r
220         }\r
221         p_td[p_dev->ul_tx_list_size - 1].status.val =\r
222                         GMAC_TXD_USED | GMAC_TXD_WRAP;\r
223 \r
224         /* Set transmit buffer queue */\r
225         gmac_set_tx_queue(p_hw, (uint32_t) p_td);\r
226 }\r
227 \r
228 /**\r
229  * \brief Disable receiver, reset registers and descriptor list.\r
230  *\r
231  * \param p_drv Pointer to GMAC Driver instance.\r
232  */\r
233 static void gmac_reset_rx_mem(gmac_device_t* p_dev)\r
234 {\r
235         Gmac *p_hw = p_dev->p_hw;\r
236         uint8_t *p_rx_buff = p_dev->p_rx_buffer;\r
237         gmac_rx_descriptor_t *pRd = p_dev->p_rx_dscr;\r
238 \r
239         uint32_t ul_index;\r
240         uint32_t ul_address;\r
241 \r
242         /* Disable RX */\r
243         gmac_enable_receive(p_hw, 0);\r
244 \r
245         /* Set up the RX descriptors */\r
246         p_dev->ul_rx_idx = 0;\r
247         for( ul_index = 0; ul_index < p_dev->ul_rx_list_size; ul_index++ )\r
248         {\r
249                 ul_address = (uint32_t) (&(p_rx_buff[ul_index * GMAC_RX_UNITSIZE]));\r
250                 pRd[ul_index].addr.val = ul_address & GMAC_RXD_ADDR_MASK;\r
251                 pRd[ul_index].status.val = 0;\r
252         }\r
253         pRd[p_dev->ul_rx_list_size - 1].addr.val |= GMAC_RXD_WRAP;\r
254 \r
255         /* Set receive buffer queue */\r
256         gmac_set_rx_queue(p_hw, (uint32_t) pRd);\r
257 }\r
258 \r
259 \r
260 /**\r
261  * \brief Initialize the allocated buffer lists for GMAC driver to transfer data.\r
262  * Must be invoked after gmac_dev_init() but before RX/TX starts.\r
263  *\r
264  * \note If input address is not 8-byte aligned, the address is automatically\r
265  *       adjusted and the list size is reduced by one.\r
266  *\r
267  * \param p_gmac Pointer to GMAC instance.\r
268  * \param p_gmac_dev Pointer to GMAC device instance.\r
269  * \param p_dev_mm Pointer to the GMAC memory management control block.\r
270  * \param p_tx_cb Pointer to allocated TX callback list.\r
271  *\r
272  * \return GMAC_OK or GMAC_PARAM.\r
273  */\r
274 static uint8_t gmac_init_mem(Gmac* p_gmac, gmac_device_t* p_gmac_dev,\r
275                 gmac_dev_mem_t* p_dev_mm\r
276 #if( GMAC_USES_TX_CALLBACK != 0 )\r
277                 , gmac_dev_tx_cb_t* p_tx_cb\r
278 #endif\r
279                 )\r
280 {\r
281         if (p_dev_mm->us_rx_size <= 1 || p_dev_mm->us_tx_size <= 1\r
282 #if( GMAC_USES_TX_CALLBACK != 0 )\r
283                 || p_tx_cb == NULL\r
284 #endif\r
285                 ) {\r
286                 return GMAC_PARAM;\r
287         }\r
288 \r
289         /* Assign RX buffers */\r
290         if (((uint32_t) p_dev_mm->p_rx_buffer & 0x7)\r
291                         || ((uint32_t) p_dev_mm->p_rx_dscr & 0x7)) {\r
292                 p_dev_mm->us_rx_size--;\r
293         }\r
294         p_gmac_dev->p_rx_buffer =\r
295                         (uint8_t *) ((uint32_t) p_dev_mm->p_rx_buffer & 0xFFFFFFF8);\r
296         p_gmac_dev->p_rx_dscr =\r
297                         (gmac_rx_descriptor_t *) ((uint32_t) p_dev_mm->p_rx_dscr\r
298                         & 0xFFFFFFF8);\r
299         p_gmac_dev->ul_rx_list_size = p_dev_mm->us_rx_size;\r
300 \r
301         /* Assign TX buffers */\r
302         if (((uint32_t) p_dev_mm->p_tx_buffer & 0x7)\r
303                         || ((uint32_t) p_dev_mm->p_tx_dscr & 0x7)) {\r
304                 p_dev_mm->us_tx_size--;\r
305         }\r
306         p_gmac_dev->p_tx_buffer =\r
307                         (uint8_t *) ((uint32_t) p_dev_mm->p_tx_buffer & 0xFFFFFFF8);\r
308         p_gmac_dev->p_tx_dscr =\r
309                         (gmac_tx_descriptor_t *) ((uint32_t) p_dev_mm->p_tx_dscr\r
310                         & 0xFFFFFFF8);\r
311         p_gmac_dev->ul_tx_list_size = p_dev_mm->us_tx_size;\r
312 #if( GMAC_USES_TX_CALLBACK != 0 )\r
313         p_gmac_dev->func_tx_cb_list = p_tx_cb;\r
314 #endif\r
315         /* Reset TX & RX */\r
316         gmac_reset_rx_mem(p_gmac_dev);\r
317         gmac_reset_tx_mem(p_gmac_dev);\r
318 \r
319         /* Enable Rx and Tx, plus the statistics register */\r
320         gmac_enable_transmit(p_gmac, true);\r
321         gmac_enable_receive(p_gmac, true);\r
322         gmac_enable_statistics_write(p_gmac, true);\r
323 \r
324         /* Set up the interrupts for transmission and errors */\r
325         gmac_enable_interrupt(p_gmac,\r
326                         GMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */\r
327                         GMAC_IER_TUR   | /* Enable transmit underrun interrupt. */\r
328                         GMAC_IER_RLEX  | /* Enable retry limit  exceeded interrupt. */\r
329                         GMAC_IER_TFC   | /* Enable transmit buffers exhausted in mid-frame interrupt. */\r
330                         GMAC_IER_TCOMP | /* Enable transmit complete interrupt. */\r
331                         GMAC_IER_ROVR  | /* Enable receive overrun interrupt. */\r
332                         GMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */\r
333                         GMAC_IER_PFNZ  | /* Enable pause frame received interrupt. */\r
334                         GMAC_IER_PTZ);   /* Enable pause time zero interrupt. */\r
335 \r
336         return GMAC_OK;\r
337 }\r
338 \r
339 /**\r
340  * \brief Read the PHY register.\r
341  *\r
342  * \param p_gmac   Pointer to the GMAC instance.\r
343  * \param uc_phy_address PHY address.\r
344  * \param uc_address Register address.\r
345  * \param p_value Pointer to a 32-bit location to store read data.\r
346  *\r
347  * \Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout.\r
348  */\r
349 uint8_t gmac_phy_read(Gmac* p_gmac, uint8_t uc_phy_address, uint8_t uc_address,\r
350                 uint32_t* p_value)\r
351 {\r
352         gmac_maintain_phy(p_gmac, uc_phy_address, uc_address, 1, 0);\r
353 \r
354         if (gmac_wait_phy(p_gmac, MAC_PHY_RETRY_MAX) == GMAC_TIMEOUT) {\r
355                 return GMAC_TIMEOUT;\r
356         }\r
357         *p_value = gmac_get_phy_data(p_gmac);\r
358         return GMAC_OK;\r
359 }\r
360 \r
361 /**\r
362  * \brief Write the PHY register.\r
363  *\r
364  * \param p_gmac   Pointer to the GMAC instance.\r
365  * \param uc_phy_address PHY Address.\r
366  * \param uc_address Register Address.\r
367  * \param ul_value Data to write, actually 16-bit data.\r
368  *\r
369  * \Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout.\r
370  */\r
371 uint8_t gmac_phy_write(Gmac* p_gmac, uint8_t uc_phy_address,\r
372                 uint8_t uc_address, uint32_t ul_value)\r
373 {\r
374         gmac_maintain_phy(p_gmac, uc_phy_address, uc_address, 0, ul_value);\r
375 \r
376         if (gmac_wait_phy(p_gmac, MAC_PHY_RETRY_MAX) == GMAC_TIMEOUT) {\r
377                 return GMAC_TIMEOUT;\r
378         }\r
379         return GMAC_OK;\r
380 }\r
381 \r
382 /**\r
383  * \brief Initialize the GMAC driver.\r
384  *\r
385  * \param p_gmac   Pointer to the GMAC instance.\r
386  * \param p_gmac_dev Pointer to the GMAC device instance.\r
387  * \param p_opt GMAC configure options.\r
388  */\r
389 void gmac_dev_init(Gmac* p_gmac, gmac_device_t* p_gmac_dev,\r
390                 gmac_options_t* p_opt)\r
391 {\r
392         gmac_dev_mem_t gmac_dev_mm;\r
393 \r
394         /* Disable TX & RX and more */\r
395         gmac_network_control(p_gmac, 0);\r
396         gmac_disable_interrupt(p_gmac, ~0u);\r
397 \r
398 \r
399         gmac_clear_statistics(p_gmac);\r
400 \r
401         /* Clear all status bits in the receive status register. */\r
402         gmac_clear_rx_status(p_gmac, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA);\r
403 \r
404         /* Clear all status bits in the transmit status register */\r
405         gmac_clear_tx_status(p_gmac, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE\r
406                         | GMAC_TSR_TFC | GMAC_TSR_TXCOMP | GMAC_TSR_UND);\r
407 \r
408         /* Clear interrupts */\r
409         gmac_get_interrupt_status(p_gmac);\r
410 #if !defined(ETHERNET_CONF_DATA_OFFSET)\r
411         /*  Receive Buffer Offset\r
412          * Indicates the number of bytes by which the received data\r
413          * is offset from the start of the receive buffer\r
414          * which can be handy for alignment reasons */\r
415         /* Note: FreeRTOS+TCP wants to have this offset set to 2 bytes */\r
416         #error ETHERNET_CONF_DATA_OFFSET not defined, assuming 0\r
417 #endif\r
418         /* Enable the copy of data into the buffers\r
419            ignore broadcasts, and not copy FCS. */\r
420 \r
421         gmac_set_configure(p_gmac,\r
422                         ( gmac_get_configure(p_gmac) & ~GMAC_NCFGR_RXBUFO_Msk ) |\r
423                         GMAC_NCFGR_RFCS |   /*  Remove FCS, frame check sequence (last 4 bytes) */\r
424                         GMAC_NCFGR_PEN |    /* Pause Enable */\r
425                         GMAC_NCFGR_RXBUFO( ETHERNET_CONF_DATA_OFFSET ) |\r
426                         GMAC_RXD_RXCOEN );\r
427 \r
428         /*\r
429          * GMAC_DCFGR_TXCOEN: (GMAC_DCFGR) Transmitter Checksum Generation Offload Enable.\r
430          * Note: tha SAM4E does have RX checksum offloading\r
431          * but TX checksum offloading has NOT been implemented.\r
432          */\r
433 \r
434         gmac_set_dma(p_gmac,\r
435                         gmac_get_dma(p_gmac) | GMAC_DCFGR_TXCOEN );\r
436 \r
437         gmac_enable_copy_all(p_gmac, p_opt->uc_copy_all_frame);\r
438         gmac_disable_broadcast(p_gmac, p_opt->uc_no_boardcast);\r
439 \r
440         /* Fill in GMAC device memory management */\r
441         gmac_dev_mm.p_rx_buffer = gs_uc_rx_buffer;\r
442         gmac_dev_mm.p_rx_dscr = gs_rx_desc;\r
443         gmac_dev_mm.us_rx_size = GMAC_RX_BUFFERS;\r
444 \r
445         #if( ipconfigZERO_COPY_TX_DRIVER != 0 )\r
446         {\r
447                 gmac_dev_mm.p_tx_buffer = NULL;\r
448         }\r
449         #else\r
450         {\r
451                 gmac_dev_mm.p_tx_buffer = gs_uc_tx_buffer;\r
452         }\r
453         #endif\r
454         gmac_dev_mm.p_tx_dscr = gs_tx_desc;\r
455         gmac_dev_mm.us_tx_size = GMAC_TX_BUFFERS;\r
456 \r
457         gmac_init_mem(p_gmac, p_gmac_dev, &gmac_dev_mm\r
458 #if( GMAC_USES_TX_CALLBACK != 0 )\r
459                 , gs_tx_callback\r
460 #endif\r
461                 );\r
462 \r
463         gmac_set_address(p_gmac, 0, p_opt->uc_mac_addr);\r
464 }\r
465 \r
466 /**\r
467  * \brief Frames can be read from the GMAC in multiple sections.\r
468  *\r
469  * Returns > 0 if a complete frame is available\r
470  * It also it cleans up incomplete older frames\r
471  */\r
472 \r
473 static uint32_t gmac_dev_poll(gmac_device_t* p_gmac_dev)\r
474 {\r
475         uint32_t ulReturn = 0;\r
476         int32_t ulIndex = p_gmac_dev->ul_rx_idx;\r
477         gmac_rx_descriptor_t *pxHead = &p_gmac_dev->p_rx_dscr[ulIndex];\r
478 \r
479         /* Discard any incomplete frames */\r
480         while ((pxHead->addr.val & GMAC_RXD_OWNERSHIP) &&\r
481                         (pxHead->status.val & GMAC_RXD_SOF) == 0) {\r
482                 pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP);\r
483                 circ_inc32 (&ulIndex, p_gmac_dev->ul_rx_list_size);\r
484                 pxHead = &p_gmac_dev->p_rx_dscr[ulIndex];\r
485                 p_gmac_dev->ul_rx_idx = ulIndex;\r
486                 #if( GMAC_STATS != 0 )\r
487                 {\r
488                         gmacStats.incompCount++;\r
489                 }\r
490                 #endif\r
491         }\r
492 \r
493         while ((pxHead->addr.val & GMAC_RXD_OWNERSHIP) != 0) {\r
494                 if ((pxHead->status.val & GMAC_RXD_EOF) != 0) {\r
495                         /* Here a complete frame has been seen with SOF and EOF */\r
496                         ulReturn = pxHead->status.bm.len;\r
497                         break;\r
498                 }\r
499                 circ_inc32 (&ulIndex, p_gmac_dev->ul_rx_list_size);\r
500                 pxHead = &p_gmac_dev->p_rx_dscr[ulIndex];\r
501                 if ((pxHead->addr.val & GMAC_RXD_OWNERSHIP) == 0) {\r
502                         /* CPU is not the owner (yet) */\r
503                         break;\r
504                 }\r
505                 if ((pxHead->status.val & GMAC_RXD_SOF) != 0) {\r
506                         /* Strange, we found a new Start Of Frame\r
507                          * discard previous segments */\r
508                         int32_t ulPrev = p_gmac_dev->ul_rx_idx;\r
509                         pxHead = &p_gmac_dev->p_rx_dscr[ulPrev];\r
510                         do {\r
511                                 pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP);\r
512                                 circ_inc32 (&ulPrev, p_gmac_dev->ul_rx_list_size);\r
513                                 pxHead = &p_gmac_dev->p_rx_dscr[ulPrev];\r
514                                 #if( GMAC_STATS != 0 )\r
515                                 {\r
516                                         gmacStats.truncCount++;\r
517                                 }\r
518                                 #endif\r
519                         } while (ulPrev != ulIndex);\r
520                         p_gmac_dev->ul_rx_idx = ulIndex;\r
521                 }\r
522         }\r
523         return ulReturn;\r
524 }\r
525 \r
526 /**\r
527  * \brief Frames can be read from the GMAC in multiple sections.\r
528  * Read ul_frame_size bytes from the GMAC receive buffers to pcTo.\r
529  * p_rcv_size is the size of the entire frame.  Generally gmac_read\r
530  * will be repeatedly called until the sum of all the ul_frame_size equals\r
531  * the value of p_rcv_size.\r
532  *\r
533  * \param p_gmac_dev Pointer to the GMAC device instance.\r
534  * \param p_frame Address of the frame buffer.\r
535  * \param ul_frame_size  Length of the frame.\r
536  * \param p_rcv_size   Received frame size.\r
537  *\r
538  * \return GMAC_OK if receiving frame successfully, otherwise failed.\r
539  */\r
540 uint32_t gmac_dev_read(gmac_device_t* p_gmac_dev, uint8_t* p_frame,\r
541                 uint32_t ul_frame_size, uint32_t* p_rcv_size)\r
542 {\r
543         int32_t nextIdx;        /* A copy of the Rx-index 'ul_rx_idx' */\r
544         int32_t bytesLeft = gmac_dev_poll (p_gmac_dev);\r
545         gmac_rx_descriptor_t *pxHead;\r
546 \r
547         if (bytesLeft == 0 )\r
548         {\r
549                 return GMAC_RX_NULL;\r
550         }\r
551 \r
552         /* gmac_dev_poll has confirmed that there is a complete frame at\r
553          * the current position 'ul_rx_idx'\r
554          */\r
555         nextIdx = p_gmac_dev->ul_rx_idx;\r
556 \r
557         /* Read +2 bytes because buffers are aligned at -2 bytes */\r
558         bytesLeft = min( bytesLeft + 2, ( int32_t )ul_frame_size );\r
559 \r
560         /* The frame will be copied in 1 or 2 memcpy's */\r
561         if( ( p_frame != NULL ) && ( bytesLeft != 0 ) )\r
562         {\r
563         const uint8_t *source;\r
564         int32_t left;\r
565         int32_t toCopy;\r
566 \r
567                 source = p_gmac_dev->p_rx_buffer + nextIdx * GMAC_RX_UNITSIZE;\r
568                 left = bytesLeft;\r
569                 toCopy = ( p_gmac_dev->ul_rx_list_size - nextIdx ) * GMAC_RX_UNITSIZE;\r
570                 if(toCopy > left )\r
571                 {\r
572                         toCopy = left;\r
573                 }\r
574                 memcpy (p_frame, source, toCopy);\r
575                 left -= toCopy;\r
576 \r
577                 if( left != 0ul )\r
578                 {\r
579                         memcpy (p_frame + toCopy, (void*)p_gmac_dev->p_rx_buffer, left);\r
580                 }\r
581         }\r
582 \r
583         do\r
584         {\r
585                 pxHead = &p_gmac_dev->p_rx_dscr[nextIdx];\r
586                 pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP);\r
587                 circ_inc32 (&nextIdx, p_gmac_dev->ul_rx_list_size);\r
588         } while ((pxHead->status.val & GMAC_RXD_EOF) == 0);\r
589 \r
590         p_gmac_dev->ul_rx_idx = nextIdx;\r
591 \r
592         *p_rcv_size = bytesLeft;\r
593 \r
594         return GMAC_OK;\r
595 }\r
596 \r
597 \r
598 extern void vGMACGenerateChecksum( uint8_t *apBuffer );\r
599 \r
600 /**\r
601  * \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the\r
602  * GMAC Tx buffers, and then indicates to the GMAC that the buffer is ready.\r
603  * If lEndOfFrame is true then the data being copied is the end of the frame\r
604  * and the frame can be transmitted.\r
605  *\r
606  * \param p_gmac_dev Pointer to the GMAC device instance.\r
607  * \param p_buffer       Pointer to the data buffer.\r
608  * \param ul_size    Length of the frame.\r
609  * \param func_tx_cb  Transmit callback function.\r
610  *\r
611  * \return Length sent.\r
612  */\r
613 uint32_t gmac_dev_write(gmac_device_t* p_gmac_dev, void *p_buffer,\r
614                 uint32_t ul_size, gmac_dev_tx_cb_t func_tx_cb)\r
615 {\r
616 \r
617         volatile gmac_tx_descriptor_t *p_tx_td;\r
618 #if( GMAC_USES_TX_CALLBACK != 0 )\r
619         volatile gmac_dev_tx_cb_t *p_func_tx_cb;\r
620 #endif\r
621 \r
622         Gmac *p_hw = p_gmac_dev->p_hw;\r
623 \r
624 #if( GMAC_USES_TX_CALLBACK == 0 )\r
625         ( void )func_tx_cb;\r
626 #endif\r
627 \r
628         /* Check parameter */\r
629         if (ul_size > GMAC_TX_UNITSIZE) {\r
630                 return GMAC_PARAM;\r
631         }\r
632 \r
633         /* Pointers to the current transmit descriptor */\r
634         p_tx_td = &p_gmac_dev->p_tx_dscr[p_gmac_dev->l_tx_head];\r
635 \r
636         /* If no free TxTd, buffer can't be sent, schedule the wakeup callback */\r
637 //      if (CIRC_SPACE(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail,\r
638 //                                      p_gmac_dev->ul_tx_list_size) == 0)\r
639         {\r
640                 if ((p_tx_td->status.val & GMAC_TXD_USED) == 0)\r
641                         return GMAC_TX_BUSY;\r
642         }\r
643 #if( GMAC_USES_TX_CALLBACK != 0 )\r
644         /* Pointers to the current Tx callback */\r
645         p_func_tx_cb = &p_gmac_dev->func_tx_cb_list[p_gmac_dev->l_tx_head];\r
646 #endif\r
647 \r
648         /* Set up/copy data to transmission buffer */\r
649         if (p_buffer && ul_size) {\r
650                 /* Driver manages the ring buffer */\r
651                 /* Calculating the checksum here is faster than calculating it from the GMAC buffer\r
652                  * because withing p_buffer, it is well aligned */\r
653                 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )\r
654                 {\r
655                         /* Zero-copy... */\r
656                         p_tx_td->addr = ( uint32_t ) p_buffer;\r
657                 }\r
658                 #else\r
659                 {\r
660                         /* Or Memcopy... */\r
661                         memcpy((void *)p_tx_td->addr, p_buffer, ul_size);\r
662                 }\r
663                 #endif /* ipconfigZERO_COPY_TX_DRIVER */\r
664                 vGMACGenerateChecksum( ( uint8_t * ) p_tx_td->addr );\r
665         }\r
666 \r
667 #if( GMAC_USES_TX_CALLBACK != 0 )\r
668         /* Tx callback */\r
669         *p_func_tx_cb = func_tx_cb;\r
670 #endif\r
671 \r
672         /* Update transmit descriptor status */\r
673 \r
674         /* The buffer size defined is the length of ethernet frame,\r
675            so it's always the last buffer of the frame. */\r
676         if( p_gmac_dev->l_tx_head == ( int32_t )( p_gmac_dev->ul_tx_list_size - 1 ) )\r
677         {\r
678                 /* No need to 'and' with GMAC_TXD_LEN_MASK because ul_size has been checked */\r
679                 p_tx_td->status.val =\r
680                         ul_size | GMAC_TXD_LAST | GMAC_TXD_WRAP;\r
681         } else {\r
682                 p_tx_td->status.val =\r
683                         ul_size | GMAC_TXD_LAST;\r
684         }\r
685 \r
686         circ_inc32( &p_gmac_dev->l_tx_head, p_gmac_dev->ul_tx_list_size );\r
687 \r
688         /* Now start to transmit if it is still not done */\r
689         gmac_start_transmission(p_hw);\r
690 \r
691         return GMAC_OK;\r
692 }\r
693 \r
694 /**\r
695  * \brief Get current load of transmit.\r
696  *\r
697  * \param p_gmac_dev Pointer to the GMAC device instance.\r
698  *\r
699  * \return Current load of transmit.\r
700  */\r
701 #if( GMAC_USES_TX_CALLBACK != 0 )\r
702 /* Without defining GMAC_USES_TX_CALLBACK, l_tx_tail won't be updated */\r
703 uint32_t gmac_dev_get_tx_load(gmac_device_t* p_gmac_dev)\r
704 {\r
705         uint16_t us_head = p_gmac_dev->l_tx_head;\r
706         uint16_t us_tail = p_gmac_dev->l_tx_tail;\r
707         return CIRC_CNT(us_head, us_tail, p_gmac_dev->ul_tx_list_size);\r
708 }\r
709 #endif\r
710 \r
711 /**\r
712  * \brief Register/Clear RX callback. Callback will be invoked after the next received\r
713  * frame.\r
714  *\r
715  * When gmac_dev_read() returns GMAC_RX_NULL, the application task calls\r
716  * gmac_dev_set_rx_callback() to register func_rx_cb() callback and enters suspend state.\r
717  * The callback is in charge to resume the task once a new frame has been\r
718  * received. The next time gmac_dev_read() is called, it will be successful.\r
719  *\r
720  * This function is usually invoked from the RX callback itself with NULL\r
721  * callback, to unregister. Once the callback has resumed the application task,\r
722  * there is no need to invoke the callback again.\r
723  *\r
724  * \param p_gmac_dev Pointer to the GMAC device instance.\r
725  * \param func_tx_cb  Receive callback function.\r
726  */\r
727 void gmac_dev_set_rx_callback(gmac_device_t* p_gmac_dev,\r
728                 gmac_dev_rx_cb_t func_rx_cb)\r
729 {\r
730         Gmac *p_hw = p_gmac_dev->p_hw;\r
731 \r
732         if (func_rx_cb == NULL) {\r
733                 gmac_disable_interrupt(p_hw, GMAC_IDR_RCOMP);\r
734                 p_gmac_dev->func_rx_cb = NULL;\r
735         } else {\r
736                 p_gmac_dev->func_rx_cb = func_rx_cb;\r
737                 gmac_enable_interrupt(p_hw, GMAC_IER_RCOMP);\r
738         }\r
739 }\r
740 \r
741 /**\r
742  *  \brief Register/Clear TX wakeup callback.\r
743  *\r
744  * When gmac_dev_write() returns GMAC_TX_BUSY (all transmit descriptor busy), the application\r
745  * task calls gmac_dev_set_tx_wakeup_callback() to register func_wakeup() callback and\r
746  * enters suspend state. The callback is in charge to resume the task once\r
747  * several transmit descriptors have been released. The next time gmac_dev_write() will be called,\r
748  * it shall be successful.\r
749  *\r
750  * This function is usually invoked with NULL callback from the TX wakeup\r
751  * callback itself, to unregister. Once the callback has resumed the\r
752  * application task, there is no need to invoke the callback again.\r
753  *\r
754  * \param p_gmac_dev   Pointer to GMAC device instance.\r
755  * \param func_wakeup    Pointer to wakeup callback function.\r
756  * \param uc_threshold Number of free transmit descriptor before wakeup callback invoked.\r
757  *\r
758  * \return GMAC_OK, GMAC_PARAM on parameter error.\r
759  */\r
760 #if( GMAC_USES_WAKEUP_CALLBACK )\r
761 uint8_t gmac_dev_set_tx_wakeup_callback(gmac_device_t* p_gmac_dev,\r
762                 gmac_dev_wakeup_cb_t func_wakeup_cb, uint8_t uc_threshold)\r
763 {\r
764         if (func_wakeup_cb == NULL) {\r
765                 p_gmac_dev->func_wakeup_cb = NULL;\r
766         } else {\r
767                 if (uc_threshold <= p_gmac_dev->ul_tx_list_size) {\r
768                         p_gmac_dev->func_wakeup_cb = func_wakeup_cb;\r
769                         p_gmac_dev->uc_wakeup_threshold = uc_threshold;\r
770                 } else {\r
771                         return GMAC_PARAM;\r
772                 }\r
773         }\r
774 \r
775         return GMAC_OK;\r
776 }\r
777 #endif /* GMAC_USES_WAKEUP_CALLBACK */\r
778 \r
779 /**\r
780  * \brief Reset TX & RX queue & statistics.\r
781  *\r
782  * \param p_gmac_dev   Pointer to GMAC device instance.\r
783  */\r
784 void gmac_dev_reset(gmac_device_t* p_gmac_dev)\r
785 {\r
786         Gmac *p_hw = p_gmac_dev->p_hw;\r
787 \r
788         gmac_reset_rx_mem(p_gmac_dev);\r
789         gmac_reset_tx_mem(p_gmac_dev);\r
790         gmac_network_control(p_hw, GMAC_NCR_TXEN | GMAC_NCR_RXEN\r
791                         | GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT);\r
792 }\r
793 \r
794 void gmac_dev_halt(Gmac* p_gmac);\r
795 \r
796 void gmac_dev_halt(Gmac* p_gmac)\r
797 {\r
798         gmac_network_control(p_gmac, GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT);\r
799         gmac_disable_interrupt(p_gmac, ~0u);\r
800 }\r
801 \r
802 \r
803 /**\r
804  * \brief GMAC Interrupt handler.\r
805  *\r
806  * \param p_gmac_dev   Pointer to GMAC device instance.\r
807  */\r
808 \r
809 #if( GMAC_STATS != 0 )\r
810         extern int logPrintf( const char *pcFormat, ... );\r
811 \r
812         void gmac_show_irq_counts ()\r
813         {\r
814                 int index;\r
815                 for (index = 0; index < ARRAY_SIZE(intPairs); index++) {\r
816                         if (gmacStats.intStatus[intPairs[index].index]) {\r
817                                 logPrintf("%s : %6u\n", intPairs[index].name, gmacStats.intStatus[intPairs[index].index]);\r
818                         }\r
819                 }\r
820         }\r
821 #endif\r
822 \r
823 void gmac_handler(gmac_device_t* p_gmac_dev)\r
824 {\r
825         Gmac *p_hw = p_gmac_dev->p_hw;\r
826 \r
827 #if( GMAC_USES_TX_CALLBACK != 0 )\r
828         gmac_tx_descriptor_t *p_tx_td;\r
829         gmac_dev_tx_cb_t *p_tx_cb = NULL;\r
830         uint32_t ul_tx_status_flag;\r
831 #endif\r
832 #if( GMAC_STATS != 0 )\r
833         int index;\r
834 #endif\r
835 \r
836         /* volatile */ uint32_t ul_isr;\r
837         /* volatile */ uint32_t ul_rsr;\r
838         /* volatile */ uint32_t ul_tsr;\r
839 \r
840         ul_isr = gmac_get_interrupt_status(p_hw);\r
841         ul_rsr = gmac_get_rx_status(p_hw);\r
842         ul_tsr = gmac_get_tx_status(p_hw);\r
843 \r
844 /*      Why clear bits that are ignored anyway ? */\r
845 /*      ul_isr &= ~(gmac_get_interrupt_mask(p_hw) | 0xF8030300); */\r
846         #if( GMAC_STATS != 0 )\r
847         {\r
848                 for (index = 0; index < ARRAY_SIZE(intPairs); index++) {\r
849                         if (ul_isr & intPairs[index].mask)\r
850                                 gmacStats.intStatus[intPairs[index].index]++;\r
851                 }\r
852         }\r
853         #endif /* GMAC_STATS != 0 */\r
854 \r
855         /* RX packet */\r
856         if ((ul_isr & GMAC_ISR_RCOMP) || (ul_rsr & (GMAC_RSR_REC|GMAC_RSR_RXOVR|GMAC_RSR_BNA))) {\r
857                 /* Clear status */\r
858                 gmac_clear_rx_status(p_hw, ul_rsr);\r
859 \r
860                 if (ul_isr & GMAC_ISR_RCOMP)\r
861                         ul_rsr |= GMAC_RSR_REC;\r
862                 /* Invoke callbacks which can be useful to wake op a task */\r
863                 if (p_gmac_dev->func_rx_cb) {\r
864                         p_gmac_dev->func_rx_cb(ul_rsr);\r
865                 }\r
866         }\r
867 \r
868         /* TX packet */\r
869         if ((ul_isr & GMAC_ISR_TCOMP) || (ul_tsr & (GMAC_TSR_TXCOMP|GMAC_TSR_COL|GMAC_TSR_RLE|GMAC_TSR_UND))) {\r
870 \r
871 #if( GMAC_USES_TX_CALLBACK != 0 )\r
872                 ul_tx_status_flag = GMAC_TSR_TXCOMP;\r
873 #endif\r
874                 /* A frame transmitted */\r
875 \r
876                 /* Check RLE */\r
877                 if (ul_tsr & GMAC_TSR_RLE) {\r
878                         /* Status RLE & Number of discarded buffers */\r
879 #if( GMAC_USES_TX_CALLBACK != 0 )\r
880                         ul_tx_status_flag = GMAC_TSR_RLE | CIRC_CNT(p_gmac_dev->l_tx_head,\r
881                                         p_gmac_dev->l_tx_tail, p_gmac_dev->ul_tx_list_size);\r
882                         p_tx_cb = &p_gmac_dev->func_tx_cb_list[p_gmac_dev->l_tx_tail];\r
883 #endif\r
884                         gmac_reset_tx_mem(p_gmac_dev);\r
885                         gmac_enable_transmit(p_hw, 1);\r
886                 }\r
887                 /* Clear status */\r
888                 gmac_clear_tx_status(p_hw, ul_tsr);\r
889 \r
890 #if( GMAC_USES_TX_CALLBACK != 0 )\r
891                 if (!CIRC_EMPTY(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail)) {\r
892                         /* Check the buffers */\r
893                         do {\r
894                                 p_tx_td = &p_gmac_dev->p_tx_dscr[p_gmac_dev->l_tx_tail];\r
895                                 p_tx_cb = &p_gmac_dev->func_tx_cb_list[p_gmac_dev->l_tx_tail];\r
896                                 /* Any error? Exit if buffer has not been sent yet */\r
897                                 if ((p_tx_td->status.val & GMAC_TXD_USED) == 0) {\r
898                                         break;\r
899                                 }\r
900 \r
901                                 /* Notify upper layer that a packet has been sent */\r
902                                 if (*p_tx_cb) {\r
903                                         (*p_tx_cb) (ul_tx_status_flag, (void*)p_tx_td->addr);\r
904                                         #if( ipconfigZERO_COPY_TX_DRIVER != 0 )\r
905                                         {\r
906                                                 p_tx_td->addr = 0ul;\r
907                                         }\r
908                                         #endif /* ipconfigZERO_COPY_TX_DRIVER */\r
909                                 }\r
910 \r
911                                 circ_inc32(&p_gmac_dev->l_tx_tail, p_gmac_dev->ul_tx_list_size);\r
912                         } while (CIRC_CNT(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail,\r
913                                                         p_gmac_dev->ul_tx_list_size));\r
914                 }\r
915 \r
916                 if (ul_tsr & GMAC_TSR_RLE) {\r
917                         /* Notify upper layer RLE */\r
918                         if (*p_tx_cb) {\r
919                                 (*p_tx_cb) (ul_tx_status_flag, NULL);\r
920                         }\r
921                 }\r
922 #endif /* GMAC_USES_TX_CALLBACK */\r
923 \r
924 #if( GMAC_USES_WAKEUP_CALLBACK )\r
925                 /* If a wakeup has been scheduled, notify upper layer that it can\r
926                    send other packets, and the sending will be successful. */\r
927                 if ((CIRC_SPACE(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail,\r
928                                 p_gmac_dev->ul_tx_list_size) >= p_gmac_dev->uc_wakeup_threshold)\r
929                                 && p_gmac_dev->func_wakeup_cb) {\r
930                         p_gmac_dev->func_wakeup_cb();\r
931                 }\r
932 #endif\r
933         }\r
934 }\r
935 \r
936 //@}\r
937 \r
938 /// @cond 0\r
939 /**INDENT-OFF**/\r
940 #ifdef __cplusplus\r
941 }\r
942 #endif\r
943 /**INDENT-ON**/\r
944 /// @endcond\r