1273 lines
38 KiB
C
1273 lines
38 KiB
C
|
/*
|
||
|
FreeRTOS+TCP V2.0.11
|
||
|
Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||
|
this software and associated documentation files (the "Software"), to deal in
|
||
|
the Software without restriction, including without limitation the rights to
|
||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||
|
subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in all
|
||
|
copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
|
||
|
http://aws.amazon.com/freertos
|
||
|
http://www.FreeRTOS.org
|
||
|
*/
|
||
|
|
||
|
/* Standard includes. */
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
/* FreeRTOS includes. */
|
||
|
#include "FreeRTOS.h"
|
||
|
#include "task.h"
|
||
|
#include "queue.h"
|
||
|
#include "semphr.h"
|
||
|
|
||
|
/* FreeRTOS+TCP includes. */
|
||
|
#include "FreeRTOS_IP.h"
|
||
|
#include "FreeRTOS_Sockets.h"
|
||
|
#include "FreeRTOS_IP_Private.h"
|
||
|
#include "NetworkBufferManagement.h"
|
||
|
#include "NetworkInterface.h"
|
||
|
|
||
|
#include "sam4e_xplained_pro.h"
|
||
|
#include "hr_gettime.h"
|
||
|
#include "conf_eth.h"
|
||
|
#include "ksz8851snl.h"
|
||
|
#include "ksz8851snl_reg.h"
|
||
|
|
||
|
/* Some files from the Atmel Software Framework */
|
||
|
#include <sysclk.h>
|
||
|
#include <pdc/pdc.h>
|
||
|
#include <spi/spi.h>
|
||
|
|
||
|
/*
|
||
|
Sending a packet:
|
||
|
|
||
|
1) Called by UP-task, add buffer to the TX-list:
|
||
|
xNetworkInterfaceOutput()
|
||
|
tx_buffers[ us_tx_head ] = pxNetworkBuffer;
|
||
|
tx_busy[ us_tx_head ] = pdTRUE;
|
||
|
us_tx_head++;
|
||
|
|
||
|
2) Called by EMAC-Task: start SPI transfer
|
||
|
ksz8851snl_update()
|
||
|
if( ul_spi_pdc_status == SPI_PDC_IDLE )
|
||
|
{
|
||
|
if( ( tx_busy[ us_tx_tail ] != pdFALSE ) &&
|
||
|
( us_pending_frame == 0 ) &&
|
||
|
( ul_had_intn_interrupt == 0 ) )
|
||
|
{
|
||
|
// disable all interrupts.
|
||
|
ksz8851_reg_write( REG_INT_MASK, 0 );
|
||
|
Bring KSZ8851SNL_CSN_GPIO low
|
||
|
ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength );
|
||
|
ul_spi_pdc_status = SPI_PDC_TX_START;
|
||
|
tx_cur_buffer = pxNetworkBuffer;
|
||
|
}
|
||
|
}
|
||
|
3) Wait for SPI RXBUFF interrupt
|
||
|
SPI_Handler()
|
||
|
if( ul_spi_pdc_status == SPI_PDC_TX_START )
|
||
|
{
|
||
|
if( SPI_Status & SPI_SR_RXBUFF )
|
||
|
{
|
||
|
ul_spi_pdc_status = SPI_PDC_TX_COMPLETE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
4) Called by EMAC-Task: finish SPI transfer
|
||
|
ksz8851snl_update()
|
||
|
if( ul_spi_pdc_status == SPI_PDC_TX_COMPLETE )
|
||
|
{
|
||
|
ul_spi_pdc_status = SPI_PDC_IDLE;
|
||
|
Bring KSZ8851SNL_CSN_GPIO high
|
||
|
// TX step12: disable TXQ write access.
|
||
|
ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );
|
||
|
// TX step12.1: enqueue frame in TXQ.
|
||
|
ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE );
|
||
|
|
||
|
// RX step13: enable INT_RX flag.
|
||
|
ksz8851_reg_write( REG_INT_MASK, INT_RX );
|
||
|
|
||
|
// Buffer sent, free the corresponding buffer and mark descriptor as owned by software.
|
||
|
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
|
||
|
|
||
|
tx_buffers[ us_tx_tail ] = NULL;
|
||
|
tx_busy[ us_tx_tail ] = pdFALSE;
|
||
|
us_tx_tail++
|
||
|
}
|
||
|
|
||
|
Receiving a packet:
|
||
|
|
||
|
1) Wait for a INTN interrupt
|
||
|
INTN_Handler()
|
||
|
ul_had_intn_interrupt = 1
|
||
|
vTaskNotifyGiveFromISR(); // Wake up the EMAC task
|
||
|
|
||
|
2) Called by EMAC-Task: check for new fragments and start SPI transfer
|
||
|
ksz8851snl_update()
|
||
|
if( ul_spi_pdc_status == SPI_PDC_IDLE )
|
||
|
{
|
||
|
if( ( ul_had_intn_interrupt != 0 ) || ( us_pending_frame > 0 ) )
|
||
|
{
|
||
|
if( us_pending_frame == 0 )
|
||
|
{
|
||
|
us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8;
|
||
|
if( us_pending_frame == 0 )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// RX step2: disable all interrupts.
|
||
|
ksz8851_reg_write( REG_INT_MASK, 0 );
|
||
|
Check if there is a valid packet: REG_RX_FHR_STATUS
|
||
|
Read the length of the next fragment: REG_RX_FHR_BYTE_CNT
|
||
|
ul_spi_pdc_status = SPI_PDC_RX_START;
|
||
|
gpio_set_pin_low(KSZ8851SNL_CSN_GPIO);
|
||
|
// Start SPI data transfer
|
||
|
ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer, xReadLength );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
3) Wait for SPI RXBUFF interrupt
|
||
|
SPI_Handler()
|
||
|
if( ul_spi_pdc_status == SPI_PDC_RX_START:
|
||
|
{
|
||
|
if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 )
|
||
|
{
|
||
|
// Transfer complete, disable SPI RXBUFF interrupt.
|
||
|
spi_disable_interrupt( KSZ8851SNL_SPI, SPI_IDR_RXBUFF );
|
||
|
|
||
|
ul_spi_pdc_status = SPI_PDC_RX_COMPLETE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
4) Finish SPI transfer
|
||
|
ksz8851snl_update()
|
||
|
if( ul_spi_pdc_status == SPI_PDC_RX_COMPLETE )
|
||
|
{
|
||
|
ul_spi_pdc_status = SPI_PDC_IDLE;
|
||
|
Bring KSZ8851SNL_CSN_GPIO high
|
||
|
// RX step21: end RXQ read access.
|
||
|
ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START);
|
||
|
// RX step22-23: update frame count to be read.
|
||
|
us_pending_frame--
|
||
|
// RX step24: enable INT_RX flag if transfer complete.
|
||
|
if( us_pending_frame == 0 )
|
||
|
{
|
||
|
// Allow more RX interrupts.
|
||
|
ksz8851_reg_write( REG_INT_MASK, INT_RX );
|
||
|
}
|
||
|
|
||
|
// Mark descriptor ready to be read.
|
||
|
rx_ready[ rxHead ] = pdTRUE;
|
||
|
rxHead++
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
#define PHY_REG_00_BMCR 0x00 // Basic mode control register
|
||
|
#define PHY_REG_01_BMSR 0x01 // Basic mode status register
|
||
|
#define PHY_REG_02_PHYSID1 0x02 // PHYS ID 1
|
||
|
#define PHY_REG_03_PHYSID2 0x03 // PHYS ID 2
|
||
|
#define PHY_REG_04_ADVERTISE 0x04 // Advertisement control reg
|
||
|
#define PHY_REG_05_LPA 0x05 // Link partner ability reg
|
||
|
#define PHY_REG_06_ANER 0x06 // 6 RW Auto-Negotiation Expansion Register
|
||
|
#define PHY_REG_07_ANNPTR 0x07 // 7 RW Auto-Negotiation Next Page TX
|
||
|
#define PHY_REG_08_RESERVED0 0x08 // 0x08..0x0Fh 8-15 RW RESERVED
|
||
|
|
||
|
#define BMSR_LINK_STATUS 0x0004 //!< Link status
|
||
|
|
||
|
#ifndef PHY_LS_HIGH_CHECK_TIME_MS
|
||
|
/* Check if the LinkSStatus in the PHY is still high after 15 seconds of not
|
||
|
receiving packets. */
|
||
|
#define PHY_LS_HIGH_CHECK_TIME_MS 15000
|
||
|
#endif
|
||
|
|
||
|
#ifndef PHY_LS_LOW_CHECK_TIME_MS
|
||
|
/* Check if the LinkSStatus in the PHY is still low every second. */
|
||
|
#define PHY_LS_LOW_CHECK_TIME_MS 1000
|
||
|
#endif
|
||
|
|
||
|
/* Interrupt events to process. Currently only the Rx event is processed
|
||
|
although code for other events is included to allow for possible future
|
||
|
expansion. */
|
||
|
#define EMAC_IF_RX_EVENT 1UL
|
||
|
#define EMAC_IF_TX_EVENT 2UL
|
||
|
#define EMAC_IF_ERR_EVENT 4UL
|
||
|
#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )
|
||
|
|
||
|
#define ETHERNET_CONF_PHY_ADDR BOARD_GMAC_PHY_ADDR
|
||
|
|
||
|
#ifdef ipconfigHAS_TX_CRC_OFFLOADING
|
||
|
#undef ipconfigHAS_TX_CRC_OFFLOADING
|
||
|
#endif
|
||
|
/* Override this define because the KSZ8851 is programmed to set all outgoing CRC's */
|
||
|
#define ipconfigHAS_TX_CRC_OFFLOADING 1
|
||
|
|
||
|
#ifndef EMAC_MAX_BLOCK_TIME_MS
|
||
|
#define EMAC_MAX_BLOCK_TIME_MS 100ul
|
||
|
#endif
|
||
|
|
||
|
/* Default the size of the stack used by the EMAC deferred handler task to 4x
|
||
|
the size of the stack used by the idle task - but allow this to be overridden in
|
||
|
FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */
|
||
|
#ifndef configEMAC_TASK_STACK_SIZE
|
||
|
#define configEMAC_TASK_STACK_SIZE ( 6 * configMINIMAL_STACK_SIZE )
|
||
|
#endif
|
||
|
|
||
|
#define SPI_PDC_IDLE 0
|
||
|
#define SPI_PDC_RX_START 1
|
||
|
#define SPI_PDC_TX_ERROR 2
|
||
|
#define SPI_PDC_RX_COMPLETE 3
|
||
|
#define SPI_PDC_TX_START 4
|
||
|
#define SPI_PDC_RX_ERROR 5
|
||
|
#define SPI_PDC_TX_COMPLETE 6
|
||
|
|
||
|
/**
|
||
|
* ksz8851snl driver structure.
|
||
|
*/
|
||
|
typedef struct {
|
||
|
/** Set to 1 when owner is software (ready to read), 0 for Micrel. */
|
||
|
uint32_t rx_ready[MICREL_RX_BUFFERS];
|
||
|
/** Set to 1 when owner is Micrel, 0 for software. */
|
||
|
uint32_t tx_busy[MICREL_TX_BUFFERS];
|
||
|
/** RX NetworkBufferDescriptor_t pointer list */
|
||
|
NetworkBufferDescriptor_t *rx_buffers[MICREL_RX_BUFFERS];
|
||
|
/** TX NetworkBufferDescriptor_t pointer list */
|
||
|
NetworkBufferDescriptor_t *tx_buffers[MICREL_TX_BUFFERS];
|
||
|
NetworkBufferDescriptor_t *tx_cur_buffer;
|
||
|
|
||
|
/** Circular buffer head pointer for packet received. */
|
||
|
uint32_t us_rx_head;
|
||
|
/** Circular buffer tail pointer for packet to be read. */
|
||
|
uint32_t us_rx_tail;
|
||
|
/** Circular buffer head pointer by upper layer (buffer to be sent). */
|
||
|
uint32_t us_tx_head;
|
||
|
/** Circular buffer tail pointer incremented by handlers (buffer sent). */
|
||
|
uint32_t us_tx_tail;
|
||
|
|
||
|
uint32_t ul_total_tx;
|
||
|
uint32_t ul_total_rx;
|
||
|
uint32_t tx_space;
|
||
|
|
||
|
/** Still experimental: hash table to allow certain multicast addresses. */
|
||
|
uint16_t pusHashTable[ 4 ];
|
||
|
|
||
|
/* ul_spi_pdc_status has "SPI_PDC_xxx" values. */
|
||
|
volatile uint32_t ul_spi_pdc_status;
|
||
|
|
||
|
/* ul_had_intn_interrupt becomes true within the INTN interrupt. */
|
||
|
volatile uint32_t ul_had_intn_interrupt;
|
||
|
|
||
|
uint16_t us_pending_frame;
|
||
|
} xKSZ8851_Device_t;
|
||
|
|
||
|
/* SPI PDC register base.
|
||
|
Declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */
|
||
|
extern Pdc *g_p_spi_pdc;
|
||
|
|
||
|
/* Temporary buffer for PDC reception.
|
||
|
declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */
|
||
|
extern uint8_t tmpbuf[1536];
|
||
|
|
||
|
COMPILER_ALIGNED(8)
|
||
|
static xKSZ8851_Device_t xMicrelDevice;
|
||
|
|
||
|
static TaskHandle_t xTransmitHandle;
|
||
|
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
/*
|
||
|
* Wait a fixed time for the link status to indicate the network is up.
|
||
|
*/
|
||
|
static BaseType_t xGMACWaitLS( TickType_t xMaxTime );
|
||
|
|
||
|
/*
|
||
|
* A deferred interrupt handler task that processes GMAC interrupts.
|
||
|
*/
|
||
|
static void prvEMACHandlerTask( void *pvParameters );
|
||
|
|
||
|
/*
|
||
|
* Try to obtain an Rx packet from the hardware.
|
||
|
*/
|
||
|
static uint32_t prvEMACRxPoll( void );
|
||
|
|
||
|
static inline unsigned long ulReadMDIO( unsigned uAddress );
|
||
|
|
||
|
static void ksz8851snl_low_level_init( void );
|
||
|
|
||
|
static NetworkBufferDescriptor_t *ksz8851snl_low_level_input( void );
|
||
|
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
/* Bit map of outstanding ETH interrupt events for processing. Currently only
|
||
|
the Rx interrupt is handled, although code is included for other events to
|
||
|
enable future expansion. */
|
||
|
static volatile uint32_t ulISREvents;
|
||
|
|
||
|
/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
|
||
|
static uint32_t ulPHYLinkStatus = 0;
|
||
|
static volatile BaseType_t xGMACSwitchRequired;
|
||
|
|
||
|
static void ksz8851snl_update( void );
|
||
|
|
||
|
static void ksz8851snl_rx_init( void );
|
||
|
|
||
|
static void ksz8851snl_tx_init( void );
|
||
|
|
||
|
/* Holds the handle of the task used as a deferred interrupt processor. The
|
||
|
handle is used so direct notifications can be sent to the task for all EMAC/DMA
|
||
|
related interrupts. */
|
||
|
TaskHandle_t xEMACTaskHandle = NULL;
|
||
|
|
||
|
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
BaseType_t xNetworkInterfaceInitialise( void )
|
||
|
{
|
||
|
const TickType_t x5_Seconds = 5000UL;
|
||
|
|
||
|
if( xEMACTaskHandle == NULL )
|
||
|
{
|
||
|
ksz8851snl_low_level_init();
|
||
|
|
||
|
/* Wait at most 5 seconds for a Link Status in the PHY. */
|
||
|
xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) );
|
||
|
|
||
|
/* The handler task is created at the highest possible priority to
|
||
|
ensure the interrupt handler can return directly to it. */
|
||
|
xTaskCreate( prvEMACHandlerTask, "KSZ8851", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle );
|
||
|
configASSERT( xEMACTaskHandle );
|
||
|
}
|
||
|
|
||
|
/* When returning non-zero, the stack will become active and
|
||
|
start DHCP (in configured) */
|
||
|
ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR );
|
||
|
|
||
|
return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
BaseType_t xGetPhyLinkStatus( void )
|
||
|
{
|
||
|
BaseType_t xResult;
|
||
|
|
||
|
/* This function returns true if the Link Status in the PHY is high. */
|
||
|
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
|
||
|
{
|
||
|
xResult = pdTRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
xResult = pdFALSE;
|
||
|
}
|
||
|
|
||
|
return xResult;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t bReleaseAfterSend )
|
||
|
{
|
||
|
BaseType_t xResult = pdFALSE;
|
||
|
int txHead = xMicrelDevice.us_tx_head;
|
||
|
|
||
|
/* Make sure the next descriptor is free. */
|
||
|
if( xMicrelDevice.tx_busy[ txHead ] != pdFALSE )
|
||
|
{
|
||
|
/* All TX buffers busy. */
|
||
|
}
|
||
|
else if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 )
|
||
|
{
|
||
|
/* Output: LS low. */
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Pass the packet. */
|
||
|
xMicrelDevice.tx_buffers[ txHead ] = pxNetworkBuffer;
|
||
|
/* The descriptor is now owned by Micrel. */
|
||
|
xMicrelDevice.tx_busy[ txHead ] = pdTRUE;
|
||
|
|
||
|
/* Move the head pointer. */
|
||
|
if( ++txHead == MICREL_TX_BUFFERS )
|
||
|
{
|
||
|
txHead = 0;
|
||
|
}
|
||
|
xMicrelDevice.us_tx_head = txHead;
|
||
|
if( xEMACTaskHandle != NULL )
|
||
|
{
|
||
|
xTaskNotifyGive( xEMACTaskHandle );
|
||
|
}
|
||
|
|
||
|
#if( ipconfigZERO_COPY_TX_DRIVER != 1 )
|
||
|
#warning Please ipconfigZERO_COPY_TX_DRIVER as 1
|
||
|
#endif
|
||
|
configASSERT( bReleaseAfterSend != pdFALSE );
|
||
|
xResult = pdTRUE;
|
||
|
}
|
||
|
if( ( xResult == pdFALSE ) && ( bReleaseAfterSend != pdFALSE ) )
|
||
|
{
|
||
|
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
|
||
|
}
|
||
|
return xResult;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
/* This Micrel has numbered it's PHY registers in a different way.
|
||
|
Translate the register index. */
|
||
|
static int ks8851_phy_reg( int reg )
|
||
|
{
|
||
|
switch (reg) {
|
||
|
case PHY_REG_00_BMCR:
|
||
|
return REG_PHY_CNTL; // P1MBCR;
|
||
|
case PHY_REG_01_BMSR:
|
||
|
return REG_PHY_STATUS;
|
||
|
case PHY_REG_02_PHYSID1:
|
||
|
return REG_PHY_ID_LOW;
|
||
|
case PHY_REG_03_PHYSID2:
|
||
|
return REG_PHY_ID_HIGH;
|
||
|
case PHY_REG_04_ADVERTISE:
|
||
|
return REG_PHY_AUTO_NEGOTIATION;
|
||
|
case PHY_REG_05_LPA:
|
||
|
return REG_PHY_REMOTE_CAPABILITY;
|
||
|
}
|
||
|
|
||
|
return 0x0;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static inline unsigned long ulReadMDIO( unsigned uAddress )
|
||
|
{
|
||
|
uint16_t usPHYStatus;
|
||
|
int ks8851_reg = ks8851_phy_reg( uAddress );
|
||
|
|
||
|
if( ks8851_reg != 0 )
|
||
|
{
|
||
|
usPHYStatus = ksz8851_reg_read( ks8851_reg );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Other addresses not yet implemented. */
|
||
|
usPHYStatus = 0;
|
||
|
}
|
||
|
return usPHYStatus;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static BaseType_t xGMACWaitLS( TickType_t xMaxTime )
|
||
|
{
|
||
|
TickType_t xStartTime = xTaskGetTickCount();
|
||
|
TickType_t xEndTime;
|
||
|
BaseType_t xReturn;
|
||
|
const TickType_t xShortTime = pdMS_TO_TICKS( 100UL );
|
||
|
const uint32_t ulHz_Per_MHz = 1000000UL;
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
xEndTime = xTaskGetTickCount();
|
||
|
|
||
|
if( ( xEndTime - xStartTime ) > xMaxTime )
|
||
|
{
|
||
|
/* Wated more than xMaxTime, return. */
|
||
|
xReturn = pdFALSE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Check the link status again. */
|
||
|
ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR );
|
||
|
|
||
|
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
|
||
|
{
|
||
|
/* Link is up - return. */
|
||
|
xReturn = pdTRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Link is down - wait in the Blocked state for a short while (to allow
|
||
|
other tasks to execute) before checking again. */
|
||
|
vTaskDelay( xShortTime );
|
||
|
}
|
||
|
|
||
|
FreeRTOS_printf( ( "xGMACWaitLS: %ld freq %lu Mz\n",
|
||
|
xReturn,
|
||
|
sysclk_get_cpu_hz() / ulHz_Per_MHz ) );
|
||
|
|
||
|
return xReturn;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void vPioSetPinHigh(uint32_t ul_pin)
|
||
|
{
|
||
|
Pio *p_pio = (Pio *)((uint32_t)PIOA + (PIO_DELTA * (ul_pin >> 5)));
|
||
|
// Value to be driven on the I/O line: 1.
|
||
|
p_pio->PIO_SODR = 1 << (ul_pin & 0x1F);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handler for SPI interrupt.
|
||
|
*/
|
||
|
void SPI_Handler(void)
|
||
|
{
|
||
|
BaseType_t xDoWakeup = pdFALSE;
|
||
|
BaseType_t xKSZTaskWoken = pdFALSE;
|
||
|
uint32_t ulCurrentSPIStatus;
|
||
|
uint32_t ulEnabledSPIStatus;
|
||
|
|
||
|
ulCurrentSPIStatus = spi_read_status( KSZ8851SNL_SPI );
|
||
|
ulEnabledSPIStatus = spi_read_interrupt_mask( KSZ8851SNL_SPI );
|
||
|
ulCurrentSPIStatus &= ulEnabledSPIStatus;
|
||
|
spi_disable_interrupt( KSZ8851SNL_SPI, ulCurrentSPIStatus );
|
||
|
|
||
|
|
||
|
switch( xMicrelDevice.ul_spi_pdc_status )
|
||
|
{
|
||
|
case SPI_PDC_RX_START:
|
||
|
{
|
||
|
if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 )
|
||
|
{
|
||
|
pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_ERROR;
|
||
|
xDoWakeup = pdTRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 )
|
||
|
{
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_COMPLETE;
|
||
|
xDoWakeup = pdTRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPI_PDC_TX_START:
|
||
|
{
|
||
|
/* Middle of TX. */
|
||
|
if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 )
|
||
|
{
|
||
|
pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS);
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_ERROR;
|
||
|
xDoWakeup = pdTRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( ( ulCurrentSPIStatus & SPI_SR_ENDRX ) != 0 )
|
||
|
{
|
||
|
/* Enable RX complete interrupt. */
|
||
|
spi_enable_interrupt( KSZ8851SNL_SPI, SPI_IER_RXBUFF );
|
||
|
}
|
||
|
/* End of TX. */
|
||
|
if( ( ulCurrentSPIStatus & SPI_END_OF_TX ) != 0 )
|
||
|
{
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_COMPLETE;
|
||
|
xDoWakeup = pdTRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
} /* switch( xMicrelDevice.ul_spi_pdc_status ) */
|
||
|
|
||
|
if( xDoWakeup != pdFALSE )
|
||
|
{
|
||
|
if( xEMACTaskHandle != NULL )
|
||
|
{
|
||
|
vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xKSZTaskWoken );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
}
|
||
|
portEND_SWITCHING_ISR( xKSZTaskWoken );
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void INTN_Handler(uint32_t id, uint32_t mask)
|
||
|
{
|
||
|
BaseType_t xKSZTaskWoken = pdFALSE;
|
||
|
|
||
|
if( ( id == INTN_ID ) &&
|
||
|
( mask == INTN_PIN_MSK ) )
|
||
|
{
|
||
|
/* Clear the PIO interrupt flags. */
|
||
|
pio_get_interrupt_status( INTN_PIO );
|
||
|
|
||
|
/* Set the INTN flag. */
|
||
|
xMicrelDevice.ul_had_intn_interrupt++;
|
||
|
if( xEMACTaskHandle != NULL )
|
||
|
{
|
||
|
vTaskNotifyGiveFromISR( xEMACTaskHandle, &( xKSZTaskWoken ) );
|
||
|
}
|
||
|
}
|
||
|
portEND_SWITCHING_ISR( xKSZTaskWoken );
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
/**
|
||
|
* \brief Populate the RX descriptor ring buffers with pbufs.
|
||
|
*
|
||
|
* \param p_ksz8851snl_dev Pointer to driver data structure.
|
||
|
*/
|
||
|
static void ksz8851snl_rx_populate_queue( void )
|
||
|
{
|
||
|
uint32_t ul_index = 0;
|
||
|
NetworkBufferDescriptor_t *pxNetworkBuffer;
|
||
|
|
||
|
/* Set up the RX descriptors */
|
||
|
for (ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++) {
|
||
|
if( xMicrelDevice.rx_buffers[ ul_index ] == NULL )
|
||
|
{
|
||
|
/* Allocate a new NetworkBufferDescriptor_t with the maximum size. */
|
||
|
pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipconfigNETWORK_MTU + 36, 100 );
|
||
|
if( pxNetworkBuffer == NULL )
|
||
|
{
|
||
|
FreeRTOS_printf( ( "ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t allocation failure\n" ) );
|
||
|
configASSERT( 1 == 2 );
|
||
|
}
|
||
|
|
||
|
/* Make sure lwIP is well configured so one NetworkBufferDescriptor_t can contain the maximum packet size. */
|
||
|
//LWIP_ASSERT("ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t size too small!", pbuf_clen(pxNetworkBuffer) <= 1);
|
||
|
|
||
|
/* Save NetworkBufferDescriptor_t pointer to be sent to lwIP upper layer. */
|
||
|
xMicrelDevice.rx_buffers[ ul_index ] = pxNetworkBuffer;
|
||
|
/* Pass it to Micrel for reception. */
|
||
|
xMicrelDevice.rx_ready[ ul_index ] = pdFALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsigned tx_space, wait_tx_space, tx_status, fhr_status;
|
||
|
unsigned rx_debug = 0;
|
||
|
/**
|
||
|
* \brief Update Micrel state machine and perform required actions.
|
||
|
*
|
||
|
* \param netif the lwIP network interface structure for this ethernetif.
|
||
|
*/
|
||
|
static void ksz8851snl_update()
|
||
|
{
|
||
|
uint16_t txmir = 0;
|
||
|
|
||
|
/* Check for free PDC. */
|
||
|
switch( xMicrelDevice.ul_spi_pdc_status )
|
||
|
{
|
||
|
case SPI_PDC_TX_ERROR:
|
||
|
{
|
||
|
uint32_t ulValue;
|
||
|
// /* TX step11: end TX transfer. */
|
||
|
gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
|
||
|
|
||
|
vTaskDelay( 2 ); gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );
|
||
|
vTaskDelay( 1 ); gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
|
||
|
vTaskDelay( 1 );
|
||
|
|
||
|
/* Disable asynchronous transfer mode. */
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;
|
||
|
|
||
|
/* TX step12: disable TXQ write access. */
|
||
|
ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );
|
||
|
|
||
|
ulValue = ksz8851snl_reset_tx();
|
||
|
|
||
|
xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK;
|
||
|
|
||
|
FreeRTOS_printf( ("SPI_PDC_TX_ERROR %02X\n", ulValue ) );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPI_PDC_RX_ERROR:
|
||
|
{
|
||
|
uint32_t ulValue;
|
||
|
/* TX step11: end TX transfer. */
|
||
|
gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
|
||
|
|
||
|
vTaskDelay( 2 ); gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );
|
||
|
vTaskDelay( 1 ); gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
|
||
|
vTaskDelay( 1 );
|
||
|
|
||
|
/* Disable asynchronous transfer mode. */
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;
|
||
|
|
||
|
/* TX step12: disable TXQ write access. */
|
||
|
ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );
|
||
|
|
||
|
//ulValue = ksz8851snl_reset_rx();
|
||
|
ulValue = ksz8851snl_reinit();
|
||
|
|
||
|
xGMACWaitLS( pdMS_TO_TICKS( 5000UL ) );
|
||
|
|
||
|
FreeRTOS_printf( ("SPI_PDC_RX_ERROR %02X\n", ulValue ) );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
switch( xMicrelDevice.ul_spi_pdc_status )
|
||
|
{
|
||
|
case SPI_PDC_IDLE:
|
||
|
{
|
||
|
int txTail = xMicrelDevice.us_tx_tail;
|
||
|
|
||
|
/*
|
||
|
* ========================== Handle RX ==========================
|
||
|
*/
|
||
|
if( ( xMicrelDevice.ul_had_intn_interrupt != 0 ) || ( xMicrelDevice.us_pending_frame > 0 ) )
|
||
|
{
|
||
|
int rxHead = xMicrelDevice.us_rx_head;
|
||
|
NetworkBufferDescriptor_t *pxNetworkBuffer;
|
||
|
#warning try
|
||
|
xMicrelDevice.ul_had_intn_interrupt = 0;
|
||
|
|
||
|
if( xMicrelDevice.us_pending_frame == 0 )
|
||
|
{
|
||
|
uint16_t int_status;
|
||
|
/* RX step1: read interrupt status for INT_RX flag. */
|
||
|
int_status = ksz8851_reg_read( REG_INT_STATUS );
|
||
|
|
||
|
|
||
|
/* RX step2: disable all interrupts. */
|
||
|
ksz8851_reg_write( REG_INT_MASK, 0 );
|
||
|
|
||
|
/* RX step3: clear INT_RX flag. */
|
||
|
ksz8851_reg_setbits( REG_INT_STATUS, INT_RX );
|
||
|
|
||
|
/* RX step4-5: check for received frames. */
|
||
|
xMicrelDevice.us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8;
|
||
|
if( xMicrelDevice.us_pending_frame == 0 )
|
||
|
{
|
||
|
/* RX step24: enable INT_RX flag. */
|
||
|
ksz8851_reg_write(REG_INT_MASK, INT_RX);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
#warning try
|
||
|
xMicrelDevice.ul_had_intn_interrupt = 0;
|
||
|
|
||
|
/* Now xMicrelDevice.us_pending_frame != 0 */
|
||
|
|
||
|
/* Don't break Micrel state machine, wait for a free descriptor first! */
|
||
|
if( xMicrelDevice.rx_ready[ rxHead ] != pdFALSE )
|
||
|
{
|
||
|
FreeRTOS_printf( ( "ksz8851snl_update: out of free descriptor! [tail=%u head=%u]\n",
|
||
|
xMicrelDevice.us_rx_tail, rxHead ) );
|
||
|
return;
|
||
|
}
|
||
|
pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxHead ];
|
||
|
|
||
|
if( pxNetworkBuffer == NULL )
|
||
|
{
|
||
|
ksz8851snl_rx_populate_queue();
|
||
|
FreeRTOS_printf( ( "ksz8851snl_update: no buffer set [head=%u]\n", rxHead ) );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* RX step6: get RX packet status. */
|
||
|
fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS );
|
||
|
if( ( ( fhr_status & RX_VALID ) == 0 ) || ( ( fhr_status & RX_ERRORS ) != 0 ) )
|
||
|
{
|
||
|
ksz8851_reg_setbits(REG_RXQ_CMD, RXQ_CMD_FREE_PACKET);
|
||
|
FreeRTOS_printf( ( "ksz8851snl_update: RX packet error!\n" ) );
|
||
|
|
||
|
/* RX step4-5: check for received frames. */
|
||
|
xMicrelDevice.us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8;
|
||
|
if( xMicrelDevice.us_pending_frame == 0 )
|
||
|
{
|
||
|
/* RX step24: enable INT_RX flag. */
|
||
|
ksz8851_reg_write(REG_INT_MASK, INT_RX);
|
||
|
}
|
||
|
ulISREvents |= EMAC_IF_ERR_EVENT;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
size_t xLength;
|
||
|
/* RX step7: read frame length. */
|
||
|
xLength = ksz8851_reg_read(REG_RX_FHR_BYTE_CNT) & RX_BYTE_CNT_MASK;
|
||
|
|
||
|
/* RX step8: Drop packet if len is invalid or no descriptor available. */
|
||
|
if( xLength == 0 )
|
||
|
{
|
||
|
ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_CMD_FREE_PACKET );
|
||
|
FreeRTOS_printf( ( "ksz8851snl_update: RX bad len!\n" ) );
|
||
|
ulISREvents |= EMAC_IF_ERR_EVENT;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
size_t xReadLength = xLength;
|
||
|
|
||
|
xMicrelDevice.ul_total_rx++;
|
||
|
/* RX step9: reset RX frame pointer. */
|
||
|
ksz8851_reg_clrbits(REG_RX_ADDR_PTR, ADDR_PTR_MASK);
|
||
|
|
||
|
/* RX step10: start RXQ read access. */
|
||
|
ksz8851_reg_setbits(REG_RXQ_CMD, RXQ_START);
|
||
|
/* RX step11-17: start asynchronous FIFO read operation. */
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_START;
|
||
|
gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );
|
||
|
if( ( xReadLength & ( sizeof( size_t ) - 1 ) ) != 0 )
|
||
|
{
|
||
|
xReadLength = ( xReadLength | ( sizeof( size_t ) - 1 ) ) + 1;
|
||
|
}
|
||
|
|
||
|
/* Pass the buffer minus 2 bytes, see ksz8851snl.c: RXQ_TWOBYTE_OFFSET. */
|
||
|
ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer - 2, xReadLength );
|
||
|
/* Remove CRC and update buffer length. */
|
||
|
xLength -= 4;
|
||
|
pxNetworkBuffer->xDataLength = xLength;
|
||
|
/* Wait for SPI interrupt to set status 'SPI_PDC_RX_COMPLETE'. */
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
} /* ul_had_intn_interrupt || us_pending_frame */
|
||
|
/*
|
||
|
* ========================== Handle TX ==========================
|
||
|
*/
|
||
|
|
||
|
/* Fetch next packet to be sent. */
|
||
|
if( ( xMicrelDevice.tx_busy[ txTail ] != pdFALSE ) &&
|
||
|
( xMicrelDevice.us_pending_frame == 0 ) &&
|
||
|
( xMicrelDevice.ul_had_intn_interrupt == 0 ) )
|
||
|
{
|
||
|
NetworkBufferDescriptor_t *pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ];
|
||
|
size_t xLength = pxNetworkBuffer->xDataLength;
|
||
|
int iIndex = xLength;
|
||
|
|
||
|
xLength = 4 * ( ( xLength + 3 ) / 4 );
|
||
|
while( iIndex < ( int ) xLength )
|
||
|
{
|
||
|
pxNetworkBuffer->pucEthernetBuffer[ iIndex ] = '\0';
|
||
|
iIndex++;
|
||
|
}
|
||
|
pxNetworkBuffer->xDataLength = xLength;
|
||
|
|
||
|
/* TX step1: check if TXQ memory size is available for transmit. */
|
||
|
txmir = ksz8851_reg_read( REG_TX_MEM_INFO );
|
||
|
txmir = txmir & TX_MEM_AVAILABLE_MASK;
|
||
|
|
||
|
if( txmir < ( xLength + 8 ) )
|
||
|
{
|
||
|
if( wait_tx_space == pdFALSE )
|
||
|
{
|
||
|
tx_status = ksz8851_reg_read( REG_TX_STATUS );
|
||
|
fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS );
|
||
|
wait_tx_space = pdTRUE;
|
||
|
}
|
||
|
//return;
|
||
|
rx_debug = 1;
|
||
|
tx_space = txmir;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tx_space = txmir;
|
||
|
|
||
|
/* TX step2: disable all interrupts. */
|
||
|
ksz8851_reg_write( REG_INT_MASK, 0 );
|
||
|
|
||
|
xMicrelDevice.tx_space -= xLength;
|
||
|
|
||
|
/* TX step3: enable TXQ write access. */
|
||
|
ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_START );
|
||
|
/* TX step4-8: perform FIFO write operation. */
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_START;
|
||
|
xMicrelDevice.tx_cur_buffer = pxNetworkBuffer;
|
||
|
/* Bring SPI SS low. */
|
||
|
gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );
|
||
|
xMicrelDevice.ul_total_tx++;
|
||
|
|
||
|
ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break; /* SPI_PDC_IDLE */
|
||
|
|
||
|
case SPI_PDC_RX_COMPLETE:
|
||
|
{
|
||
|
int rxHead = xMicrelDevice.us_rx_head;
|
||
|
/* RX step18-19: pad with dummy data to keep dword alignment. */
|
||
|
/* Packet lengths will be rounded up to a multiple of "sizeof size_t". */
|
||
|
// xLength = xMicrelDevice.rx_buffers[ rxHead ]->xDataLength & 3;
|
||
|
// if( xLength != 0 )
|
||
|
// {
|
||
|
// ksz8851_fifo_dummy( 4 - xLength );
|
||
|
// }
|
||
|
|
||
|
/* RX step20: end RX transfer. */
|
||
|
gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
|
||
|
|
||
|
/* Disable asynchronous transfer mode. */
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;
|
||
|
|
||
|
/* RX step21: end RXQ read access. */
|
||
|
ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START);
|
||
|
|
||
|
/* RX step22-23: update frame count to be read. */
|
||
|
xMicrelDevice.us_pending_frame -= 1;
|
||
|
|
||
|
/* RX step24: enable INT_RX flag if transfer complete. */
|
||
|
if( xMicrelDevice.us_pending_frame == 0 )
|
||
|
{
|
||
|
ksz8851_reg_write(REG_INT_MASK, INT_RX);
|
||
|
}
|
||
|
|
||
|
/* Mark descriptor ready to be read. */
|
||
|
xMicrelDevice.rx_ready[ rxHead ] = pdTRUE;
|
||
|
if( ++rxHead == MICREL_RX_BUFFERS )
|
||
|
{
|
||
|
rxHead = 0;
|
||
|
}
|
||
|
xMicrelDevice.us_rx_head = rxHead;
|
||
|
if( rx_debug != 0 )
|
||
|
{
|
||
|
uint32_t txmir;
|
||
|
rx_debug = 0;
|
||
|
txmir = ksz8851_reg_read( REG_TX_MEM_INFO );
|
||
|
txmir = txmir & TX_MEM_AVAILABLE_MASK;
|
||
|
}
|
||
|
/* Tell prvEMACHandlerTask that RX packets are available. */
|
||
|
ulISREvents |= EMAC_IF_RX_EVENT;
|
||
|
} /* case SPI_PDC_RX_COMPLETE */
|
||
|
break;
|
||
|
|
||
|
case SPI_PDC_TX_COMPLETE:
|
||
|
{
|
||
|
int txTail = xMicrelDevice.us_tx_tail;
|
||
|
NetworkBufferDescriptor_t *pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ];
|
||
|
|
||
|
size_t xLength;
|
||
|
/* TX step9-10: pad with dummy data to keep dword alignment. */
|
||
|
/* Not necessary: length is already a multiple of 4. */
|
||
|
xLength = pxNetworkBuffer->xDataLength & 3;
|
||
|
if( xLength != 0 )
|
||
|
{
|
||
|
// ksz8851_fifo_dummy( 4 - xLength );
|
||
|
}
|
||
|
|
||
|
// /* TX step11: end TX transfer. */
|
||
|
gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
|
||
|
|
||
|
/* Disable asynchronous transfer mode. */
|
||
|
xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;
|
||
|
|
||
|
/* TX step12: disable TXQ write access. */
|
||
|
ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );
|
||
|
|
||
|
xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK;
|
||
|
|
||
|
/* TX step12.1: enqueue frame in TXQ. */
|
||
|
ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE );
|
||
|
|
||
|
/* RX step13: enable INT_RX flag. */
|
||
|
// ksz8851_reg_write( REG_INT_MASK, INT_RX );
|
||
|
/* Buffer sent, free the corresponding buffer and mark descriptor as owned by software. */
|
||
|
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
|
||
|
|
||
|
xMicrelDevice.tx_buffers[ txTail ] = NULL;
|
||
|
xMicrelDevice.tx_busy[ txTail ] = pdFALSE;
|
||
|
if( ++txTail == MICREL_TX_BUFFERS )
|
||
|
{
|
||
|
txTail = 0;
|
||
|
}
|
||
|
|
||
|
xMicrelDevice.us_tx_tail = txTail;
|
||
|
/* Experiment. */
|
||
|
//xMicrelDevice.ul_had_intn_interrupt = 1;
|
||
|
if( xTransmitHandle != NULL )
|
||
|
{
|
||
|
xTaskNotifyGive( xTransmitHandle );
|
||
|
}
|
||
|
#warning moved downward
|
||
|
/* RX step13: enable INT_RX flag. */
|
||
|
ksz8851_reg_write( REG_INT_MASK, INT_RX );
|
||
|
/* Prevent the EMAC task from sleeping a single time. */
|
||
|
ulISREvents |= EMAC_IF_TX_EVENT;
|
||
|
} /* case SPI_PDC_TX_COMPLETE */
|
||
|
break;
|
||
|
} /* switch( xMicrelDevice.ul_spi_pdc_status ) */
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Set up the RX descriptor ring buffers.
|
||
|
*
|
||
|
* This function sets up the descriptor list used for RX packets.
|
||
|
*
|
||
|
*/
|
||
|
static void ksz8851snl_rx_init()
|
||
|
{
|
||
|
uint32_t ul_index = 0;
|
||
|
|
||
|
/* Init pointer index. */
|
||
|
xMicrelDevice.us_rx_head = 0;
|
||
|
xMicrelDevice.us_rx_tail = 0;
|
||
|
|
||
|
/* Set up the RX descriptors. */
|
||
|
for (ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++) {
|
||
|
xMicrelDevice.rx_buffers[ul_index] = NULL;
|
||
|
xMicrelDevice.rx_ready[ul_index] = pdFALSE;
|
||
|
}
|
||
|
|
||
|
/* Build RX buffer and descriptors. */
|
||
|
ksz8851snl_rx_populate_queue();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Set up the TX descriptor ring buffers.
|
||
|
*
|
||
|
* This function sets up the descriptor list used for TX packets.
|
||
|
*
|
||
|
*/
|
||
|
static void ksz8851snl_tx_init()
|
||
|
{
|
||
|
uint32_t ul_index = 0;
|
||
|
|
||
|
/* Init TX index pointer. */
|
||
|
xMicrelDevice.us_tx_head = 0;
|
||
|
xMicrelDevice.us_tx_tail = 0;
|
||
|
|
||
|
/* Set up the TX descriptors */
|
||
|
for( ul_index = 0; ul_index < MICREL_TX_BUFFERS; ul_index++ )
|
||
|
{
|
||
|
xMicrelDevice.tx_busy[ul_index] = pdFALSE;
|
||
|
}
|
||
|
xMicrelDevice.tx_space = 6144;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Initialize ksz8851snl ethernet controller.
|
||
|
*
|
||
|
* \note Called from ethernetif_init().
|
||
|
*
|
||
|
* \param netif the lwIP network interface structure for this ethernetif.
|
||
|
*/
|
||
|
static void ksz8851snl_low_level_init( void )
|
||
|
{
|
||
|
ksz8851snl_rx_init();
|
||
|
ksz8851snl_tx_init();
|
||
|
|
||
|
/* Enable NVIC interrupts. */
|
||
|
NVIC_SetPriority(SPI_IRQn, INT_PRIORITY_SPI);
|
||
|
NVIC_EnableIRQ(SPI_IRQn);
|
||
|
|
||
|
/* Initialize SPI link. */
|
||
|
if( ksz8851snl_init() < 0 )
|
||
|
{
|
||
|
FreeRTOS_printf( ( "ksz8851snl_low_level_init: failed to initialize the Micrel driver!\n" ) );
|
||
|
configASSERT(0 == 1);
|
||
|
}
|
||
|
memset( xMicrelDevice.pusHashTable, 255, sizeof( xMicrelDevice.pusHashTable ) );
|
||
|
ksz8851_reg_write( REG_MAC_HASH_0, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 0 ] ) );
|
||
|
ksz8851_reg_write( REG_MAC_HASH_2, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 1 ] ) );
|
||
|
ksz8851_reg_write( REG_MAC_HASH_4, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 2 ] ) );
|
||
|
ksz8851_reg_write( REG_MAC_HASH_6, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 3 ] ) );
|
||
|
|
||
|
/* Initialize interrupt line INTN. */
|
||
|
configure_intn( INTN_Handler );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Use pre-allocated pbuf as DMA source and return the incoming packet.
|
||
|
*
|
||
|
* \param netif the lwIP network interface structure for this ethernetif.
|
||
|
*
|
||
|
* \return a pbuf filled with the received packet (including MAC header).
|
||
|
* 0 on memory error.
|
||
|
*/
|
||
|
static NetworkBufferDescriptor_t *ksz8851snl_low_level_input( void )
|
||
|
{
|
||
|
NetworkBufferDescriptor_t *pxNetworkBuffer = NULL;
|
||
|
int rxTail = xMicrelDevice.us_rx_tail;
|
||
|
|
||
|
/* Check that descriptor is owned by software (ie packet received). */
|
||
|
if( xMicrelDevice.rx_ready[ rxTail ] != pdFALSE )
|
||
|
{
|
||
|
|
||
|
/* Fetch pre-allocated buffer */
|
||
|
pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxTail ];
|
||
|
|
||
|
/* Remove this pbuf from its descriptor. */
|
||
|
xMicrelDevice.rx_buffers[ rxTail ] = NULL;
|
||
|
|
||
|
/* Clears rx_ready and sets rx_buffers. */
|
||
|
ksz8851snl_rx_populate_queue();
|
||
|
|
||
|
if( ++rxTail == MICREL_RX_BUFFERS )
|
||
|
{
|
||
|
rxTail = 0;
|
||
|
}
|
||
|
xMicrelDevice.us_rx_tail = rxTail;
|
||
|
}
|
||
|
|
||
|
return pxNetworkBuffer;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static uint32_t prvEMACRxPoll( void )
|
||
|
{
|
||
|
NetworkBufferDescriptor_t *pxNetworkBuffer;
|
||
|
IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
|
||
|
uint32_t ulReturnValue = 0;
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
/* Only for logging. */
|
||
|
int rxTail = xMicrelDevice.us_rx_tail;
|
||
|
EthernetHeader_t *pxEthernetHeader;
|
||
|
|
||
|
pxNetworkBuffer = ksz8851snl_low_level_input();
|
||
|
|
||
|
if( pxNetworkBuffer == NULL )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
pxEthernetHeader = ( EthernetHeader_t * ) ( pxNetworkBuffer->pucEthernetBuffer );
|
||
|
|
||
|
if( ( pxEthernetHeader->usFrameType != ipIPv4_FRAME_TYPE ) &&
|
||
|
( pxEthernetHeader->usFrameType != ipARP_FRAME_TYPE ) )
|
||
|
{
|
||
|
FreeRTOS_printf( ( "Frame type %02X received\n", pxEthernetHeader->usFrameType ) );
|
||
|
}
|
||
|
ulReturnValue++;
|
||
|
|
||
|
xRxEvent.pvData = ( void * )pxNetworkBuffer;
|
||
|
/* Send the descriptor to the IP task for processing. */
|
||
|
if( xSendEventStructToIPTask( &xRxEvent, 100UL ) != pdTRUE )
|
||
|
{
|
||
|
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
|
||
|
iptraceETHERNET_RX_EVENT_LOST();
|
||
|
FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ulReturnValue;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void prvEMACHandlerTask( void *pvParameters )
|
||
|
{
|
||
|
TimeOut_t xPhyTime;
|
||
|
TickType_t xPhyRemTime;
|
||
|
TickType_t xLoggingTime;
|
||
|
UBaseType_t uxLastMinBufferCount = 0;
|
||
|
UBaseType_t uxCurrentCount;
|
||
|
BaseType_t xResult = 0;
|
||
|
uint32_t xStatus;
|
||
|
const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS );
|
||
|
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
|
||
|
UBaseType_t uxLastMinQueueSpace = 0;
|
||
|
#endif
|
||
|
|
||
|
/* Remove compiler warnings about unused parameters. */
|
||
|
( void ) pvParameters;
|
||
|
|
||
|
configASSERT( xEMACTaskHandle );
|
||
|
|
||
|
vTaskSetTimeOutState( &xPhyTime );
|
||
|
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
|
||
|
xLoggingTime = xTaskGetTickCount();
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
|
||
|
if( uxLastMinBufferCount != uxCurrentCount )
|
||
|
{
|
||
|
/* The logging produced below may be helpful
|
||
|
while tuning +TCP: see how many buffers are in use. */
|
||
|
uxLastMinBufferCount = uxCurrentCount;
|
||
|
FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
|
||
|
uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
|
||
|
}
|
||
|
|
||
|
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
|
||
|
{
|
||
|
uxCurrentCount = uxGetMinimumIPQueueSpace();
|
||
|
if( uxLastMinQueueSpace != uxCurrentCount )
|
||
|
{
|
||
|
/* The logging produced below may be helpful
|
||
|
while tuning +TCP: see how many buffers are in use. */
|
||
|
uxLastMinQueueSpace = uxCurrentCount;
|
||
|
FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
|
||
|
}
|
||
|
}
|
||
|
#endif /* ipconfigCHECK_IP_QUEUE_SPACE */
|
||
|
|
||
|
/* Run the state-machine of the ksz8851 driver. */
|
||
|
ksz8851snl_update();
|
||
|
|
||
|
if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 )
|
||
|
{
|
||
|
/* No events to process now, wait for the next. */
|
||
|
ulTaskNotifyTake( pdTRUE, ulMaxBlockTime );
|
||
|
}
|
||
|
|
||
|
if( ( xTaskGetTickCount() - xLoggingTime ) > 10000 )
|
||
|
{
|
||
|
xLoggingTime += 10000;
|
||
|
FreeRTOS_printf( ( "Now Tx/Rx %7d /%7d\n",
|
||
|
xMicrelDevice.ul_total_tx, xMicrelDevice.ul_total_rx ) );
|
||
|
}
|
||
|
|
||
|
if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
|
||
|
{
|
||
|
ulISREvents &= ~EMAC_IF_RX_EVENT;
|
||
|
|
||
|
/* Wait for the EMAC interrupt to indicate that another packet has been
|
||
|
received. */
|
||
|
xResult = prvEMACRxPoll();
|
||
|
}
|
||
|
|
||
|
if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
|
||
|
{
|
||
|
/* Future extension: code to release TX buffers if zero-copy is used. */
|
||
|
ulISREvents &= ~EMAC_IF_TX_EVENT;
|
||
|
}
|
||
|
|
||
|
if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 )
|
||
|
{
|
||
|
/* Future extension: logging about errors that occurred. */
|
||
|
ulISREvents &= ~EMAC_IF_ERR_EVENT;
|
||
|
}
|
||
|
|
||
|
if( xResult > 0 )
|
||
|
{
|
||
|
/* As long as packets are being received, assume that
|
||
|
the Link Status is high. */
|
||
|
ulPHYLinkStatus |= BMSR_LINK_STATUS;
|
||
|
/* A packet was received. No need to check for the PHY status now,
|
||
|
but set a timer to check it later on. */
|
||
|
vTaskSetTimeOutState( &xPhyTime );
|
||
|
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
|
||
|
xResult = 0;
|
||
|
}
|
||
|
else if( ( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) &&
|
||
|
( xMicrelDevice.ul_spi_pdc_status == SPI_PDC_IDLE ) )
|
||
|
{
|
||
|
/* Check the link status again. */
|
||
|
xStatus = ulReadMDIO( PHY_REG_01_BMSR );
|
||
|
|
||
|
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) )
|
||
|
{
|
||
|
ulPHYLinkStatus = xStatus;
|
||
|
FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) );
|
||
|
}
|
||
|
|
||
|
vTaskSetTimeOutState( &xPhyTime );
|
||
|
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
|
||
|
{
|
||
|
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|