rt-thread-official/bsp/wch/risc-v/Libraries/ch32_drivers/drv_eth.c

771 lines
25 KiB
C

/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-09-09 WCH the first version
*/
#include "board.h"
#include <rtdevice.h>
#ifdef BSP_USING_ETH
#include <netif/ethernetif.h>
#include "lwipopts.h"
#include <drv_eth.h>
#define LOG_TAG "drv.eth"
#include <drv_log.h>
#define MAX_ADDR_LEN 6
#define ETH_DMARxDesc_FrameLengthShift 16
#define define_O(a,b) \
GPIO_InitStructure.GPIO_Pin = b;\
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;\
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;\
GPIO_Init(a, &GPIO_InitStructure)
#define define_I(a,b) \
GPIO_InitStructure.GPIO_Pin = b;\
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;\
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;\
GPIO_Init(a, &GPIO_InitStructure)
/* globe variable */
extern ETH_DMADESCTypeDef *DMATxDescToSet; //set tx Desc then send it
extern ETH_DMADESCTypeDef *DMARxDescToGet; //current rx Desc
/* rt-thread eth */
struct rt_ch32_eth
{
/* inherit from ethernet device */
struct eth_device parent;
#ifndef PHY_USING_INTERRUPT_MODE
rt_timer_t poll_link_timer;
#endif
/* interface address info, hw address */
rt_uint8_t dev_addr[MAX_ADDR_LEN];
/* ETH_Speed */
uint32_t ETH_Speed;
/* ETH_Duplex_Mode */
uint32_t ETH_Mode;
};
static ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
static rt_uint8_t Rx_Buff[ETH_RXBUFNB][ETH_MAX_PACKET_SIZE],Tx_Buff[ETH_TXBUFNB][ETH_MAX_PACKET_SIZE];
struct rt_ch32_eth ch32v30x_eth_device;
//#define ETH_RX_DUMP
#if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP)
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen)
{
unsigned char *buf = (unsigned char *)ptr;
int i, j;
for (i = 0; i < buflen; i += 16)
{
rt_kprintf("%08X: ", i);
for (j = 0; j < 16; j++)
if (i + j < buflen)
rt_kprintf("%02X ", buf[i + j]);
else
rt_kprintf(" ");
rt_kprintf(" ");
for (j = 0; j < 16; j++)
if (i + j < buflen)
rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
rt_kprintf("\r\n");
}
}
#endif
/* received data */
FrameTypeDef ETH_RxPkt_ChainMode(void)
{
u32 framelength = 0;
FrameTypeDef frame = {0,0,NULL};
/* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
if((DMARxDescToGet->Status & ETH_DMARxDesc_OWN) != (u32)RESET)
{
frame.length = ETH_ERROR;
if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)
{
/* Clear RBUS ETHERNET DMA flag */
ETH->DMASR = ETH_DMASR_RBUS;
/* Resume DMA reception */
ETH->DMARPDR = 0;
}
LOG_E("Error:ETH_DMARxDesc_OWN.\r\n");
/* Return error: OWN bit set */
return frame;
}
if(
((DMARxDescToGet->Status & ETH_DMARxDesc_ES) == (u32)RESET) &&
((DMARxDescToGet->Status & ETH_DMARxDesc_LS) != (u32)RESET) &&
((DMARxDescToGet->Status & ETH_DMARxDesc_FS) != (u32)RESET))
{
/* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
framelength = ((DMARxDescToGet->Status & ETH_DMARxDesc_FL) >> ETH_DMARxDesc_FrameLengthShift) - 4;
/* Get the addrees of the actual buffer */
frame.buffer = DMARxDescToGet->Buffer1Addr;
}
else
{
/* Return ERROR */
framelength = ETH_ERROR;
rt_kprintf("Error:recv error frame,status:0x%08x.\r\n",DMARxDescToGet->Status);
}
DMARxDescToGet->Status|=ETH_DMARxDesc_OWN;
frame.length = framelength;
frame.descriptor = DMARxDescToGet;
/* Update the ETHERNET DMA global Rx descriptor with next Rx decriptor */
/* Chained Mode */
/* Selects the next DMA Rx descriptor list for next buffer to read */
DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr);
/* Return Frame */
return (frame);
}
/*******************************************************************************
* Function Name : GETH_pin_init
* Description : PHY RGMII interface GPIO initialization.
* Input : None.
* Return : None.
*******************************************************************************/
void GETH_pin_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* PB12/13 set AF_PP_OUT */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE);
GPIOB->CFGHR&=~(0xff<<16);
GPIOB->CFGHR|= (0xbb<<16);
GPIOB->CFGLR&=~(0xff<<4);
define_O(GPIOA,GPIO_Pin_2);
define_O(GPIOA,GPIO_Pin_3);
define_O(GPIOA,GPIO_Pin_7);
define_O(GPIOC,GPIO_Pin_4);
define_O(GPIOC,GPIO_Pin_5);
define_O(GPIOB,GPIO_Pin_0);
define_I(GPIOC,GPIO_Pin_0);
define_I(GPIOC,GPIO_Pin_1);
define_I(GPIOC,GPIO_Pin_2);
define_I(GPIOC,GPIO_Pin_3);
define_I(GPIOA,GPIO_Pin_0);
define_I(GPIOA,GPIO_Pin_1);
define_I(GPIOB,GPIO_Pin_1);/* 125m in */
}
/*******************************************************************************
* Function Name : GETH_pin_init
* Description : PHY MII/RMII interface GPIO initialization.
* Input : None.
* Return : None.
*******************************************************************************/
void FETH_pin_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
#ifdef USE_RMII
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO, ENABLE);
GPIO_ETH_MediaInterfaceConfig(GPIO_ETH_MediaInterface_RMII);
define_O(GPIOA,GPIO_Pin_2);/* MDC */
define_O(GPIOC,GPIO_Pin_1);/* MDIO */
define_O(GPIOB,GPIO_Pin_11);//txen
define_O(GPIOB,GPIO_Pin_12);//txd0
define_O(GPIOB,GPIO_Pin_13);//txd1
define_I(GPIOA,GPIO_Pin_1);/* PA1 REFCLK */
define_I(GPIOA,GPIO_Pin_7);/* PA7 CRSDV */
define_I(GPIOC,GPIO_Pin_4);/* RXD0 */
define_I(GPIOC,GPIO_Pin_5);/* RXD1 */
#else
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE);
/* tx */
define_O(GPIOA,GPIO_Pin_2);/* MDC */
define_O(GPIOC,GPIO_Pin_1);/* MDIO */
define_I(GPIOC,GPIO_Pin_3);//txclk
define_O(GPIOB,GPIO_Pin_11);//txen
define_O(GPIOB,GPIO_Pin_12);//txd0
define_O(GPIOB,GPIO_Pin_13);//txd1
define_O(GPIOC,GPIO_Pin_2); //txd2
define_O(GPIOB,GPIO_Pin_8);//txd3
/* tx */
define_I(GPIOA,GPIO_Pin_1);/* PA1 RXC */
define_I(GPIOA,GPIO_Pin_7);/* PA7 RXDV */
define_I(GPIOC,GPIO_Pin_4);/* RXD0 */
define_I(GPIOC,GPIO_Pin_5);/* RXD1 */
define_I(GPIOB,GPIO_Pin_0);/* RXD2 */
define_I(GPIOB,GPIO_Pin_1);/* RXD3 */
define_I(GPIOB,GPIO_Pin_10);/* RXER */
define_O(GPIOA,GPIO_Pin_0);/* PA0 */
define_O(GPIOA,GPIO_Pin_3);/* PA3 */
#endif
}
/*******************************************************************************
* Function Name : ETH_Init
* Description : ETH initialization.
* Input : ETH_InitStruct:initialization struct.
* PHYAddress:PHY address.
* Return : Initialization status.
*******************************************************************************/
uint32_t ETH_Init(ETH_InitTypeDef* ETH_InitStruct, uint16_t PHYAddress)
{
uint32_t tmpreg = 0;
uint16_t RegValue = 0;
uint32_t tickstart=0;
/* config phy */
tmpreg = ETH->MACMIIAR;
tmpreg &= MACMIIAR_CR_MASK;
/* set SMI clock */
tmpreg |= (uint32_t)ETH_MACMIIAR_CR_Div42;
ETH->MACMIIAR = (uint32_t)tmpreg;
/* reset phy */
ETH_WritePHYRegister(PHYAddress, PHY_BCR, PHY_Reset);
tickstart= rt_tick_get_millisecond();
do{
asm("nop"); /* waiting for finish */
}while((rt_tick_get_millisecond()-tickstart)<1000);
tickstart= rt_tick_get_millisecond();
RegValue = 0;
while( (RegValue&(PHY_Reset)) )
{
RegValue = ETH_ReadPHYRegister(PHYAddress, PHY_BCR);
if((rt_tick_get_millisecond()-tickstart)>10000)
{
LOG_E("Error:Wait phy reset timeout!\r\n");
while(1);
}
}
/* waiting for link up */
tickstart= rt_tick_get_millisecond();
RegValue = 0;
while((RegValue&(PHY_Linked_Status)) == 0)
{
RegValue = ETH_ReadPHYRegister(PHYAddress, PHY_BSR);
if((rt_tick_get_millisecond()-tickstart)>10000)
{
LOG_E("Error:Wait phy linking timeout!\r\n");
while(1);
}
}
/* waiting for auto-negotiation */
tickstart= rt_tick_get_millisecond();
RegValue = 0;
while((RegValue&PHY_AutoNego_Complete) == 0)
{
RegValue = ETH_ReadPHYRegister(PHYAddress, PHY_BSR);
if((rt_tick_get_millisecond()-tickstart)>10000)
{
LOG_E("Error:Wait phy auto-negotiation complete timeout!\r\n");
while(1);
}
}
#ifdef USE10BASE_T
RegValue = ETH_ReadPHYRegister(PHYAddress, PHY_BMCR);
LOG_D("PHY_BMCR:%d,value:%04x.\n",PHY_BMCR,RegValue);
if((RegValue & (1<<8)) != (uint32_t)RESET)
{
ETH_InitStruct->ETH_Mode = ETH_Mode_FullDuplex;
ch32v30x_eth_device.ETH_Mode=ETH_Mode_FullDuplex;
LOG_D("Full-Duplex.\n");
}
else
{
ETH_InitStruct->ETH_Mode = ETH_Mode_HalfDuplex;
ch32v30x_eth_device.ETH_Mode=ETH_Mode_HalfDuplex;
LOG_D("Half-Duplex.\n");
}
if(RegValue & (1<<13))
{
ETH_InitStruct->ETH_Speed = ETH_Speed_100M;
ch32v30x_eth_device.ETH_Speed=ETH_Speed_100M;
/* send link up. */
eth_device_linkchange(&ch32v30x_eth_device.parent, RT_TRUE);
LOG_D("Link speed:100M.\n");
}
else
{
ETH_InitStruct->ETH_Speed = ETH_Speed_10M;
ch32v30x_eth_device.ETH_Speed=ETH_Speed_10M;
/* send link up. */
eth_device_linkchange(&ch32v30x_eth_device.parent, RT_TRUE);
LOG_D("Link speed:10M.\n");
}
#endif
#ifdef USE_FAST_MAC
/* 100M MAC,RMII */
RegValue = ETH_ReadPHYRegister(PHYAddress, PHY_BMCR);
LOG_D("PHY_BMCR:%d,value:%04x.\n",PHY_BMCR,RegValue);
if((RegValue & (1<<8)) != (uint32_t)RESET)
{
ETH_InitStruct->ETH_Mode = ETH_Mode_FullDuplex;
ch32v30x_eth_device.ETH_Mode = ETH_Mode_FullDuplex;
LOG_D("Full-Duplex.\n");
}
else
{
ETH_InitStruct->ETH_Mode = ETH_Mode_HalfDuplex;
ch32v30x_eth_device.ETH_Mode = ETH_Mode_HalfDuplex;
LOG_D("Half-Duplex.\n");
}
if(RegValue & (1<<13))
{
ETH_InitStruct->ETH_Speed = ETH_Speed_100M;
ch32v30x_eth_device.ETH_Speed=ETH_Speed_100M;
/* send link up. */
eth_device_linkchange(&ch32v30x_eth_device.parent, RT_TRUE);
LOG_D("Link speed:100M.\n");
}
else
{
ETH_InitStruct->ETH_Speed = ETH_Speed_10M;
ch32v30x_eth_device.ETH_Speed=ETH_Speed_10M;
/* send link up. */
eth_device_linkchange(&ch32v30x_eth_device.parent, RT_TRUE);
LOG_D("Link speed:10M.\n");
}
#endif
#ifdef USE_GIGA_MAC
/* GMAC,RGMI */
ETH_WritePHYRegister(PHYAddress, 31,0x0a43 );
RegValue = ETH_ReadPHYRegister(PHYAddress, 26);
if( RegValue & 0x0008 )
{
ETH_InitStruct->ETH_Mode = ETH_Mode_FullDuplex;
ch32v30x_eth_device.ETH_Mode = ETH_Mode_FullDuplex;
LOG_D("full duplex.\n");
}
else
{
ETH_InitStruct->ETH_Mode = ETH_Mode_HalfDuplex;
ch32v30x_eth_device.ETH_Mode = ETH_Mode_HalfDuplex;
LOG_D("half duplex!\n");
}
if(( RegValue & 0x0030 ) == 0x0000)
{
ETH_InitStruct->ETH_Speed = ETH_Speed_10M;
ch32v30x_eth_device.ETH_Speed= ETH_Speed_10M;
eth_device_linkchange(&ch32v30x_eth_device.parent, RT_TRUE);
LOG_D("Link speed:10Mbps.\n");
}
else if(( RegValue & 0x0030 ) == 0x0010)
{
ETH_InitStruct->ETH_Speed = ETH_Speed_100M;
ch32v30x_eth_device.ETH_Speed= ETH_Speed_100M;
eth_device_linkchange(&ch32v30x_eth_device.parent, RT_TRUE);
LOG_D("Link speed:100Mbps.\n");
}
else if(( RegValue & 0x0030 ) == 0x0020)
{
ETH_InitStruct->ETH_Speed = ETH_Speed_1000M;
ch32v30x_eth_device.ETH_Speed= ETH_Speed_1000M;
eth_device_linkchange(&ch32v30x_eth_device.parent, RT_TRUE);
LOG_D("Link speed:1000Mbps.\n");
}
#endif
tickstart= rt_tick_get_millisecond();
do{
asm("nop");
}while((rt_tick_get_millisecond()-tickstart)<1000);
/* MAC config */
/* if use RGMII,should config RGMII clock delay register */
tmpreg = ETH->MACCR;
tmpreg &= MACCR_CLEAR_MASK;
tmpreg |= (uint32_t)(ETH_InitStruct->ETH_Watchdog |
ETH_InitStruct->ETH_Jabber |
ETH_InitStruct->ETH_InterFrameGap |
ETH_InitStruct->ETH_CarrierSense |
ETH_InitStruct->ETH_Speed |
ETH_InitStruct->ETH_ReceiveOwn |
ETH_InitStruct->ETH_LoopbackMode |
ETH_InitStruct->ETH_Mode |
ETH_InitStruct->ETH_ChecksumOffload |
ETH_InitStruct->ETH_RetryTransmission |
ETH_InitStruct->ETH_AutomaticPadCRCStrip |
ETH_InitStruct->ETH_BackOffLimit |
ETH_InitStruct->ETH_DeferralCheck);
ETH->MACCR = (uint32_t)tmpreg;
#ifdef USE10BASE_T
/* enable internal pull up resistance,50Ω */
ETH->MACCR|=ETH_Internal_Pull_Up_Res_Enable;
#endif
ETH->MACFFR = (uint32_t)(ETH_InitStruct->ETH_ReceiveAll |
ETH_InitStruct->ETH_SourceAddrFilter |
ETH_InitStruct->ETH_PassControlFrames |
ETH_InitStruct->ETH_BroadcastFramesReception |
ETH_InitStruct->ETH_DestinationAddrFilter |
ETH_InitStruct->ETH_PromiscuousMode |
ETH_InitStruct->ETH_MulticastFramesFilter |
ETH_InitStruct->ETH_UnicastFramesFilter);
/* Write to ETHERNET MACHTHR */
ETH->MACHTHR = (uint32_t)ETH_InitStruct->ETH_HashTableHigh;
/* Write to ETHERNET MACHTLR */
ETH->MACHTLR = (uint32_t)ETH_InitStruct->ETH_HashTableLow;
/* Get the ETHERNET MACFCR value */
tmpreg = ETH->MACFCR;
/* Clear xx bits */
tmpreg &= MACFCR_CLEAR_MASK;
tmpreg |= (uint32_t)((ETH_InitStruct->ETH_PauseTime << 16) |
ETH_InitStruct->ETH_ZeroQuantaPause |
ETH_InitStruct->ETH_PauseLowThreshold |
ETH_InitStruct->ETH_UnicastPauseFrameDetect |
ETH_InitStruct->ETH_ReceiveFlowControl |
ETH_InitStruct->ETH_TransmitFlowControl);
ETH->MACFCR = (uint32_t)tmpreg;
ETH->MACVLANTR = (uint32_t)(ETH_InitStruct->ETH_VLANTagComparison |
ETH_InitStruct->ETH_VLANTagIdentifier);
tmpreg = ETH->DMAOMR;
/* Clear xx bits */
tmpreg &= DMAOMR_CLEAR_MASK;
tmpreg |= (uint32_t)(ETH_InitStruct->ETH_DropTCPIPChecksumErrorFrame |
ETH_InitStruct->ETH_ReceiveStoreForward |
ETH_InitStruct->ETH_FlushReceivedFrame |
ETH_InitStruct->ETH_TransmitStoreForward |
ETH_InitStruct->ETH_TransmitThresholdControl |
ETH_InitStruct->ETH_ForwardErrorFrames |
ETH_InitStruct->ETH_ForwardUndersizedGoodFrames |
ETH_InitStruct->ETH_ReceiveThresholdControl |
ETH_InitStruct->ETH_SecondFrameOperate);
ETH->DMAOMR = (uint32_t)tmpreg;
ETH->DMABMR = (uint32_t)(ETH_InitStruct->ETH_AddressAlignedBeats |
ETH_InitStruct->ETH_FixedBurst |
ETH_InitStruct->ETH_RxDMABurstLength | /* !! if 4xPBL is selected for Tx or Rx it is applied for the other */
ETH_InitStruct->ETH_TxDMABurstLength |
(ETH_InitStruct->ETH_DescriptorSkipLength << 2) |
ETH_InitStruct->ETH_DMAArbitration |
ETH_DMABMR_USP);
return ETH_SUCCESS;
}
/* eth initialization function */
static rt_err_t rt_ch32_eth_init(rt_device_t dev)
{
ETH_InitTypeDef ETH_InitStructure={0};
RCC_PLL3Cmd(DISABLE);
RCC_PREDIV2Config(RCC_PREDIV2_Div2);
RCC_PLL3Config(RCC_PLL3Mul_15);
RCC_PLL3Cmd(ENABLE);
while(RESET == RCC_GetFlagStatus(RCC_FLAG_PLL3RDY));
LOG_D("PLL3 Init Finish\r\n");
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC|RCC_AHBPeriph_ETH_MAC_Tx|RCC_AHBPeriph_ETH_MAC_Rx,ENABLE);
#ifdef USE10BASE_T
/* Enable internal 10BASE-T PHY*/
EXTEN->EXTEN_CTR |=EXTEN_ETH_10M_EN;
#endif
#ifdef USE_GIGA_MAC
/* Enable 1G MAC*/
EXTEN->EXTEN_CTR |= EXTEN_ETH_RGMII_SEL;
/* mac clock use external 125MHz,input from PB1 */
RCC_ETH1GCLKConfig(RCC_ETH1GCLKSource_PB1_IN);
RCC_ETH1G_125Mcmd(ENABLE);
/* Enable RGMII GPIO */
GETH_pin_init();
#endif
#ifdef USE_FAST_MAC
/* Enable MII or RMII GPIO */
FETH_pin_init();
#endif
/* Reset ETHERNET on AHB Bus */
ETH_DeInit();
/* Software reset */
ETH_SoftwareReset();
/* Wait for software reset */
while(ETH->DMABMR & ETH_DMABMR_SR);
LOG_D("ETH RST Finish\r\n");
ETH_StructInit(&ETH_InitStructure);
ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;
ETH_InitStructure.ETH_Speed = ETH_Speed_1000M;
ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable ;
ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;
ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;
ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;
ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Enable;
ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;
ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Enable;
ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;
ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;
#ifdef CHECKSUM_BY_HARDWARE
ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable;
#endif
/*------------------------ DMA -----------------------------------*/
/* When we use the Checksum offload feature, we need to enable the Store and Forward mode:
the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum,
if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */
ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;
ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;
ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;
ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Enable;
ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Enable;
ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Disable;
ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable;
ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable;
ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;
ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;
ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;
/* Configure Ethernet */
ETH_Init(&ETH_InitStructure, PHY_ADDRESS);
/* Enable the Ethernet Rx Interrupt */
ETH_DMAITConfig(ETH_DMA_IT_NIS
| ETH_DMA_IT_R
| ETH_DMA_IT_T
,ENABLE);
NVIC_SetPriority(ETH_IRQn,1<<4);
NVIC_EnableIRQ(ETH_IRQn);
ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
#ifdef USE_GIGA_MAC
RGMII_TXC_Delay(0,2);
#endif
ETH_Start();
return RT_EOK;
}
static rt_err_t rt_ch32_eth_open(rt_device_t dev, rt_uint16_t oflag)
{
LOG_D("eth open\r\n");
return RT_EOK;
}
static rt_err_t rt_ch32_eth_close(rt_device_t dev)
{
LOG_D("eth close\r\n");
return RT_EOK;
}
static rt_ssize_t rt_ch32_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
LOG_D("eth read\r\n");
rt_set_errno(-RT_ENOSYS);
return 0;
}
static rt_ssize_t rt_ch32_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
LOG_D("eth write\r\n");
rt_set_errno(-RT_ENOSYS);
return 0;
}
static rt_err_t rt_ch32_eth_control(rt_device_t dev, int cmd, void *args)
{
switch (cmd)
{
case NIOCTL_GADDR:
/* get mac address */
if (args) rt_memcpy(args, ch32v30x_eth_device.dev_addr, 6);
else return -RT_ERROR;
break;
default :
break;
}
return RT_EOK;
}
rt_err_t rt_ch32_eth_tx(rt_device_t dev, struct pbuf *p)
{
rt_err_t ret = RT_ERROR;
struct pbuf *q;
uint8_t *buffer = (uint8_t *)(DMATxDescToSet->Buffer1Addr);
uint8_t *pdata;
uint32_t framelength = 0,len=0;
/* copy frame from pbufs to driver buffers */
for (q = p; q != NULL; q = q->next)
{
/* Is this buffer available? If not, goto error */
if ((DMATxDescToSet->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET)
{
LOG_E("buffer not valid\r\n");
ret = ERR_USE;
goto Tx_error;
}
pdata = q->payload;
len = q->len;
framelength += len;
rt_memcpy(buffer,pdata,len);
buffer += len;
}
#ifdef ETH_TX_DUMP
dump_hex(buffer, p->tot_len);
#endif
DMATxDescToSet->ControlBufferSize = (framelength & ETH_DMATxDesc_TBS1);
DMATxDescToSet->Status |= ETH_DMATxDesc_LS | ETH_DMATxDesc_FS;
DMATxDescToSet->Status |= ETH_DMATxDesc_OWN;
ret = ERR_OK;
Tx_error:
/* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
if ((ETH->DMASR & ETH_DMASR_TBUS) != (uint32_t)RESET)
{
/* Clear TUS ETHERNET DMA flag */
ETH->DMASR = ETH_DMASR_TUS;
/* Resume DMA transmission*/
ETH->DMATPDR = 0;
}
DMATxDescToSet = (ETH_DMADESCTypeDef*) (DMATxDescToSet->Buffer2NextDescAddr);
return ret;
}
volatile FrameTypeDef frame;
struct pbuf *rt_ch32_eth_rx(rt_device_t dev)
{
struct pbuf *p = NULL;
struct pbuf *q = NULL;
uint16_t len = 0;
uint8_t *buffer;
uint8_t *pdata;
if(!frame.length)
{
return NULL;
}
len=frame.length;
frame.length=0;
buffer=(uint8_t *)frame.buffer;
if (len > 0)
{
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
}
#ifdef ETH_RX_DUMP
dump_hex(buffer, p->tot_len);
#endif
if (p != NULL)
{
for (q = p; q != NULL; q = q->next)
{
pdata = q->payload;
rt_memcpy(pdata,buffer,q->len);
pdata += (q->len);
buffer += (q->len);
}
}
else
{
LOG_E("p=null\r\n");
}
return p;
}
/* interrupt service routine */
void ETH_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void ETH_IRQHandler(void)
{
GET_INT_SP();
/* enter interrupt */
rt_err_t result;
rt_interrupt_enter();
if(ETH->DMASR&ETH_DMA_IT_R)
{
ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
frame = ETH_RxPkt_ChainMode();
result = eth_device_ready(&(ch32v30x_eth_device.parent));
if (result != RT_EOK)
rt_kprintf("RxCpltCallback err = %d\r\n", result);
}
if(ETH->DMASR&ETH_DMA_IT_T)
{
ETH_DMAClearITPendingBit(ETH_DMA_IT_T);
}
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
/* leave interrupt */
rt_interrupt_leave();
FREE_INT_SP();
}
/* Register the EMAC device */
static int rt_hw_ch32_eth_init(void)
{
rt_err_t state = RT_EOK;
/* 84-c2-e4 WCH. */
ch32v30x_eth_device.dev_addr[0] = 0x84;
ch32v30x_eth_device.dev_addr[1] = 0xc2;
ch32v30x_eth_device.dev_addr[2] = 0xe4;
/* generate MAC (only for test). */
ch32v30x_eth_device.dev_addr[3] = 0x1;
ch32v30x_eth_device.dev_addr[4] = 0x2;
ch32v30x_eth_device.dev_addr[5] = 0x3;
ch32v30x_eth_device.parent.parent.init = rt_ch32_eth_init;
ch32v30x_eth_device.parent.parent.open = rt_ch32_eth_open;
ch32v30x_eth_device.parent.parent.close = rt_ch32_eth_close;
ch32v30x_eth_device.parent.parent.read = rt_ch32_eth_read;
ch32v30x_eth_device.parent.parent.write = rt_ch32_eth_write;
ch32v30x_eth_device.parent.parent.control = rt_ch32_eth_control;
ch32v30x_eth_device.parent.parent.user_data = RT_NULL;
ch32v30x_eth_device.parent.eth_rx = rt_ch32_eth_rx;
ch32v30x_eth_device.parent.eth_tx = rt_ch32_eth_tx;
/* register eth device */
state = eth_device_init(&(ch32v30x_eth_device.parent), "e0");
if (RT_EOK == state)
{
LOG_D("emac device init success");
}
else
{
LOG_E("emac device init faild: %d\r\n", state);
state = -RT_ERROR;
}
return state;
}
INIT_DEVICE_EXPORT(rt_hw_ch32_eth_init);
#endif /* BSP_USING_ETH */