]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ksz8851snl/NetworkInterface.c
2ba95799e8b7e13273dfc261c5635e28d0a96483
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / portable / NetworkInterface / ksz8851snl / NetworkInterface.c
1 /*\r
2  * FreeRTOS+TCP V2.0.1\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * http://www.FreeRTOS.org\r
23  * http://aws.amazon.com/freertos\r
24  *\r
25  * 1 tab == 4 spaces!\r
26  */\r
27 \r
28 /* Standard includes. */\r
29 #include <stdint.h>\r
30 #include <stdio.h>\r
31 #include <stdlib.h>\r
32 #include <stdarg.h>\r
33 \r
34 /* FreeRTOS includes. */\r
35 #include "FreeRTOS.h"\r
36 #include "task.h"\r
37 #include "queue.h"\r
38 #include "semphr.h"\r
39 \r
40 /* FreeRTOS+TCP includes. */\r
41 #include "FreeRTOS_IP.h"\r
42 #include "FreeRTOS_Sockets.h"\r
43 #include "FreeRTOS_IP_Private.h"\r
44 #include "NetworkBufferManagement.h"\r
45 #include "NetworkInterface.h"\r
46 \r
47 #include "sam4e_xplained_pro.h"\r
48 #include "hr_gettime.h"\r
49 #include "conf_eth.h"\r
50 #include "ksz8851snl.h"\r
51 #include "ksz8851snl_reg.h"\r
52 \r
53 /* Some files from the Atmel Software Framework */\r
54 #include <sysclk.h>\r
55 #include <pdc/pdc.h>\r
56 #include <spi/spi.h>\r
57 \r
58 /*\r
59         Sending a packet:\r
60 \r
61                 1) Called by UP-task, add buffer to the TX-list:\r
62                         xNetworkInterfaceOutput()\r
63                                 tx_buffers[ us_tx_head ] = pxNetworkBuffer;\r
64                                 tx_busy[ us_tx_head ] = pdTRUE;\r
65                                 us_tx_head++;\r
66 \r
67                 2) Called by EMAC-Task: start SPI transfer\r
68                         ksz8851snl_update()\r
69                         if( ul_spi_pdc_status == SPI_PDC_IDLE )\r
70                         {\r
71                                 if( ( tx_busy[ us_tx_tail ] != pdFALSE ) &&\r
72                                         ( us_pending_frame == 0 ) &&\r
73                                         ( ul_had_intn_interrupt == 0 ) )\r
74                                 {\r
75                                         // disable all interrupts.\r
76                                         ksz8851_reg_write( REG_INT_MASK, 0 );\r
77                                         Bring KSZ8851SNL_CSN_GPIO low\r
78                                         ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength );\r
79                                         ul_spi_pdc_status = SPI_PDC_TX_START;\r
80                                         tx_cur_buffer = pxNetworkBuffer;\r
81                                 }\r
82                         }\r
83                 3) Wait for SPI RXBUFF interrupt\r
84                         SPI_Handler()\r
85                                 if( ul_spi_pdc_status == SPI_PDC_TX_START )\r
86                                 {\r
87                                         if( SPI_Status & SPI_SR_RXBUFF )\r
88                                         {\r
89                                                 ul_spi_pdc_status = SPI_PDC_TX_COMPLETE;\r
90                                         }\r
91                                 }\r
92 \r
93                 4) Called by EMAC-Task: finish SPI transfer\r
94                         ksz8851snl_update()\r
95                                 if( ul_spi_pdc_status == SPI_PDC_TX_COMPLETE )\r
96                                 {\r
97                                         ul_spi_pdc_status = SPI_PDC_IDLE;\r
98                                         Bring KSZ8851SNL_CSN_GPIO high\r
99                                         // TX step12: disable TXQ write access.\r
100                                         ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );\r
101                                         // TX step12.1: enqueue frame in TXQ.\r
102                                         ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE );\r
103 \r
104                                         // RX step13: enable INT_RX flag.\r
105                                         ksz8851_reg_write( REG_INT_MASK, INT_RX );\r
106 \r
107                                         // Buffer sent, free the corresponding buffer and mark descriptor as owned by software.\r
108                                         vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );\r
109 \r
110                                         tx_buffers[ us_tx_tail ] = NULL;\r
111                                         tx_busy[ us_tx_tail ] = pdFALSE;\r
112                                         us_tx_tail++\r
113                                 }\r
114 \r
115         Receiving a packet:\r
116 \r
117                 1) Wait for a INTN interrupt\r
118                         INTN_Handler()\r
119                                 ul_had_intn_interrupt = 1\r
120                                 vTaskNotifyGiveFromISR();       // Wake up the EMAC task\r
121 \r
122                 2) Called by EMAC-Task: check for new fragments and start SPI transfer\r
123                         ksz8851snl_update()\r
124                                 if( ul_spi_pdc_status == SPI_PDC_IDLE )\r
125                                 {\r
126                                         if( ( ul_had_intn_interrupt != 0 ) || ( us_pending_frame > 0 ) )\r
127                                         {\r
128                                                 if( us_pending_frame == 0 )\r
129                                                 {\r
130                                                         us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8;\r
131                                                         if( us_pending_frame == 0 )\r
132                                                         {\r
133                                                                 break;\r
134                                                         }\r
135                                                 }\r
136                                                 // RX step2: disable all interrupts.\r
137                                                 ksz8851_reg_write( REG_INT_MASK, 0 );\r
138                                                 Check if there is a valid packet: REG_RX_FHR_STATUS\r
139                                                 Read the length of the next fragment: REG_RX_FHR_BYTE_CNT\r
140                                                 ul_spi_pdc_status = SPI_PDC_RX_START;\r
141                                                 gpio_set_pin_low(KSZ8851SNL_CSN_GPIO);\r
142                                                 // Start SPI data transfer\r
143                                                 ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer, xReadLength );\r
144                                         }\r
145                                 }\r
146 \r
147                 3) Wait for SPI RXBUFF interrupt\r
148                         SPI_Handler()\r
149                         if( ul_spi_pdc_status == SPI_PDC_RX_START:\r
150                         {\r
151                                 if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 )\r
152                                 {\r
153                                         // Transfer complete, disable SPI RXBUFF interrupt.\r
154                                         spi_disable_interrupt( KSZ8851SNL_SPI, SPI_IDR_RXBUFF );\r
155 \r
156                                         ul_spi_pdc_status = SPI_PDC_RX_COMPLETE;\r
157                                 }\r
158                         }\r
159                 }\r
160 \r
161                 4) Finish SPI transfer\r
162                         ksz8851snl_update()\r
163                                 if( ul_spi_pdc_status == SPI_PDC_RX_COMPLETE )\r
164                                 {\r
165                                         ul_spi_pdc_status = SPI_PDC_IDLE;\r
166                                         Bring KSZ8851SNL_CSN_GPIO high\r
167                                         // RX step21: end RXQ read access.\r
168                                         ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START);\r
169                                         // RX step22-23: update frame count to be read.\r
170                                         us_pending_frame--\r
171                                         // RX step24: enable INT_RX flag if transfer complete.\r
172                                         if( us_pending_frame == 0 )\r
173                                         {\r
174                                                 // Allow more RX interrupts.\r
175                                                 ksz8851_reg_write( REG_INT_MASK, INT_RX );\r
176                                         }\r
177 \r
178                                         // Mark descriptor ready to be read.\r
179                                         rx_ready[ rxHead ] = pdTRUE;\r
180                                         rxHead++\r
181                                 }\r
182 */\r
183 \r
184 #define PHY_REG_00_BMCR            0x00 // Basic mode control register\r
185 #define PHY_REG_01_BMSR            0x01 // Basic mode status register\r
186 #define PHY_REG_02_PHYSID1         0x02 // PHYS ID 1\r
187 #define PHY_REG_03_PHYSID2         0x03 // PHYS ID 2\r
188 #define PHY_REG_04_ADVERTISE       0x04 // Advertisement control reg\r
189 #define PHY_REG_05_LPA             0x05 // Link partner ability reg\r
190 #define PHY_REG_06_ANER            0x06 //      6       RW              Auto-Negotiation Expansion Register\r
191 #define PHY_REG_07_ANNPTR          0x07 //      7       RW              Auto-Negotiation Next Page TX\r
192 #define PHY_REG_08_RESERVED0       0x08 // 0x08..0x0Fh  8-15    RW              RESERVED\r
193 \r
194 #define BMSR_LINK_STATUS            0x0004  //!< Link status\r
195 \r
196 #ifndef PHY_LS_HIGH_CHECK_TIME_MS\r
197         /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not\r
198         receiving packets. */\r
199         #define PHY_LS_HIGH_CHECK_TIME_MS       15000\r
200 #endif\r
201 \r
202 #ifndef PHY_LS_LOW_CHECK_TIME_MS\r
203         /* Check if the LinkSStatus in the PHY is still low every second. */\r
204         #define PHY_LS_LOW_CHECK_TIME_MS        1000\r
205 #endif\r
206 \r
207 /* Interrupt events to process.  Currently only the Rx event is processed\r
208 although code for other events is included to allow for possible future\r
209 expansion. */\r
210 #define EMAC_IF_RX_EVENT                                1UL\r
211 #define EMAC_IF_TX_EVENT                                2UL\r
212 #define EMAC_IF_ERR_EVENT                               4UL\r
213 #define EMAC_IF_ALL_EVENT                               ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )\r
214 \r
215 #define ETHERNET_CONF_PHY_ADDR  BOARD_GMAC_PHY_ADDR\r
216 \r
217 #ifdef ipconfigHAS_TX_CRC_OFFLOADING\r
218         #undef ipconfigHAS_TX_CRC_OFFLOADING\r
219 #endif\r
220 /* Override this define because the KSZ8851 is programmed to set all outgoing CRC's */\r
221 #define ipconfigHAS_TX_CRC_OFFLOADING   1\r
222 \r
223 #ifndef EMAC_MAX_BLOCK_TIME_MS\r
224         #define EMAC_MAX_BLOCK_TIME_MS  100ul\r
225 #endif\r
226 \r
227 /* Default the size of the stack used by the EMAC deferred handler task to 4x\r
228 the size of the stack used by the idle task - but allow this to be overridden in\r
229 FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */\r
230 #ifndef configEMAC_TASK_STACK_SIZE\r
231         #define configEMAC_TASK_STACK_SIZE ( 6 * configMINIMAL_STACK_SIZE )\r
232 #endif\r
233 \r
234 #define SPI_PDC_IDLE                    0\r
235 #define SPI_PDC_RX_START                1\r
236 #define SPI_PDC_TX_ERROR                2\r
237 #define SPI_PDC_RX_COMPLETE             3\r
238 #define SPI_PDC_TX_START                4\r
239 #define SPI_PDC_RX_ERROR                5\r
240 #define SPI_PDC_TX_COMPLETE             6\r
241 \r
242 /**\r
243  * ksz8851snl driver structure.\r
244  */\r
245 typedef struct {\r
246         /** Set to 1 when owner is software (ready to read), 0 for Micrel. */\r
247         uint32_t rx_ready[MICREL_RX_BUFFERS];\r
248         /** Set to 1 when owner is Micrel, 0 for software. */\r
249         uint32_t tx_busy[MICREL_TX_BUFFERS];\r
250         /** RX NetworkBufferDescriptor_t pointer list */\r
251         NetworkBufferDescriptor_t *rx_buffers[MICREL_RX_BUFFERS];\r
252         /** TX NetworkBufferDescriptor_t pointer list */\r
253         NetworkBufferDescriptor_t *tx_buffers[MICREL_TX_BUFFERS];\r
254         NetworkBufferDescriptor_t *tx_cur_buffer;\r
255 \r
256         /** Circular buffer head pointer for packet received. */\r
257         uint32_t us_rx_head;\r
258         /** Circular buffer tail pointer for packet to be read. */\r
259         uint32_t us_rx_tail;\r
260         /** Circular buffer head pointer by upper layer (buffer to be sent). */\r
261         uint32_t us_tx_head;\r
262         /** Circular buffer tail pointer incremented by handlers (buffer sent). */\r
263         uint32_t us_tx_tail;\r
264 \r
265         uint32_t ul_total_tx;\r
266         uint32_t ul_total_rx;\r
267         uint32_t tx_space;\r
268 \r
269         /** Still experimental: hash table to allow certain multicast addresses. */\r
270         uint16_t pusHashTable[ 4 ];\r
271 \r
272         /* ul_spi_pdc_status has "SPI_PDC_xxx" values. */\r
273         volatile uint32_t ul_spi_pdc_status;\r
274 \r
275         /* ul_had_intn_interrupt becomes true within the INTN interrupt. */\r
276         volatile uint32_t ul_had_intn_interrupt;\r
277 \r
278         uint16_t us_pending_frame;\r
279 } xKSZ8851_Device_t;\r
280 \r
281 /* SPI PDC register base.\r
282 Declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */\r
283 extern Pdc *g_p_spi_pdc;\r
284 \r
285 /* Temporary buffer for PDC reception.\r
286 declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */\r
287 extern uint8_t tmpbuf[1536];\r
288 \r
289 COMPILER_ALIGNED(8)\r
290 static xKSZ8851_Device_t xMicrelDevice;\r
291 \r
292 static TaskHandle_t xTransmitHandle;\r
293 \r
294 /*-----------------------------------------------------------*/\r
295 \r
296 /*\r
297  * Wait a fixed time for the link status to indicate the network is up.\r
298  */\r
299 static BaseType_t xGMACWaitLS( TickType_t xMaxTime );\r
300 \r
301 /*\r
302  * A deferred interrupt handler task that processes GMAC interrupts.\r
303  */\r
304 static void prvEMACHandlerTask( void *pvParameters );\r
305 \r
306 /*\r
307  * Try to obtain an Rx packet from the hardware.\r
308  */\r
309 static uint32_t prvEMACRxPoll( void );\r
310 \r
311 static inline unsigned long ulReadMDIO( unsigned uAddress );\r
312 \r
313 static void ksz8851snl_low_level_init( void );\r
314 \r
315 static NetworkBufferDescriptor_t *ksz8851snl_low_level_input( void );\r
316 \r
317 /*-----------------------------------------------------------*/\r
318 \r
319 /* Bit map of outstanding ETH interrupt events for processing.  Currently only\r
320 the Rx interrupt is handled, although code is included for other events to\r
321 enable future expansion. */\r
322 static volatile uint32_t ulISREvents;\r
323 \r
324 /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */\r
325 static uint32_t ulPHYLinkStatus = 0;\r
326 static volatile BaseType_t xGMACSwitchRequired;\r
327 \r
328 static void ksz8851snl_update( void );\r
329 \r
330 static void ksz8851snl_rx_init( void );\r
331 \r
332 static void ksz8851snl_tx_init( void );\r
333 \r
334 /* Holds the handle of the task used as a deferred interrupt processor.  The\r
335 handle is used so direct notifications can be sent to the task for all EMAC/DMA\r
336 related interrupts. */\r
337 TaskHandle_t xEMACTaskHandle = NULL;\r
338 \r
339 \r
340 /*-----------------------------------------------------------*/\r
341 \r
342 BaseType_t xNetworkInterfaceInitialise( void )\r
343 {\r
344 const TickType_t x5_Seconds = 5000UL;\r
345 \r
346         if( xEMACTaskHandle == NULL )\r
347         {\r
348                 ksz8851snl_low_level_init();\r
349 \r
350                 /* Wait at most 5 seconds for a Link Status in the PHY. */\r
351                 xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) );\r
352 \r
353                 /* The handler task is created at the highest possible priority to\r
354                 ensure the interrupt handler can return directly to it. */\r
355                 xTaskCreate( prvEMACHandlerTask, "KSZ8851", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle );\r
356                 configASSERT( xEMACTaskHandle );\r
357         }\r
358 \r
359         /* When returning non-zero, the stack will become active and\r
360     start DHCP (in configured) */\r
361         ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR );\r
362 \r
363         return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0;\r
364 }\r
365 /*-----------------------------------------------------------*/\r
366 \r
367 BaseType_t xGetPhyLinkStatus( void )\r
368 {\r
369 BaseType_t xResult;\r
370 \r
371         /* This function returns true if the Link Status in the PHY is high. */\r
372         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )\r
373         {\r
374                 xResult = pdTRUE;\r
375         }\r
376         else\r
377         {\r
378                 xResult = pdFALSE;\r
379         }\r
380 \r
381         return xResult;\r
382 }\r
383 /*-----------------------------------------------------------*/\r
384 \r
385 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t bReleaseAfterSend )\r
386 {\r
387 BaseType_t xResult = pdFALSE;\r
388 int txHead = xMicrelDevice.us_tx_head;\r
389 \r
390         /* Make sure the next descriptor is free. */\r
391         if( xMicrelDevice.tx_busy[ txHead ] != pdFALSE )\r
392         {\r
393                 /* All TX buffers busy. */\r
394         }\r
395         else if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 )\r
396         {\r
397                 /* Output: LS low. */\r
398         }\r
399         else\r
400         {\r
401                 /* Pass the packet. */\r
402                 xMicrelDevice.tx_buffers[ txHead ] = pxNetworkBuffer;\r
403                 /* The descriptor is now owned by Micrel. */\r
404                 xMicrelDevice.tx_busy[ txHead ] = pdTRUE;\r
405 \r
406                 /* Move the head pointer. */\r
407                 if( ++txHead == MICREL_TX_BUFFERS )\r
408                 {\r
409                         txHead = 0;\r
410                 }\r
411                 xMicrelDevice.us_tx_head = txHead;\r
412                 if( xEMACTaskHandle != NULL )\r
413                 {\r
414                         xTaskNotifyGive( xEMACTaskHandle );\r
415                 }\r
416 \r
417         #if( ipconfigZERO_COPY_TX_DRIVER != 1 )\r
418                 #warning Please ipconfigZERO_COPY_TX_DRIVER as 1\r
419         #endif\r
420                 configASSERT( bReleaseAfterSend != pdFALSE );\r
421                 xResult = pdTRUE;\r
422         }\r
423         if( ( xResult == pdFALSE ) && ( bReleaseAfterSend  != pdFALSE ) )\r
424         {\r
425                 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );\r
426         }\r
427         return xResult;\r
428 }\r
429 /*-----------------------------------------------------------*/\r
430 \r
431 /* This Micrel has numbered it's PHY registers in a different way.\r
432 Translate the register index. */\r
433 static int ks8851_phy_reg( int reg )\r
434 {\r
435         switch (reg) {\r
436         case PHY_REG_00_BMCR:\r
437                 return REG_PHY_CNTL;    // P1MBCR;\r
438         case PHY_REG_01_BMSR:\r
439                 return REG_PHY_STATUS;\r
440         case PHY_REG_02_PHYSID1:\r
441                 return REG_PHY_ID_LOW;\r
442         case PHY_REG_03_PHYSID2:\r
443                 return REG_PHY_ID_HIGH;\r
444         case PHY_REG_04_ADVERTISE:\r
445                 return REG_PHY_AUTO_NEGOTIATION;\r
446         case PHY_REG_05_LPA:\r
447                 return REG_PHY_REMOTE_CAPABILITY;\r
448         }\r
449 \r
450         return 0x0;\r
451 }\r
452 /*-----------------------------------------------------------*/\r
453 \r
454 static inline unsigned long ulReadMDIO( unsigned uAddress )\r
455 {\r
456 uint16_t usPHYStatus;\r
457 int ks8851_reg = ks8851_phy_reg( uAddress );\r
458 \r
459         if( ks8851_reg != 0 )\r
460         {\r
461                 usPHYStatus = ksz8851_reg_read( ks8851_reg );\r
462         }\r
463         else\r
464         {\r
465                 /* Other addresses not yet implemented. */\r
466                 usPHYStatus = 0;\r
467         }\r
468         return usPHYStatus;\r
469 }\r
470 /*-----------------------------------------------------------*/\r
471 \r
472 static BaseType_t xGMACWaitLS( TickType_t xMaxTime )\r
473 {\r
474 TickType_t xStartTime = xTaskGetTickCount();\r
475 TickType_t xEndTime;\r
476 BaseType_t xReturn;\r
477 const TickType_t xShortTime = pdMS_TO_TICKS( 100UL );\r
478 const uint32_t ulHz_Per_MHz = 1000000UL;\r
479 \r
480         for( ;; )\r
481         {\r
482                 xEndTime = xTaskGetTickCount();\r
483 \r
484                 if( ( xEndTime - xStartTime ) > xMaxTime )\r
485                 {\r
486                         /* Wated more than xMaxTime, return. */\r
487                         xReturn = pdFALSE;\r
488                         break;\r
489                 }\r
490 \r
491                 /* Check the link status again. */\r
492                 ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR );\r
493 \r
494                 if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )\r
495                 {\r
496                         /* Link is up - return. */\r
497                         xReturn = pdTRUE;\r
498                         break;\r
499                 }\r
500 \r
501                 /* Link is down - wait in the Blocked state for a short while (to allow\r
502                 other tasks to execute) before checking again. */\r
503                 vTaskDelay( xShortTime );\r
504         }\r
505 \r
506         FreeRTOS_printf( ( "xGMACWaitLS: %ld freq %lu Mz\n",\r
507                 xReturn,\r
508                 sysclk_get_cpu_hz() / ulHz_Per_MHz ) );\r
509 \r
510         return xReturn;\r
511 }\r
512 /*-----------------------------------------------------------*/\r
513 \r
514 static void vPioSetPinHigh(uint32_t ul_pin)\r
515 {\r
516         Pio *p_pio = (Pio *)((uint32_t)PIOA + (PIO_DELTA * (ul_pin >> 5)));\r
517         // Value to be driven on the I/O line: 1.\r
518         p_pio->PIO_SODR = 1 << (ul_pin & 0x1F);\r
519 }\r
520 \r
521 /**\r
522  * \brief Handler for SPI interrupt.\r
523  */\r
524 void SPI_Handler(void)\r
525 {\r
526 BaseType_t xDoWakeup = pdFALSE;\r
527 BaseType_t xKSZTaskWoken = pdFALSE;\r
528 uint32_t ulCurrentSPIStatus;\r
529 uint32_t ulEnabledSPIStatus;\r
530 \r
531         ulCurrentSPIStatus = spi_read_status( KSZ8851SNL_SPI );\r
532         ulEnabledSPIStatus = spi_read_interrupt_mask( KSZ8851SNL_SPI );\r
533         ulCurrentSPIStatus &= ulEnabledSPIStatus;\r
534         spi_disable_interrupt( KSZ8851SNL_SPI, ulCurrentSPIStatus );\r
535 \r
536 \r
537         switch( xMicrelDevice.ul_spi_pdc_status )\r
538         {\r
539                 case SPI_PDC_RX_START:\r
540                 {\r
541                         if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 )\r
542                         {\r
543                                 pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);\r
544                                 xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_ERROR;\r
545                                 xDoWakeup = pdTRUE;\r
546                         }\r
547                         else\r
548                         {\r
549                                 if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 )\r
550                                 {\r
551                                         xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_COMPLETE;\r
552                                         xDoWakeup = pdTRUE;\r
553                                 }\r
554                         }\r
555                 }\r
556                 break;\r
557 \r
558                 case SPI_PDC_TX_START:\r
559                 {\r
560                         /* Middle of TX. */\r
561                         if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 )\r
562                         {\r
563                                 pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);\r
564                                 xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_ERROR;\r
565                                 xDoWakeup = pdTRUE;\r
566                         }\r
567                         else\r
568                         {\r
569                                 if( ( ulCurrentSPIStatus & SPI_SR_ENDRX ) != 0 )\r
570                                 {\r
571                                         /* Enable RX complete interrupt. */\r
572                                         spi_enable_interrupt( KSZ8851SNL_SPI, SPI_IER_RXBUFF );\r
573                                 }\r
574                                 /* End of TX. */\r
575                                 if( ( ulCurrentSPIStatus & SPI_END_OF_TX ) != 0 )\r
576                                 {\r
577                                         xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_COMPLETE;\r
578                                         xDoWakeup = pdTRUE;\r
579                                 }\r
580                         }\r
581                 }\r
582                 break;\r
583         }       /* switch( xMicrelDevice.ul_spi_pdc_status ) */\r
584 \r
585         if( xDoWakeup != pdFALSE )\r
586         {\r
587                 if( xEMACTaskHandle != NULL )\r
588                 {\r
589                         vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xKSZTaskWoken );\r
590                 }\r
591         }\r
592         else\r
593         {\r
594         }\r
595         portEND_SWITCHING_ISR( xKSZTaskWoken );\r
596 }\r
597 /*-----------------------------------------------------------*/\r
598 \r
599 static void INTN_Handler(uint32_t id, uint32_t mask)\r
600 {\r
601 BaseType_t xKSZTaskWoken = pdFALSE;\r
602 \r
603         if( ( id == INTN_ID ) &&\r
604                 ( mask == INTN_PIN_MSK ) )\r
605         {\r
606                 /* Clear the PIO interrupt flags. */\r
607                 pio_get_interrupt_status( INTN_PIO );\r
608 \r
609                 /* Set the INTN flag. */\r
610                 xMicrelDevice.ul_had_intn_interrupt++;\r
611                 if( xEMACTaskHandle != NULL )\r
612                 {\r
613                         vTaskNotifyGiveFromISR( xEMACTaskHandle, &( xKSZTaskWoken ) );\r
614                 }\r
615         }\r
616         portEND_SWITCHING_ISR( xKSZTaskWoken );\r
617 }\r
618 /*-----------------------------------------------------------*/\r
619 \r
620 /**\r
621  * \brief Populate the RX descriptor ring buffers with pbufs.\r
622  *\r
623  * \param p_ksz8851snl_dev Pointer to driver data structure.\r
624  */\r
625 static void ksz8851snl_rx_populate_queue( void )\r
626 {\r
627         uint32_t ul_index = 0;\r
628         NetworkBufferDescriptor_t *pxNetworkBuffer;\r
629 \r
630         /* Set up the RX descriptors */\r
631         for (ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++) {\r
632                 if( xMicrelDevice.rx_buffers[ ul_index ] == NULL )\r
633                 {\r
634                         /* Allocate a new NetworkBufferDescriptor_t with the maximum size. */\r
635                         pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipconfigNETWORK_MTU + 36, 100 );\r
636                         if( pxNetworkBuffer == NULL )\r
637                         {\r
638                                 FreeRTOS_printf( ( "ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t allocation failure\n" ) );\r
639                                 configASSERT( 1 == 2 );\r
640                         }\r
641 \r
642                         /* Make sure lwIP is well configured so one NetworkBufferDescriptor_t can contain the maximum packet size. */\r
643                         //LWIP_ASSERT("ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t size too small!", pbuf_clen(pxNetworkBuffer) <= 1);\r
644 \r
645                         /* Save NetworkBufferDescriptor_t pointer to be sent to lwIP upper layer. */\r
646                         xMicrelDevice.rx_buffers[ ul_index ] = pxNetworkBuffer;\r
647                         /* Pass it to Micrel for reception. */\r
648                         xMicrelDevice.rx_ready[ ul_index ] = pdFALSE;\r
649                 }\r
650         }\r
651 }\r
652 \r
653 unsigned tx_space, wait_tx_space, tx_status, fhr_status;\r
654 unsigned rx_debug = 0;\r
655 /**\r
656  * \brief Update Micrel state machine and perform required actions.\r
657  *\r
658  * \param netif the lwIP network interface structure for this ethernetif.\r
659  */\r
660 static void ksz8851snl_update()\r
661 {\r
662         uint16_t txmir = 0;\r
663 \r
664 /* Check for free PDC. */\r
665         switch( xMicrelDevice.ul_spi_pdc_status )\r
666         {\r
667         case SPI_PDC_TX_ERROR:\r
668                 {\r
669                 uint32_t ulValue;\r
670         //              /* TX step11: end TX transfer. */\r
671                         gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );\r
672 \r
673                         vTaskDelay( 2 ); gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );\r
674                         vTaskDelay( 1 ); gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );\r
675                         vTaskDelay( 1 );\r
676 \r
677                         /* Disable asynchronous transfer mode. */\r
678                         xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;\r
679 \r
680                         /* TX step12: disable TXQ write access. */\r
681                         ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );\r
682 \r
683                         ulValue = ksz8851snl_reset_tx();\r
684 \r
685                         xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK;\r
686 \r
687                         FreeRTOS_printf( ("SPI_PDC_TX_ERROR %02X\n", ulValue ) );\r
688                 }\r
689                 break;\r
690 \r
691         case SPI_PDC_RX_ERROR:\r
692                 {\r
693                 uint32_t ulValue;\r
694                         /* TX step11: end TX transfer. */\r
695                         gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );\r
696 \r
697                         vTaskDelay( 2 ); gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );\r
698                         vTaskDelay( 1 ); gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );\r
699                         vTaskDelay( 1 );\r
700 \r
701                         /* Disable asynchronous transfer mode. */\r
702                         xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;\r
703 \r
704                         /* TX step12: disable TXQ write access. */\r
705                         ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );\r
706 \r
707                         //ulValue = ksz8851snl_reset_rx();\r
708                         ulValue = ksz8851snl_reinit();\r
709 \r
710                         xGMACWaitLS( pdMS_TO_TICKS( 5000UL ) );\r
711 \r
712                         FreeRTOS_printf( ("SPI_PDC_RX_ERROR %02X\n", ulValue ) );\r
713                 }\r
714                 break;\r
715         }\r
716         switch( xMicrelDevice.ul_spi_pdc_status )\r
717         {\r
718                 case SPI_PDC_IDLE:\r
719                 {\r
720                 int txTail = xMicrelDevice.us_tx_tail;\r
721 \r
722                         /*\r
723                          * ========================== Handle RX ==========================\r
724                          */\r
725                         if( ( xMicrelDevice.ul_had_intn_interrupt != 0 ) || ( xMicrelDevice.us_pending_frame > 0 ) )\r
726                         {\r
727                         int rxHead = xMicrelDevice.us_rx_head;\r
728                         NetworkBufferDescriptor_t *pxNetworkBuffer;\r
729 #warning try\r
730                                 xMicrelDevice.ul_had_intn_interrupt = 0;\r
731 \r
732                                 if( xMicrelDevice.us_pending_frame == 0 )\r
733                                 {\r
734                                 uint16_t int_status;\r
735                                         /* RX step1: read interrupt status for INT_RX flag. */\r
736                                         int_status = ksz8851_reg_read( REG_INT_STATUS );\r
737 \r
738 \r
739                                         /* RX step2: disable all interrupts. */\r
740                                         ksz8851_reg_write( REG_INT_MASK, 0 );\r
741 \r
742                                         /* RX step3: clear INT_RX flag. */\r
743                                         ksz8851_reg_setbits( REG_INT_STATUS, INT_RX );\r
744 \r
745                                         /* RX step4-5: check for received frames. */\r
746                                         xMicrelDevice.us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8;\r
747                                         if( xMicrelDevice.us_pending_frame == 0 )\r
748                                         {\r
749                                                 /* RX step24: enable INT_RX flag. */\r
750                                                 ksz8851_reg_write(REG_INT_MASK, INT_RX);\r
751                                                 return;\r
752                                         }\r
753                                 }\r
754 #warning try\r
755                                 xMicrelDevice.ul_had_intn_interrupt = 0;\r
756 \r
757                                 /* Now xMicrelDevice.us_pending_frame != 0 */\r
758 \r
759                                 /* Don't break Micrel state machine, wait for a free descriptor first! */\r
760                                 if( xMicrelDevice.rx_ready[ rxHead ] != pdFALSE )\r
761                                 {\r
762                                         FreeRTOS_printf( ( "ksz8851snl_update: out of free descriptor! [tail=%u head=%u]\n",\r
763                                                         xMicrelDevice.us_rx_tail, rxHead ) );\r
764                                         return;\r
765                                 }\r
766                                 pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxHead ];\r
767 \r
768                                 if( pxNetworkBuffer == NULL )\r
769                                 {\r
770                                         ksz8851snl_rx_populate_queue();\r
771                                         FreeRTOS_printf( ( "ksz8851snl_update: no buffer set [head=%u]\n", rxHead ) );\r
772                                         return;\r
773                                 }\r
774 \r
775                                 /* RX step6: get RX packet status. */\r
776                                 fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS );\r
777                                 if( ( ( fhr_status & RX_VALID ) == 0 ) || ( ( fhr_status & RX_ERRORS ) != 0 ) )\r
778                                 {\r
779                                         ksz8851_reg_setbits(REG_RXQ_CMD, RXQ_CMD_FREE_PACKET);\r
780                                         FreeRTOS_printf( ( "ksz8851snl_update: RX packet error!\n" ) );\r
781 \r
782                                         /* RX step4-5: check for received frames. */\r
783                                         xMicrelDevice.us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8;\r
784                                         if( xMicrelDevice.us_pending_frame == 0 )\r
785                                         {\r
786                                                 /* RX step24: enable INT_RX flag. */\r
787                                                 ksz8851_reg_write(REG_INT_MASK, INT_RX);\r
788                                         }\r
789                                         ulISREvents |= EMAC_IF_ERR_EVENT;\r
790                                 }\r
791                                 else\r
792                                 {\r
793                                 size_t xLength;\r
794                                         /* RX step7: read frame length. */\r
795                                         xLength = ksz8851_reg_read(REG_RX_FHR_BYTE_CNT) & RX_BYTE_CNT_MASK;\r
796 \r
797                                         /* RX step8: Drop packet if len is invalid or no descriptor available. */\r
798                                         if( xLength == 0 )\r
799                                         {\r
800                                                 ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_CMD_FREE_PACKET );\r
801                                                 FreeRTOS_printf( ( "ksz8851snl_update: RX bad len!\n" ) );\r
802                                                 ulISREvents |= EMAC_IF_ERR_EVENT;\r
803                                         }\r
804                                         else\r
805                                         {\r
806                                         size_t xReadLength = xLength;\r
807 \r
808                                                 xMicrelDevice.ul_total_rx++;\r
809                                                 /* RX step9: reset RX frame pointer. */\r
810                                                 ksz8851_reg_clrbits(REG_RX_ADDR_PTR, ADDR_PTR_MASK);\r
811 \r
812                                                 /* RX step10: start RXQ read access. */\r
813                                                 ksz8851_reg_setbits(REG_RXQ_CMD, RXQ_START);\r
814                                                 /* RX step11-17: start asynchronous FIFO read operation. */\r
815                                                 xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_START;\r
816                                                 gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );\r
817                                                 if( ( xReadLength & ( sizeof( size_t ) - 1 ) ) != 0 )\r
818                                                 {\r
819                                                         xReadLength = ( xReadLength | ( sizeof( size_t ) - 1 ) ) + 1;\r
820                                                 }\r
821 \r
822                                                 /* Pass the buffer minus 2 bytes, see ksz8851snl.c: RXQ_TWOBYTE_OFFSET. */\r
823                                                 ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer - 2, xReadLength );\r
824                                                 /* Remove CRC and update buffer length. */\r
825                                                 xLength -= 4;\r
826                                                 pxNetworkBuffer->xDataLength = xLength;\r
827                                                 /* Wait for SPI interrupt to set status 'SPI_PDC_RX_COMPLETE'. */\r
828                                         }\r
829                                 }\r
830                                 break;\r
831                         } /* ul_had_intn_interrupt || us_pending_frame */\r
832                         /*\r
833                          * ========================== Handle TX ==========================\r
834                          */\r
835 \r
836                         /* Fetch next packet to be sent. */\r
837                         if( ( xMicrelDevice.tx_busy[ txTail ] != pdFALSE ) &&\r
838                                 ( xMicrelDevice.us_pending_frame == 0 ) &&\r
839                                 ( xMicrelDevice.ul_had_intn_interrupt == 0 ) )\r
840                         {\r
841                         NetworkBufferDescriptor_t *pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ];\r
842                         size_t xLength = pxNetworkBuffer->xDataLength;\r
843                         int iIndex = xLength;\r
844 \r
845                                 xLength = 4 * ( ( xLength + 3 ) / 4 );\r
846                                 while( iIndex < ( int ) xLength )\r
847                                 {\r
848                                         pxNetworkBuffer->pucEthernetBuffer[ iIndex ] = '\0';\r
849                                         iIndex++;\r
850                                 }\r
851                                 pxNetworkBuffer->xDataLength = xLength;\r
852 \r
853                                 /* TX step1: check if TXQ memory size is available for transmit. */\r
854                                 txmir = ksz8851_reg_read( REG_TX_MEM_INFO );\r
855                                 txmir = txmir & TX_MEM_AVAILABLE_MASK;\r
856 \r
857                                 if( txmir < ( xLength + 8 ) )\r
858                                 {\r
859                                         if( wait_tx_space == pdFALSE )\r
860                                         {\r
861                                                 tx_status = ksz8851_reg_read( REG_TX_STATUS );\r
862                                                 fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS );\r
863                                                 wait_tx_space = pdTRUE;\r
864                                         }\r
865                                         //return;\r
866                                         rx_debug = 1;\r
867                                         tx_space = txmir;\r
868                                 }\r
869                                 else\r
870                                 {\r
871                                         tx_space = txmir;\r
872 \r
873                                         /* TX step2: disable all interrupts. */\r
874                                         ksz8851_reg_write( REG_INT_MASK, 0 );\r
875 \r
876                                         xMicrelDevice.tx_space -= xLength;\r
877 \r
878                                         /* TX step3: enable TXQ write access. */\r
879                                         ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_START );\r
880                                         /* TX step4-8: perform FIFO write operation. */\r
881                                         xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_START;\r
882                                         xMicrelDevice.tx_cur_buffer = pxNetworkBuffer;\r
883                                         /* Bring SPI SS low. */\r
884                                         gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );\r
885                                         xMicrelDevice.ul_total_tx++;\r
886 \r
887                                         ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength );\r
888                                 }\r
889                         }\r
890                 }\r
891                 break;  /* SPI_PDC_IDLE */\r
892 \r
893         case SPI_PDC_RX_COMPLETE:\r
894                 {\r
895                 int rxHead = xMicrelDevice.us_rx_head;\r
896                         /* RX step18-19: pad with dummy data to keep dword alignment. */\r
897                         /* Packet lengths will be rounded up to a multiple of "sizeof size_t". */\r
898 //                      xLength = xMicrelDevice.rx_buffers[ rxHead ]->xDataLength & 3;\r
899 //                      if( xLength != 0 )\r
900 //                      {\r
901 //                              ksz8851_fifo_dummy( 4 - xLength );\r
902 //                      }\r
903 \r
904                         /* RX step20: end RX transfer. */\r
905                         gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );\r
906 \r
907                         /* Disable asynchronous transfer mode. */\r
908                         xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;\r
909 \r
910                         /* RX step21: end RXQ read access. */\r
911                         ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START);\r
912 \r
913                         /* RX step22-23: update frame count to be read. */\r
914                         xMicrelDevice.us_pending_frame -= 1;\r
915 \r
916                         /* RX step24: enable INT_RX flag if transfer complete. */\r
917                         if( xMicrelDevice.us_pending_frame == 0 )\r
918                         {\r
919                                 ksz8851_reg_write(REG_INT_MASK, INT_RX);\r
920                         }\r
921 \r
922                         /* Mark descriptor ready to be read. */\r
923                         xMicrelDevice.rx_ready[ rxHead ] = pdTRUE;\r
924                         if( ++rxHead == MICREL_RX_BUFFERS )\r
925                         {\r
926                                 rxHead = 0;\r
927                         }\r
928                         xMicrelDevice.us_rx_head = rxHead;\r
929                         if( rx_debug != 0 )\r
930                         {\r
931                         uint32_t txmir;\r
932                                 rx_debug = 0;\r
933                                 txmir = ksz8851_reg_read( REG_TX_MEM_INFO );\r
934                                 txmir = txmir & TX_MEM_AVAILABLE_MASK;\r
935                         }\r
936                         /* Tell prvEMACHandlerTask that RX packets are available. */\r
937                         ulISREvents |= EMAC_IF_RX_EVENT;\r
938                 }       /* case SPI_PDC_RX_COMPLETE */\r
939                 break;\r
940 \r
941         case SPI_PDC_TX_COMPLETE:\r
942                 {\r
943                 int txTail = xMicrelDevice.us_tx_tail;\r
944                 NetworkBufferDescriptor_t *pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ];\r
945 \r
946                 size_t xLength;\r
947                         /* TX step9-10: pad with dummy data to keep dword alignment. */\r
948                         /* Not necessary: length is already a multiple of 4. */\r
949                         xLength = pxNetworkBuffer->xDataLength & 3;\r
950                         if( xLength != 0 )\r
951                         {\r
952 //                              ksz8851_fifo_dummy( 4 - xLength );\r
953                         }\r
954 \r
955 //                      /* TX step11: end TX transfer. */\r
956                         gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );\r
957 \r
958                         /* Disable asynchronous transfer mode. */\r
959                         xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;\r
960 \r
961                         /* TX step12: disable TXQ write access. */\r
962                         ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );\r
963 \r
964                         xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK;\r
965 \r
966                         /* TX step12.1: enqueue frame in TXQ. */\r
967                         ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE );\r
968 \r
969                         /* RX step13: enable INT_RX flag. */\r
970 //                      ksz8851_reg_write( REG_INT_MASK, INT_RX );\r
971                         /* Buffer sent, free the corresponding buffer and mark descriptor as owned by software. */\r
972                         vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );\r
973 \r
974                         xMicrelDevice.tx_buffers[ txTail ] = NULL;\r
975                         xMicrelDevice.tx_busy[ txTail ] = pdFALSE;\r
976                         if( ++txTail == MICREL_TX_BUFFERS )\r
977                         {\r
978                                 txTail = 0;\r
979                         }\r
980 \r
981                         xMicrelDevice.us_tx_tail = txTail;\r
982                         /* Experiment. */\r
983                         //xMicrelDevice.ul_had_intn_interrupt = 1;\r
984                         if( xTransmitHandle != NULL )\r
985                         {\r
986                                 xTaskNotifyGive( xTransmitHandle );\r
987                         }\r
988 #warning moved downward\r
989                         /* RX step13: enable INT_RX flag. */\r
990                         ksz8851_reg_write( REG_INT_MASK, INT_RX );\r
991                         /* Prevent the EMAC task from sleeping a single time. */\r
992                         ulISREvents |= EMAC_IF_TX_EVENT;\r
993                 }       /* case SPI_PDC_TX_COMPLETE */\r
994                 break;\r
995         }       /* switch( xMicrelDevice.ul_spi_pdc_status ) */\r
996 }\r
997 \r
998 /**\r
999  * \brief Set up the RX descriptor ring buffers.\r
1000  *\r
1001  * This function sets up the descriptor list used for RX packets.\r
1002  *\r
1003  */\r
1004 static void ksz8851snl_rx_init()\r
1005 {\r
1006         uint32_t ul_index = 0;\r
1007 \r
1008         /* Init pointer index. */\r
1009         xMicrelDevice.us_rx_head = 0;\r
1010         xMicrelDevice.us_rx_tail = 0;\r
1011 \r
1012         /* Set up the RX descriptors. */\r
1013         for (ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++) {\r
1014                 xMicrelDevice.rx_buffers[ul_index] = NULL;\r
1015                 xMicrelDevice.rx_ready[ul_index] = pdFALSE;\r
1016         }\r
1017 \r
1018         /* Build RX buffer and descriptors. */\r
1019         ksz8851snl_rx_populate_queue();\r
1020 }\r
1021 \r
1022 /**\r
1023  * \brief Set up the TX descriptor ring buffers.\r
1024  *\r
1025  * This function sets up the descriptor list used for TX packets.\r
1026  *\r
1027  */\r
1028 static void ksz8851snl_tx_init()\r
1029 {\r
1030         uint32_t ul_index = 0;\r
1031 \r
1032         /* Init TX index pointer. */\r
1033         xMicrelDevice.us_tx_head = 0;\r
1034         xMicrelDevice.us_tx_tail = 0;\r
1035 \r
1036         /* Set up the TX descriptors */\r
1037         for( ul_index = 0; ul_index < MICREL_TX_BUFFERS; ul_index++ )\r
1038         {\r
1039                 xMicrelDevice.tx_busy[ul_index] = pdFALSE;\r
1040         }\r
1041         xMicrelDevice.tx_space = 6144;\r
1042 }\r
1043 \r
1044 /**\r
1045  * \brief Initialize ksz8851snl ethernet controller.\r
1046  *\r
1047  * \note Called from ethernetif_init().\r
1048  *\r
1049  * \param netif the lwIP network interface structure for this ethernetif.\r
1050  */\r
1051 static void ksz8851snl_low_level_init( void )\r
1052 {\r
1053         ksz8851snl_rx_init();\r
1054         ksz8851snl_tx_init();\r
1055 \r
1056         /* Enable NVIC interrupts. */\r
1057         NVIC_SetPriority(SPI_IRQn, INT_PRIORITY_SPI);\r
1058         NVIC_EnableIRQ(SPI_IRQn);\r
1059 \r
1060         /* Initialize SPI link. */\r
1061         if( ksz8851snl_init() < 0 )\r
1062         {\r
1063                 FreeRTOS_printf( ( "ksz8851snl_low_level_init: failed to initialize the Micrel driver!\n" ) );\r
1064                 configASSERT(0 == 1);\r
1065         }\r
1066         memset( xMicrelDevice.pusHashTable, 255, sizeof( xMicrelDevice.pusHashTable ) );\r
1067         ksz8851_reg_write( REG_MAC_HASH_0, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 0 ] ) );\r
1068         ksz8851_reg_write( REG_MAC_HASH_2, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 1 ] ) );\r
1069         ksz8851_reg_write( REG_MAC_HASH_4, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 2 ] ) );\r
1070         ksz8851_reg_write( REG_MAC_HASH_6, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 3 ] ) );\r
1071 \r
1072         /* Initialize interrupt line INTN. */\r
1073         configure_intn( INTN_Handler );\r
1074 }\r
1075 \r
1076 /**\r
1077  * \brief Use pre-allocated pbuf as DMA source and return the incoming packet.\r
1078  *\r
1079  * \param netif the lwIP network interface structure for this ethernetif.\r
1080  *\r
1081  * \return a pbuf filled with the received packet (including MAC header).\r
1082  * 0 on memory error.\r
1083  */\r
1084 static NetworkBufferDescriptor_t *ksz8851snl_low_level_input( void )\r
1085 {\r
1086 NetworkBufferDescriptor_t *pxNetworkBuffer = NULL;\r
1087 int rxTail = xMicrelDevice.us_rx_tail;\r
1088 \r
1089         /* Check that descriptor is owned by software (ie packet received). */\r
1090         if( xMicrelDevice.rx_ready[ rxTail ] != pdFALSE )\r
1091         {\r
1092 \r
1093                 /* Fetch pre-allocated buffer */\r
1094                 pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxTail ];\r
1095 \r
1096                 /* Remove this pbuf from its descriptor. */\r
1097                 xMicrelDevice.rx_buffers[ rxTail ] = NULL;\r
1098 \r
1099                 /* Clears rx_ready and sets rx_buffers. */\r
1100                 ksz8851snl_rx_populate_queue();\r
1101 \r
1102                 if( ++rxTail == MICREL_RX_BUFFERS )\r
1103                 {\r
1104                         rxTail = 0;\r
1105                 }\r
1106                 xMicrelDevice.us_rx_tail = rxTail;\r
1107         }\r
1108 \r
1109         return pxNetworkBuffer;\r
1110 }\r
1111 /*-----------------------------------------------------------*/\r
1112 \r
1113 static uint32_t prvEMACRxPoll( void )\r
1114 {\r
1115 NetworkBufferDescriptor_t *pxNetworkBuffer;\r
1116 IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };\r
1117 uint32_t ulReturnValue = 0;\r
1118 \r
1119         for( ;; )\r
1120         {\r
1121         /* Only for logging. */\r
1122         int rxTail = xMicrelDevice.us_rx_tail;\r
1123         EthernetHeader_t *pxEthernetHeader;\r
1124 \r
1125         pxNetworkBuffer = ksz8851snl_low_level_input();\r
1126         \r
1127                 if( pxNetworkBuffer == NULL )\r
1128                 {\r
1129                         break;\r
1130                 }\r
1131                 pxEthernetHeader = ( EthernetHeader_t * ) ( pxNetworkBuffer->pucEthernetBuffer );\r
1132 \r
1133                 if( ( pxEthernetHeader->usFrameType != ipIPv4_FRAME_TYPE ) &&\r
1134                         ( pxEthernetHeader->usFrameType != ipARP_FRAME_TYPE     ) )\r
1135                 {\r
1136                         FreeRTOS_printf( ( "Frame type %02X received\n", pxEthernetHeader->usFrameType ) );\r
1137                 }\r
1138                 ulReturnValue++;\r
1139 \r
1140                 xRxEvent.pvData = ( void * )pxNetworkBuffer;\r
1141                 /* Send the descriptor to the IP task for processing. */\r
1142                 if( xSendEventStructToIPTask( &xRxEvent, 100UL ) != pdTRUE )\r
1143                 {\r
1144                         vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );\r
1145                         iptraceETHERNET_RX_EVENT_LOST();\r
1146                         FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) );\r
1147                 }\r
1148         }\r
1149 \r
1150         return ulReturnValue;\r
1151 }\r
1152 /*-----------------------------------------------------------*/\r
1153 \r
1154 static void prvEMACHandlerTask( void *pvParameters )\r
1155 {\r
1156 TimeOut_t xPhyTime;\r
1157 TickType_t xPhyRemTime;\r
1158 TickType_t xLoggingTime;\r
1159 UBaseType_t uxLastMinBufferCount = 0;\r
1160 UBaseType_t uxCurrentCount;\r
1161 BaseType_t xResult = 0;\r
1162 uint32_t xStatus;\r
1163 const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS );\r
1164 #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )\r
1165         UBaseType_t uxLastMinQueueSpace = 0;\r
1166 #endif\r
1167 \r
1168         /* Remove compiler warnings about unused parameters. */\r
1169         ( void ) pvParameters;\r
1170 \r
1171         configASSERT( xEMACTaskHandle );\r
1172 \r
1173         vTaskSetTimeOutState( &xPhyTime );\r
1174         xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );\r
1175         xLoggingTime = xTaskGetTickCount();\r
1176 \r
1177         for( ;; )\r
1178         {\r
1179                 uxCurrentCount = uxGetMinimumFreeNetworkBuffers();\r
1180                 if( uxLastMinBufferCount != uxCurrentCount )\r
1181                 {\r
1182                         /* The logging produced below may be helpful\r
1183                         while tuning +TCP: see how many buffers are in use. */\r
1184                         uxLastMinBufferCount = uxCurrentCount;\r
1185                         FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",\r
1186                                 uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );\r
1187                 }\r
1188 \r
1189                 #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )\r
1190                 {\r
1191                         uxCurrentCount = uxGetMinimumIPQueueSpace();\r
1192                         if( uxLastMinQueueSpace != uxCurrentCount )\r
1193                         {\r
1194                                 /* The logging produced below may be helpful\r
1195                                 while tuning +TCP: see how many buffers are in use. */\r
1196                                 uxLastMinQueueSpace = uxCurrentCount;\r
1197                                 FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );\r
1198                         }\r
1199                 }\r
1200                 #endif /* ipconfigCHECK_IP_QUEUE_SPACE */\r
1201 \r
1202                 /* Run the state-machine of the ksz8851 driver. */\r
1203                 ksz8851snl_update();\r
1204 \r
1205                 if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 )\r
1206                 {\r
1207                         /* No events to process now, wait for the next. */\r
1208                         ulTaskNotifyTake( pdTRUE, ulMaxBlockTime );\r
1209                 }\r
1210 \r
1211                 if( ( xTaskGetTickCount() - xLoggingTime ) > 10000 )\r
1212                 {\r
1213                         xLoggingTime += 10000;\r
1214                         FreeRTOS_printf( ( "Now Tx/Rx %7d /%7d\n",\r
1215                                 xMicrelDevice.ul_total_tx, xMicrelDevice.ul_total_rx ) );\r
1216                 }\r
1217 \r
1218                 if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )\r
1219                 {\r
1220                         ulISREvents &= ~EMAC_IF_RX_EVENT;\r
1221 \r
1222                         /* Wait for the EMAC interrupt to indicate that another packet has been\r
1223                         received. */\r
1224                         xResult = prvEMACRxPoll();\r
1225                 }\r
1226 \r
1227                 if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )\r
1228                 {\r
1229                         /* Future extension: code to release TX buffers if zero-copy is used. */\r
1230                         ulISREvents &= ~EMAC_IF_TX_EVENT;\r
1231                 }\r
1232 \r
1233                 if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 )\r
1234                 {\r
1235                         /* Future extension: logging about errors that occurred. */\r
1236                         ulISREvents &= ~EMAC_IF_ERR_EVENT;\r
1237                 }\r
1238 \r
1239                 if( xResult > 0 )\r
1240                 {\r
1241                         /* As long as packets are being received, assume that\r
1242                         the Link Status is high. */\r
1243                         ulPHYLinkStatus |= BMSR_LINK_STATUS;\r
1244                         /* A packet was received. No need to check for the PHY status now,\r
1245                         but set a timer to check it later on. */\r
1246                         vTaskSetTimeOutState( &xPhyTime );\r
1247                         xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );\r
1248                         xResult = 0;\r
1249                 }\r
1250                 else if( ( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) &&\r
1251                         ( xMicrelDevice.ul_spi_pdc_status == SPI_PDC_IDLE ) )\r
1252                 {\r
1253                         /* Check the link status again. */\r
1254                         xStatus = ulReadMDIO( PHY_REG_01_BMSR );\r
1255 \r
1256                         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) )\r
1257                         {\r
1258                                 ulPHYLinkStatus = xStatus;\r
1259                                 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) );\r
1260                         }\r
1261 \r
1262                         vTaskSetTimeOutState( &xPhyTime );\r
1263                         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )\r
1264                         {\r
1265                                 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );\r
1266                         }\r
1267                         else\r
1268                         {\r
1269                                 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );\r
1270                         }\r
1271                 }\r
1272         }\r
1273 }\r
1274 /*-----------------------------------------------------------*/\r