cmake_cpputest_template/inc/extralibs/ch32v307gigabit.h

548 lines
21 KiB
C

#ifndef _CH32V307GIGABIT_H
#define _CH32V307GIGABIT_H
/* This file is written against the RTL8211E
*/
// #define CH32V307GIGABIT_MCO25 1
// #define CH32V307GIGABIT_PHYADDRESS 0
#define CH32V307GIGABIT_RXBUFNB 8
#define CH32V307GIGABIT_TXBUFNB 8
#define CH32V307GIGABIT_BUFFSIZE 1524 // 1518 + 4, Rounded up.
#define CH32V307GIGABIT_CFG_CLOCK_DELAY 4 // 0..7
#define CH32V307GIGABIT_CFG_CLOCK_PHASE 0
#ifndef CH32V307GIGABIT_MCO25
#define CH32V307GIGABIT_MCO25 1
#endif
#ifndef CH32V307GIGABIT_PHYADDRESS
#define CH32V307GIGABIT_PHYADDRESS 0
#endif
#ifndef CH32V307GIGABIT_PHY_TIMEOUT
#define CH32V307GIGABIT_PHY_TIMEOUT 0x10000
#endif
// Additional definitions, not part of ch32v003fun.h
#ifndef CH32V307GIGABIT_PHY_RSTB
#define CH32V307GIGABIT_PHY_RSTB PA10
#endif
// ETH DMA structure definition (From ch32v30x_eth.c
typedef struct
{
uint32_t volatile Status; /* Status */
uint32_t ControlBufferSize; /* Control and Buffer1, Buffer2 lengths */
uint32_t Buffer1Addr; /* Buffer1 address pointer */
uint32_t Buffer2NextDescAddr; /* Buffer2 or next descriptor address pointer */
} ETH_DMADESCTypeDef;
// You must provide:
void ch32v307ethHandleReconfig(int link, int speed, int duplex);
// Return non-zero to suppress OWN return (for if you are still holding onto the buffer)
int ch32v307ethInitHandlePacket(uint8_t *data, int frame_length, ETH_DMADESCTypeDef *dmadesc);
void ch32v307ethInitHandleTXC(void);
// This library provides:
static void ch32v307ethGetMacInUC(uint8_t *mac);
static int ch32v307ethInit(void);
static int ch32v307ethTransmitStatic(uint8_t *buffer, uint32_t length, int enable_txc); // Does not copy.
static int ch32v307ethTickPhy(void);
// Data pursuent to ethernet.
uint8_t ch32v307eth_mac[6] = {0};
uint16_t ch32v307eth_phyid = 0; // 0xc916 = RTL8211FS / 0xc915 = RTL8211E-VB
ETH_DMADESCTypeDef ch32v307eth_DMARxDscrTab[CH32V307GIGABIT_RXBUFNB] __attribute__((aligned(4))); // MAC receive descriptor, 4-byte aligned
ETH_DMADESCTypeDef ch32v307eth_DMATxDscrTab[CH32V307GIGABIT_TXBUFNB] __attribute__((aligned(4))); // MAC send descriptor, 4-byte aligned
uint8_t ch32v307eth_MACRxBuf[CH32V307GIGABIT_RXBUFNB * CH32V307GIGABIT_BUFFSIZE] __attribute__((aligned(4))); // MAC receive buffer, 4-byte aligned
ETH_DMADESCTypeDef *pDMARxGet;
ETH_DMADESCTypeDef *pDMATxSet;
// Internal functions
static int ch32v307ethPHYRegWrite(uint32_t reg, uint32_t val);
static int ch32v307ethPHYRegAsyncRead(int reg, int *value);
static int ch32v307ethPHYRegRead(uint32_t reg);
static int ch32v307ethPHYRegAsyncRead(int reg, int *value)
{
static uint8_t reg_request_count = 0;
uint32_t miiar = ETH->MACMIIAR;
if (miiar & ETH_MACMIIAR_MB)
{
return -1;
}
if (((miiar & ETH_MACMIIAR_MR) >> 6) != reg || reg_request_count < 2)
{
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11) | // ETH_MACMIIAR_PA
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
(0 /*!ETH_MACMIIAR_MW*/) | ETH_MACMIIAR_MB;
reg_request_count++;
return -1;
}
reg_request_count = 0;
*value = ETH->MACMIIDR;
ETH->MACMIIAR |= ETH_MACMIIAR_MR; // Poison register.
return 0;
}
static int ch32v307ethTickPhy(void)
{
int speed, linked, duplex;
const int reg = (ch32v307eth_phyid == 0xc916) ? 0x1a : 0x11; // PHYSR (different on each part)
int miidr;
if (ch32v307ethPHYRegAsyncRead(reg, &miidr)) return -1;
printf("REG: %02x / %04x / %04x\n", reg, miidr, ch32v307eth_phyid);
if (reg == 0x1a)
{
speed = ((miidr >> 4) & 3);
linked = ((miidr >> 2) & 1);
duplex = ((miidr >> 3) & 1);
}
else
{
speed = ((miidr >> 14) & 3);
linked = ((miidr >> 10) & 1);
duplex = ((miidr >> 13) & 1);
}
printf("LINK INFO: %d %d %d\n", speed, linked, duplex);
if (linked)
{
uint32_t oldmaccr = ETH->MACCR;
uint32_t newmaccr = (oldmaccr & ~((1 << 11) | (3 << 14))) | (speed << 14) | (duplex << 11);
if (newmaccr != oldmaccr)
{
ETH->MACCR = newmaccr;
ch32v307ethHandleReconfig(linked, speed, duplex);
return 1;
}
}
return 0;
}
// Based on ETH_WritePHYRegister
static int ch32v307ethPHYRegWrite(uint32_t reg, uint32_t val)
{
ETH->MACMIIDR = val;
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
(((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11)) | // ETH_MACMIIAR_PA
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
uint32_t timeout = 0x100000;
while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) && --timeout)
;
// If timeout = 0, is an error.
return timeout ? 0 : -1;
}
static int ch32v307ethPHYRegRead(uint32_t reg)
{
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11) | // ETH_MACMIIAR_PA
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
(0 /*!ETH_MACMIIAR_MW*/) | ETH_MACMIIAR_MB;
uint32_t timeout = 0x100000;
while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) && --timeout)
;
// If timeout = 0, is an error.
return timeout ? ETH->MACMIIDR : -1;
}
static void ch32v307ethGetMacInUC(uint8_t *mac)
{
// Mac is backwards.
const uint8_t *macaddr = (const uint8_t *)(ROM_CFG_USERADR_ID + 5);
for (int i = 0; i < 6; i++)
{
mac[i] = *(macaddr--);
}
}
static int ch32v307ethInit(void)
{
int i;
#ifdef CH32V307GIGABIT_PHY_RSTB
funPinMode(CH32V307GIGABIT_PHY_RSTB, GPIO_CFGLR_OUT_50Mhz_PP); // PHY_RSTB (For reset)
funDigitalWrite(CH32V307GIGABIT_PHY_RSTB, FUN_LOW);
#endif
// Configure strapping.
funPinMode(PA1, GPIO_CFGLR_IN_PUPD); // GMII_RXD3
funPinMode(PA0, GPIO_CFGLR_IN_PUPD); // GMII_RXD2
funPinMode(PC3, GPIO_CFGLR_IN_PUPD); // GMII_RXD1
funPinMode(PC2, GPIO_CFGLR_IN_PUPD); // GMII_RXD0
funDigitalWrite(PA1, FUN_HIGH);
funDigitalWrite(PA0, FUN_HIGH);
funDigitalWrite(PC3, FUN_HIGH); // No TX Delay
funDigitalWrite(PC2, FUN_HIGH);
// Pull-up MDIO
funPinMode(PD9, GPIO_CFGLR_OUT_50Mhz_PP); // Pull-up control (DO NOT CHECK IN, ADD RESISTOR)
funDigitalWrite(PD9, FUN_HIGH);
// Will be required later.
RCC->APB2PCENR |= RCC_APB2Periph_AFIO;
// https://cnlohr.github.io/microclockoptimizer/?chipSelect=ch32vx05_7%2Cd8c&HSI=1,8&HSE=0,8&PREDIV2=1,1&PLL2CLK=1,7&PLL2VCO=0,72&PLL3CLK=1,1&PLL3VCO=0,100&PREDIV1SRC=1,0&PREDIV1=1,2&PLLSRC=1,0&PLL=0,4&PLLVCO=1,144&SYSCLK=1,2&
// Clock Tree:
// 8MHz Input
// PREDIV2 = 2 (1 in register) = 4MHz
// PLL2 = 9 (7 in register) = 36MHz / PLL2VCO = 72MHz
// PLL3CLK = 12.5 (1 in register) = 50MHz = 100MHz VCO
// PREDIV1SRC = HSE (1 in register) = 8MHz
// PREDIV1 = 2 (1 in register).
// PLLSRC = PREDIV1 (0 in register) = 4MHz
// PLL = 18 (0 in register) = 72MHz
// PLLVCO = 144MHz
// SYSCLK = PLLVCO = 144MHz
// Use EXT_125M (ETH1G_SRC)
// Switch processor back to HSI so we don't eat dirt.
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSI;
// Setup clock tree.
RCC->CFGR2 |=
(1 << RCC_PREDIV2_OFFSET) | // PREDIV = /2; Prediv Freq = 4MHz
(1 << RCC_PLL3MUL_OFFSET) | // PLL3 = x12.5 (PLL3 = 50MHz)
(2 << RCC_ETH1GSRC_OFFSET) | // External source for RGMII
(7 << RCC_PLL2MUL_OFFSET) | // PLL2 = x9 (PLL2 = 36MHz)
(1 << RCC_PREDIV1_OFFSET) | // PREDIV1 = /2; Prediv freq = 50MHz
0;
// Power on PLLs
RCC->CTLR |= RCC_PLL3ON | RCC_PLL2ON;
int timeout;
for (timeout = 10000; timeout > 0; timeout--)
if (RCC->CTLR & RCC_PLL3RDY) break;
if (timeout == 0) return -5;
for (timeout = 10000; timeout > 0; timeout--)
if (RCC->CTLR & RCC_PLL2RDY) break;
if (timeout == 0) return -6;
// PLL = x18 (0 in register)
RCC->CFGR0 = (RCC->CFGR0 & ~(0xf << 18)) | 0;
RCC->CTLR |= RCC_PLLON;
for (timeout = 10000; timeout > 0; timeout--)
if (RCC->CTLR & RCC_PLLRDY) break;
if (timeout == 0) return -7;
// Switch to PLL.
#ifdef CH32V307GIGABIT_MCO25
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL | (9 << 24); // And output clock on PA8
#else
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
#endif
// For clock in.
funPinMode(PB1, GPIO_CFGLR_IN_FLOAT); // GMII_CLK125
RCC->CFGR2 |= RCC_ETH1G_125M_EN; // Enable 125MHz clock.
// Power on and reset.
RCC->AHBPCENR |= RCC_ETHMACEN | RCC_ETHMACTXEN | RCC_ETHMACRXEN;
RCC->AHBRSTR |= RCC_ETHMACRST;
RCC->AHBRSTR &= ~RCC_ETHMACRST;
ETH->DMABMR |= ETH_DMABMR_SR;
// Wait for reset to complete.
for (timeout = 10000; timeout > 0 && (ETH->DMABMR & ETH_DMABMR_SR); timeout--)
{
Delay_Us(10);
}
// Use RGMII
EXTEN->EXTEN_CTR |= EXTEN_ETH_RGMII_SEL; // EXTEN_ETH_RGMII_SEL;
funPinMode(PB13, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_MDIO
funPinMode(PB12, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_MDC
// For clock output to Ethernet module.
funPinMode(PA8, GPIO_CFGLR_OUT_50Mhz_AF_PP); // PHY_CKTAL
// Release PHY from reset.
#ifdef CH32V307GIGABIT_PHY_RSTB
funDigitalWrite(CH32V307GIGABIT_PHY_RSTB, FUN_HIGH);
#endif
Delay_Ms(25); // Waiting for PHY to exit sleep. This is inconsistent at 23ms (But only on the RTL8211FS) None is needed on the RTL8211E
funPinMode(PB0, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD3
funPinMode(PC5, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD2
funPinMode(PC4, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD1
funPinMode(PA7, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD0
funPinMode(PA3, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXCTL
funPinMode(PA2, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXC
funPinMode(PA1, GPIO_CFGLR_IN_PUPD); // GMII_RXD3
funPinMode(PA0, GPIO_CFGLR_IN_PUPD); // GMII_RXD2
funPinMode(PC3, GPIO_CFGLR_IN_PUPD); // GMII_RXD1
funPinMode(PC2, GPIO_CFGLR_IN_PUPD); // GMII_RXD0
funPinMode(PC1, GPIO_CFGLR_IN_PUPD); // GMII_RXCTL
funPinMode(PC0, GPIO_CFGLR_IN_FLOAT); // GMII_RXC
funDigitalWrite(PA1, FUN_HIGH); // SELGRV = 3.3V
funDigitalWrite(PA0, FUN_HIGH); // TXDelay = 1
funDigitalWrite(PC3, FUN_HIGH); // AN[0] = 1
funDigitalWrite(PC2, FUN_HIGH); // AN[1] = 1
funDigitalWrite(PC1, FUN_LOW); // PHYAD[0]
// Configure MDC/MDIO
// Conflicting notes - some say /42, others don't.
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42;
// Update MACCR
ETH->MACCR =
(CH32V307GIGABIT_CFG_CLOCK_DELAY << 29) | // No clock delay
(0 << 23) | // Max RX = 2kB (Revisit if looking into jumbo frames)
(0 << 22) | // Max TX = 2kB (Revisit if looking into jumbo frames)
(0 << 21) | // Rated Drive (instead of energy savings mode) (10M PHY only)
(1 << 20) | // Bizarre re-use of termination resistor terminology? (10M PHY Only)
(0 << 17) | // IFG = 0, 96-bit guard time.
(0 << 14) | // FES = 2 = GBE, 1=100MBit/s (UNSET TO START)
(0 << 12) | // Self Loop = 0
(0 << 11) | // Full-Duplex Mode (UNSET TO START)
(1 << 10) | // IPCO = 1, Check TCP, UDP, ICMP header checksums.
(1 << 7) | // APCS (automatically strip frames)
(1 << 3) | // TE (Transmit enable!)
(1 << 2) | // RE (Receive Enable)
(CH32V307GIGABIT_CFG_CLOCK_PHASE << 1) | // TCF = 0 (Potentailly change if clocking is wrong)
0;
Delay_Ms(25); // Waiting for PHY to exit sleep. This is inconsistent at 19ms.
// Reset the physical layer
ch32v307ethPHYRegWrite(PHY_BCR,
PHY_Reset |
1 << 12 | // Auto negotiate
1 << 8 | // Duplex
1 << 6 | // Speed Bit.
0);
// De-assert reset.
ch32v307ethPHYRegWrite(PHY_BCR,
1 << 12 | // Auto negotiate
1 << 8 | // Duplex
1 << 6 | // Speed Bit.
0);
ch32v307ethPHYRegRead(0x03);
ch32v307eth_phyid = ch32v307ethPHYRegRead(0x03); // Read twice to be safe.
if (ch32v307eth_phyid == 0xc916)
ch32v307ethPHYRegWrite(0x1F, 0x0a43); // RTL8211FS needs page select.
ch32v307ethGetMacInUC(ch32v307eth_mac);
ETH->MACA0HR = (uint32_t)((ch32v307eth_mac[5] << 8) | ch32v307eth_mac[4]);
ETH->MACA0LR = (uint32_t)(ch32v307eth_mac[0] | (ch32v307eth_mac[1] << 8) | (ch32v307eth_mac[2] << 16) | (ch32v307eth_mac[3] << 24));
ETH->MACFFR = (uint32_t)(ETH_ReceiveAll_Disable |
ETH_SourceAddrFilter_Disable |
ETH_PassControlFrames_BlockAll |
ETH_BroadcastFramesReception_Enable |
ETH_DestinationAddrFilter_Normal |
ETH_PromiscuousMode_Disable |
ETH_MulticastFramesFilter_Perfect |
ETH_UnicastFramesFilter_Perfect);
ETH->MACHTHR = (uint32_t)0;
ETH->MACHTLR = (uint32_t)0;
ETH->MACVLANTR = (uint32_t)(ETH_VLANTagComparison_16Bit);
ETH->MACFCR = 0; // No pause frames.
// Configure RX/TX chains.
ETH_DMADESCTypeDef *tdesc;
for (i = 0; i < CH32V307GIGABIT_TXBUFNB; i++)
{
tdesc = ch32v307eth_DMATxDscrTab + i;
tdesc->ControlBufferSize = 0;
tdesc->Status = ETH_DMATxDesc_TCH | ETH_DMATxDesc_IC | ETH_DMATxDesc_FS;
tdesc->Buffer1Addr = (uint32_t)0; // Populate with data.
tdesc->Buffer2NextDescAddr = (i < CH32V307GIGABIT_TXBUFNB - 1) ? ((uint32_t)(ch32v307eth_DMATxDscrTab + i + 1)) : (uint32_t)ch32v307eth_DMATxDscrTab;
}
ETH->DMATDLAR = (uint32_t)ch32v307eth_DMATxDscrTab;
for (i = 0; i < CH32V307GIGABIT_RXBUFNB; i++)
{
tdesc = ch32v307eth_DMARxDscrTab + i;
tdesc->Status = ETH_DMARxDesc_OWN;
tdesc->ControlBufferSize = ETH_DMARxDesc_RCH | (uint32_t)CH32V307GIGABIT_BUFFSIZE;
tdesc->Buffer1Addr = (uint32_t)(&ch32v307eth_MACRxBuf[i * CH32V307GIGABIT_BUFFSIZE]);
tdesc->Buffer2NextDescAddr = (i < CH32V307GIGABIT_RXBUFNB - 1) ? (uint32_t)(ch32v307eth_DMARxDscrTab + i + 1) : (uint32_t)(ch32v307eth_DMARxDscrTab);
}
ETH->DMARDLAR = (uint32_t)ch32v307eth_DMARxDscrTab;
pDMARxGet = ch32v307eth_DMARxDscrTab;
pDMATxSet = ch32v307eth_DMATxDscrTab;
// Receive a good frame half interrupt mask.
// Receive CRC error frame half interrupt mask.
// For the future: Why do we want this?
ETH->MMCTIMR = ETH_MMCTIMR_TGFM;
ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFCEM;
ETH->DMAIER = ETH_DMA_IT_NIS | // Normal interrupt enable.
ETH_DMA_IT_R | // Receive
ETH_DMA_IT_T | // Transmit
ETH_DMA_IT_AIS | // Abnormal interrupt
ETH_DMA_IT_RBU; // Receive buffer unavailable interrupt enable
NVIC_EnableIRQ(ETH_IRQn);
// Actually enable receiving process.
ETH->DMAOMR = ETH_DMAOMR_SR | ETH_DMAOMR_ST | ETH_DMAOMR_TSF | ETH_DMAOMR_FEF;
return 0;
}
void ETH_IRQHandler(void) __attribute__((interrupt));
void ETH_IRQHandler(void)
{
uint32_t int_sta;
do
{
int_sta = ETH->DMASR;
if ((int_sta & (ETH_DMA_IT_AIS | ETH_DMA_IT_NIS)) == 0)
{
break;
}
// Off nominal situations.
if (int_sta & ETH_DMA_IT_AIS)
{
// Receive buffer unavailable interrupt enable.
if (int_sta & ETH_DMA_IT_RBU)
{
ETH->DMASR = ETH_DMA_IT_RBU;
if ((INFO->CHIPID & 0xf0) == 0x10)
{
((ETH_DMADESCTypeDef *)(((ETH_DMADESCTypeDef *)(ETH->DMACHRDR))->Buffer2NextDescAddr))->Status = ETH_DMARxDesc_OWN;
ETH->DMARPDR = 0;
}
}
ETH->DMASR = ETH_DMA_IT_AIS;
}
// Nominal interrupts.
if (int_sta & ETH_DMA_IT_NIS)
{
if (int_sta & ETH_DMA_IT_R)
{
// Received a packet, normally.
// Status is in Table 27-17 Definitions of RDes0
do
{
// XXX TODO: Is this a good place to acknowledge? REVISIT: Should this go lower?
// XXX TODO: Restructure this to allow for
ETH->DMASR = ETH_DMA_IT_R;
uint32_t status = pDMARxGet->Status;
if (status & ETH_DMARxDesc_OWN) break;
// We only have a valid packet in a specific situation.
// So, we take the status, then mask off the bits we care about
// And see if they're equal to the ones that need to be set/unset.
const uint32_t mask =
ETH_DMARxDesc_OWN |
ETH_DMARxDesc_LS |
ETH_DMARxDesc_ES |
ETH_DMARxDesc_FS;
const uint32_t eq =
0 |
ETH_DMARxDesc_LS |
0 |
ETH_DMARxDesc_FS;
int suppress_own = 0;
if ((status & mask) == eq)
{
int32_t frame_length = ((status & ETH_DMARxDesc_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT) - 4;
if (frame_length > 0)
{
uint8_t *data = (uint8_t *)pDMARxGet->Buffer1Addr;
suppress_own = ch32v307ethInitHandlePacket(data, frame_length, pDMARxGet);
}
}
// Otherwise, Invalid Packet
// Relinquish control back to underlying hardware.
if (!suppress_own)
pDMARxGet->Status = ETH_DMARxDesc_OWN;
// Tricky logic for figuring out the next packet. Originally
// discussed in ch32v30x_eth.c in ETH_DropRxPkt
if ((pDMARxGet->ControlBufferSize & ETH_DMARxDesc_RCH) != (uint32_t)RESET)
pDMARxGet = (ETH_DMADESCTypeDef *)(pDMARxGet->Buffer2NextDescAddr);
else
{
if ((pDMARxGet->ControlBufferSize & ETH_DMARxDesc_RER) != (uint32_t)RESET)
pDMARxGet = (ETH_DMADESCTypeDef *)(ETH->DMARDLAR);
else
pDMARxGet = (ETH_DMADESCTypeDef *)((uint32_t)pDMARxGet + 0x10 + ((ETH->DMABMR & ETH_DMABMR_DSL) >> 2));
}
} while (1);
}
if (int_sta & ETH_DMA_IT_T)
{
ch32v307ethInitHandleTXC();
ETH->DMASR = ETH_DMA_IT_T;
}
ETH->DMASR = ETH_DMA_IT_NIS;
}
} while (1);
}
static int ch32v307ethTransmitStatic(uint8_t *buffer, uint32_t length, int enable_txc)
{
// The official SDK waits until ETH_DMATxDesc_TTSS is set.
// This also provides a transmit timestamp, which could be
// used for PTP.
// But we don't want to do that.
// We just want to go. If anyone cares, they can check later.
if (pDMATxSet->Status & ETH_DMATxDesc_OWN)
{
ETH->DMATPDR = 0;
return -1;
}
pDMATxSet->ControlBufferSize = (length & ETH_DMATxDesc_TBS1);
pDMATxSet->Buffer1Addr = (uint32_t)buffer;
// Status is in Table 27-12 "Definitions of TDes0 bits"
enable_txc = enable_txc ? ETH_DMATxDesc_IC : 0;
pDMATxSet->Status =
ETH_DMATxDesc_LS | // Last Segment (This is all you need to have to transmit)
ETH_DMATxDesc_FS | // First Segment (Beginning of transmission)
enable_txc | // Interrupt when complete
ETH_DMATxDesc_TCH | // Next Descriptor Address Valid
ETH_DMATxDesc_CIC_TCPUDPICMP_Full | // Do all header checksums.
ETH_DMATxDesc_OWN; // Own back to hardware
pDMATxSet = (ETH_DMADESCTypeDef *)pDMATxSet->Buffer2NextDescAddr;
ETH->DMASR = ETH_DMASR_TBUS; // This resets the transmit process (or "starts" it)
ETH->DMATPDR = 0;
return 0;
}
#endif