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