Unify naming: all network stacks are in src/net_...

This commit is contained in:
cpq committed 2023-08-18 19:59:15 +01:00
1 parent 69d2ba7316
commit 89a9a50b96
16 files changed
+1101 -1103

No files matched your search

+264
View File
@@ -0,0 +1,264 @@
#include "tcpip.h"
/*
* Todo
* This driver doesn't support 10M line autoconfiguration yet.
* Packets aren't sent if the link negociated 10M line.
* todo: MAC back auto reconfiguration.
*/
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_IMXRT1020)
struct imx_rt1020_enet {
volatile uint32_t RESERVED0, EIR, EIMR, RESERVED1, RDAR, TDAR, RESERVED2[3], ECR, RESERVED3[6], MMFR, MSCR, RESERVED4[7], MIBC, RESERVED5[7], RCR, RESERVED6[15], TCR, RESERVED7[7], PALR, PAUR, OPD, TXIC0, TXIC1, TXIC2, RESERVED8, RXIC0, RXIC1, RXIC2, RESERVED9[3], IAUR, IALR, GAUR, GALR, RESERVED10[7], TFWR, RESERVED11[14], RDSR, TDSR, MRBR[2], RSFL, RSEM, RAEM, RAFL, TSEM, TAEM, TAFL, TIPG, FTRL, RESERVED12[3], TACC, RACC, RESERVED13[15], RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2048, RMON_T_GTE2048, RMON_T_OCTETS, IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, IEEE_T_FDXFC, IEEE_T_OCTETS_OK, RESERVED14[3], RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, RESERVED15, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, RMON_R_GTE2048, RMON_R_OCTETS, IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, IEEE_R_FDXFC, IEEE_R_OCTETS_OK, RESERVED16[71], ATCR, ATVR, ATOFF, ATPER, ATCOR, ATINC, ATSTMP, RESERVED17[122], TGSR, TCSR0, TCCR0, TCSR1, TCCR1, TCSR2, TCCR2, TCSR3;
};
#undef ENET
#define ENET ((struct imx_rt1020_enet *) (uintptr_t) 0x402D8000u)
#undef BIT
#define BIT(x) ((uint32_t) 1 << (x))
#define ENET_RXBUFF_SIZE 1536 // 1522 Buffer must be 64bits aligned
#define ENET_TXBUFF_SIZE 1536 // 1522 hence set to 0x600 (1536)
#define ENET_RXBD_NUM (4)
#define ENET_TXBD_NUM (4)
const uint32_t EIMR_RX_ERR = 0x2400000; // Intr mask RXF+EBERR
void ETH_IRQHandler(void);
static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp);
static void wait_phy_complete(void);
static struct mg_tcpip_if *s_ifp; // MIP interface
static size_t mg_tcpip_driver_imxrt1020_tx(const void *, size_t , struct mg_tcpip_if *);
static bool mg_tcpip_driver_imxrt1020_up(struct mg_tcpip_if *ifp);
enum { IMXRT1020_PHY_ADDR = 0x02, IMXRT1020_PHY_BCR = 0, IMXRT1020_PHY_BSR = 1 }; // PHY constants
void delay(uint32_t);
void delay (uint32_t di) {
volatile int dno = 0; // Prevent optimization
for (uint32_t i = 0; i < di; i++)
for (int j=0; j<20; j++) // PLLx20 (500 MHz/24MHz)
dno++;
}
static void wait_phy_complete(void) {
delay(0x00010000);
const uint32_t delay_max = 0x00100000;
uint32_t delay_cnt = 0;
while (!(ENET->EIR & BIT(23)) && (delay_cnt < delay_max))
{delay_cnt++;}
ENET->EIR |= BIT(23); // MII interrupt clear
}
static uint32_t imxrt1020_eth_read_phy(uint8_t addr, uint8_t reg) {
ENET->EIR |= BIT(23); // MII interrupt clear
uint32_t mask_phy_adr_reg = 0x1f; // 0b00011111: Ensure we write 5 bits (Phy address & register)
uint32_t phy_transaction = 0x00;
phy_transaction = (0x1 << 30) \
| (0x2 << 28) \
| ((uint32_t)(addr & mask_phy_adr_reg) << 23) \
| ((uint32_t)(reg & mask_phy_adr_reg) << 18) \
| (0x2 << 16);
ENET->MMFR = phy_transaction;
wait_phy_complete();
return (ENET->MMFR & 0x0000ffff);
}
static void imxrt1020_eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
ENET->EIR |= BIT(23); // MII interrupt clear
uint8_t mask_phy_adr_reg = 0x1f; // 0b00011111: Ensure we write 5 bits (Phy address & register)
uint32_t mask_phy_data = 0x0000ffff; // Ensure we write 16 bits (data)
addr &= mask_phy_adr_reg;
reg &= mask_phy_adr_reg;
val &= mask_phy_data;
uint32_t phy_transaction = 0x00;
phy_transaction = (uint32_t)(0x1 << 30) \
| (uint32_t)(0x1 << 28) \
| (uint32_t)(addr << 23) \
| (uint32_t)(reg << 18) \
| (uint32_t)(0x2 << 16) \
| (uint32_t)(val);
ENET->MMFR = phy_transaction;
wait_phy_complete();
}
// FEC RX/TX descriptors (Enhanced descriptor not enabled)
// Descriptor buffer structure, little endian
typedef struct enet_bd_struct_def
{
uint16_t length; // Data length
uint16_t control; // Control and status
uint32_t *buffer; // Data ptr
} enet_bd_struct_t;
// Descriptor and buffer globals, in non-cached area, 64 bits aligned.
__attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t rx_buffer_descriptor[(ENET_RXBD_NUM)] __attribute__((aligned((64U))));
__attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t tx_buffer_descriptor[(ENET_TXBD_NUM)] __attribute__((aligned((64U))));
uint8_t rx_data_buffer[(ENET_RXBD_NUM)][((unsigned int)(((ENET_RXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U))));
uint8_t tx_data_buffer[(ENET_TXBD_NUM)][((unsigned int)(((ENET_TXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U))));
// Initialise driver imx_rt1020
// static bool mg_tcpip_driver_imxrt1020_init(uint8_t *mac, void *data) { // VO
static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_imxrt1020_data *d = (struct mg_tcpip_driver_imxrt1020_data *) ifp->driver_data;
s_ifp = ifp;
// ENET Reset, wait complete
ENET->ECR |= BIT(0);
while((ENET->ECR & BIT(0)) != 0) {}
// Re-latches the pin strapping pin values
ENET->ECR |= BIT(0);
while((ENET->ECR & BIT(0)) != 0) {}
// Setup MII/RMII MDC clock divider (<= 2.5MHz).
ENET->MSCR = 0x130; // HOLDTIME 2 clk, Preamble enable, MDC MII_Speed Div 0x30
imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, 0x8000); // PHY W @0x00 D=0x8000 Soft reset
while (imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BSR) & BIT(15)) {delay(0x5000);} // Wait finished poll 10ms
// PHY: Start Link
{
imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, 0x1200); // PHY W @0x00 D=0x1200 Autonego enable + start
imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, 0x1f, 0x8180); // PHY W @0x1f D=0x8180 Ref clock 50 MHz at XI input
uint32_t bcr = imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR);
bcr &= ~BIT(10); // Isolation -> Normal
imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, bcr);
}
// Disable ENET
ENET->ECR = 0x0; // Disable before configuration
// Configure ENET
ENET->RCR = 0x05ee0104; // #CRCFWD=0 (CRC kept in frame) + RMII + MII Enable
ENET->TCR = BIT(8) | BIT(2); // Addins (MAC address from PAUR+PALR) + Full duplex enable
//ENET->TFWR = BIT(8); // Store And Forward Enable, 64 bytes (minimize tx latency)
// Configure descriptors and buffers
// RX
for (int i = 0; i < ENET_RXBD_NUM; i++) {
// Wrap last descriptor buffer ptr
rx_buffer_descriptor[i].control = (BIT(15) | ((i<(ENET_RXBD_NUM-1))?0:BIT(13))); // E+(W*)
rx_buffer_descriptor[i].buffer = (uint32_t *)rx_data_buffer[i];
}
// TX
for (int i = 0; i < ENET_TXBD_NUM; i++) {
// Wrap last descriptor buffer ptr
tx_buffer_descriptor[i].control = ((i<(ENET_RXBD_NUM-1))?0:BIT(13)) | BIT(10); // (W*)+TC
tx_buffer_descriptor[i].buffer = (uint32_t *)tx_data_buffer[i];
}
// Continue ENET configuration
ENET->RDSR = (uint32_t)(uintptr_t)rx_buffer_descriptor;
ENET->TDSR = (uint32_t)(uintptr_t)tx_buffer_descriptor;
ENET->MRBR[0] = ENET_RXBUFF_SIZE; // Same size for RX/TX buffers
// MAC address filtering (bytes in reversed order)
ENET->PAUR = ((uint32_t) ifp->mac[4] << 24U) | (uint32_t) ifp->mac[5] << 16U;
ENET->PALR = (uint32_t) (ifp->mac[0] << 24U) | ((uint32_t) ifp->mac[1] << 16U) |
((uint32_t) ifp->mac[2] << 8U) | ifp->mac[3];
// Init Hash tables (mac filtering)
ENET->IAUR = 0; // Unicast
ENET->IALR = 0;
ENET->GAUR = 0; // Multicast
ENET->GALR = 0;
// Set ENET Online
ENET->ECR |= BIT(8); // ENET Set Little-endian + (FEC buffer desc.)
ENET->ECR |= BIT(1); // Enable
// Set interrupt mask
ENET->EIMR = EIMR_RX_ERR;
// RX Descriptor activation
ENET->RDAR = BIT(24); // Activate Receive Descriptor
return true;
}
// Transmit frame
static uint32_t s_rt1020_txno;
static size_t mg_tcpip_driver_imxrt1020_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
if (len > sizeof(tx_data_buffer[ENET_TXBD_NUM])) {
// MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // Frame is too big
} else if ((tx_buffer_descriptor[s_rt1020_txno].control & BIT(15))) {
MG_ERROR(("No free descriptors"));
// printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR);
len = 0; // All descriptors are busy, fail
} else {
memcpy(tx_data_buffer[s_rt1020_txno], buf, len); // Copy data
tx_buffer_descriptor[s_rt1020_txno].length = (uint16_t) len; // Set data len
tx_buffer_descriptor[s_rt1020_txno].control |= (uint16_t)(BIT(10)); // TC (transmit CRC)
// tx_buffer_descriptor[s_rt1020_txno].control &= (uint16_t)(BIT(14) | BIT(12)); // Own doesn't affect HW
tx_buffer_descriptor[s_rt1020_txno].control |= (uint16_t)(BIT(15) | BIT(11)); // R+L (ready+last)
ENET->TDAR = BIT(24); // Descriptor updated. Hand over to DMA.
// INFO
// Relevant Descriptor bits: 15(R) Ready
// 11(L) last in frame
// 10(TC) transmis CRC
// __DSB(); // ARM errata 838869 Cortex-M4, M4F, M7, M7F: "store immediate overlapping
// exception" return might vector to incorrect interrupt.
if (++s_rt1020_txno >= ENET_TXBD_NUM) s_rt1020_txno = 0;
}
(void) ifp;
return len;
}
// IRQ (RX)
static uint32_t s_rt1020_rxno;
void ENET_IRQHandler(void) {
ENET->EIMR = 0; // Mask interrupts.
uint32_t eir = ENET->EIR; // Read EIR
ENET->EIR = 0xffffffff; // Clear interrupts
if (eir & EIMR_RX_ERR) // Global mask used
{
if (rx_buffer_descriptor[s_rt1020_rxno].control & BIT(15)) {
ENET->EIMR = EIMR_RX_ERR; // Enable interrupts
return; // Empty? -> exit.
}
// Read inframes
else { // Frame received, loop
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
if (rx_buffer_descriptor[s_rt1020_rxno].control & BIT(15)) break; // exit when done
// Process if CRC OK and frame not truncated
if (!(rx_buffer_descriptor[s_rt1020_rxno].control & (BIT(2) | BIT(0)))) {
uint32_t len = (rx_buffer_descriptor[s_rt1020_rxno].length);
mg_tcpip_qwrite(rx_buffer_descriptor[s_rt1020_rxno].buffer, len > 4 ? len - 4 : len, s_ifp);
}
rx_buffer_descriptor[s_rt1020_rxno].control |= BIT(15); // Inform DMA RX is empty
if (++s_rt1020_rxno >= ENET_RXBD_NUM) s_rt1020_rxno = 0;
}
}
}
ENET->EIMR = EIMR_RX_ERR; // Enable interrupts
}
// Up/down status
static bool mg_tcpip_driver_imxrt1020_up(struct mg_tcpip_if *ifp) {
uint32_t bsr = imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BSR);
(void) ifp;
return bsr & BIT(2) ? 1 : 0;
}
// API
struct mg_tcpip_driver mg_tcpip_driver_imxrt1020 = {
mg_tcpip_driver_imxrt1020_init, mg_tcpip_driver_imxrt1020_tx, NULL,
mg_tcpip_driver_imxrt1020_up};
#endif
+15
View File
@@ -0,0 +1,15 @@
#pragma once
struct mg_tcpip_driver_imxrt1020_data {
// MDC clock divider. MDC clock is derived from IPS Bus clock (ipg_clk),
// must not exceed 2.5MHz. Configuration for clock range 2.36~2.50 MHz
// ipg_clk MSCR mdc_cr VALUE
// -------------------------------------
// -1 <-- tell driver to guess the value
// 25 MHz 0x04 0
// 33 MHz 0x06 1
// 40 MHz 0x07 2
// 50 MHz 0x09 3
// 66 MHz 0x0D 4 <-- value for iMXRT1020-EVK at max freq.
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4
};
+214
View File
@@ -0,0 +1,214 @@
#include "tcpip.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && \
MG_ENABLE_DRIVER_SAME54
#include <sam.h>
#undef BIT
#define BIT(x) ((uint32_t) 1 << (x))
#define ETH_PKT_SIZE 1536 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 2 // Descriptor size (words)
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_txno; // Current TX descriptor
static uint8_t s_rxno; // Current RX descriptor
static struct mg_tcpip_if *s_ifp; // MIP interface
enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1 };
#define PHY_BCR_DUPLEX_MODE_Msk BIT(8)
#define PHY_BCR_SPEED_Msk BIT(13)
#define PHY_BSR_LINK_STATUS_Msk BIT(2)
static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
GMAC_REGS->GMAC_MAN = GMAC_MAN_CLTTO_Msk |
GMAC_MAN_OP(2) | // Setting the read operation
GMAC_MAN_WTN(2) | GMAC_MAN_PHYA(addr) | // PHY address
GMAC_MAN_REGA(reg); // Setting the register
while (!(GMAC_REGS->GMAC_NSR & GMAC_NSR_IDLE_Msk)) (void) 0;
return GMAC_REGS->GMAC_MAN & GMAC_MAN_DATA_Msk; // Getting the read value
}
#if 0
static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
GMAC_REGS->GMAC_MAN = GMAC_MAN_CLTTO_Msk | GMAC_MAN_OP(1) | // Setting the write operation
GMAC_MAN_WTN(2) | GMAC_MAN_PHYA(addr) | // PHY address
GMAC_MAN_REGA(reg) | GMAC_MAN_DATA(val); // Setting the register
while (!(GMAC_REGS->GMAC_NSR & GMAC_NSR_IDLE_Msk)); // Waiting until the write op is complete
}
#endif
int get_clock_rate(struct mg_tcpip_driver_same54_data *d) {
if (d && d->mdc_cr >= 0 && d->mdc_cr <= 5) {
return d->mdc_cr;
} else {
// get MCLK from GCLK_GENERATOR 0
uint32_t div = 512;
uint32_t mclk;
if (!(GCLK_REGS->GCLK_GENCTRL[0] & GCLK_GENCTRL_DIVSEL_Msk)) {
div = ((GCLK_REGS->GCLK_GENCTRL[0] & 0x00FF0000) >> 16);
if (div == 0) div = 1;
}
switch (GCLK_REGS->GCLK_GENCTRL[0] & GCLK_GENCTRL_SRC_Msk) {
case GCLK_GENCTRL_SRC_XOSC0_Val:
mclk = 32000000UL; /* 32MHz */
break;
case GCLK_GENCTRL_SRC_XOSC1_Val:
mclk = 32000000UL; /* 32MHz */
break;
case GCLK_GENCTRL_SRC_OSCULP32K_Val:
mclk = 32000UL;
break;
case GCLK_GENCTRL_SRC_XOSC32K_Val:
mclk = 32000UL;
break;
case GCLK_GENCTRL_SRC_DFLL_Val:
mclk = 48000000UL; /* 48MHz */
break;
case GCLK_GENCTRL_SRC_DPLL0_Val:
mclk = 200000000UL; /* 200MHz */
break;
case GCLK_GENCTRL_SRC_DPLL1_Val:
mclk = 200000000UL; /* 200MHz */
break;
default:
mclk = 200000000UL; /* 200MHz */
}
mclk /= div;
uint8_t crs[] = {0, 1, 2, 3, 4, 5}; // GMAC->NCFGR::CLK values
uint8_t dividers[] = {8, 16, 32, 48, 64, 128}; // Respective CLK dividers
for (int i = 0; i < 6; i++) {
if (mclk / dividers[i] <= 2375000UL /* 2.5MHz - 5% */) {
return crs[i];
}
}
return 5;
}
}
static bool mg_tcpip_driver_same54_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_same54_data *d =
(struct mg_tcpip_driver_same54_data *) ifp->driver_data;
s_ifp = ifp;
MCLK_REGS->MCLK_APBCMASK |= MCLK_APBCMASK_GMAC_Msk;
MCLK_REGS->MCLK_AHBMASK |= MCLK_AHBMASK_GMAC_Msk;
GMAC_REGS->GMAC_NCFGR = GMAC_NCFGR_CLK(get_clock_rate(d)); // Set MDC divider
GMAC_REGS->GMAC_NCR = 0; // Disable RX & TX
GMAC_REGS->GMAC_NCR |= GMAC_NCR_MPE_Msk; // Enable MDC & MDIO
for (int i = 0; i < ETH_DESC_CNT; i++) { // Init TX descriptors
s_txdesc[i][0] = (uint32_t) s_txbuf[i]; // Point to data buffer
s_txdesc[i][1] = BIT(31); // OWN bit
}
s_txdesc[ETH_DESC_CNT - 1][1] |= BIT(30); // Last tx descriptor - wrap
GMAC_REGS->GMAC_DCFGR = GMAC_DCFGR_DRBS(0x18); // DMA recv buf 1536
for (int i = 0; i < ETH_DESC_CNT; i++) { // Init RX descriptors
s_rxdesc[i][0] = (uint32_t) s_rxbuf[i]; // Address of the data buffer
s_rxdesc[i][1] = 0; // Clear status
}
s_rxdesc[ETH_DESC_CNT - 1][0] |= BIT(1); // Last rx descriptor - wrap
GMAC_REGS->GMAC_TBQB = (uint32_t) s_txdesc; // about the descriptor addresses
GMAC_REGS->GMAC_RBQB = (uint32_t) s_rxdesc; // Let the controller know
GMAC_REGS->SA[0].GMAC_SAB =
MG_U32(ifp->mac[3], ifp->mac[2], ifp->mac[1], ifp->mac[0]);
GMAC_REGS->SA[0].GMAC_SAT = MG_U32(0, 0, ifp->mac[5], ifp->mac[4]);
GMAC_REGS->GMAC_UR &= ~GMAC_UR_MII_Msk; // Disable MII, use RMII
GMAC_REGS->GMAC_NCFGR |= GMAC_NCFGR_MAXFS_Msk | GMAC_NCFGR_MTIHEN_Msk |
GMAC_NCFGR_EFRHD_Msk | GMAC_NCFGR_CAF_Msk;
GMAC_REGS->GMAC_TSR = GMAC_TSR_HRESP_Msk | GMAC_TSR_UND_Msk |
GMAC_TSR_TXCOMP_Msk | GMAC_TSR_TFC_Msk |
GMAC_TSR_TXGO_Msk | GMAC_TSR_RLE_Msk |
GMAC_TSR_COL_Msk | GMAC_TSR_UBR_Msk;
GMAC_REGS->GMAC_RSR = GMAC_RSR_HNO_Msk | GMAC_RSR_RXOVR_Msk |
GMAC_RSR_REC_Msk | GMAC_RSR_BNA_Msk;
GMAC_REGS->GMAC_IDR = ~0U; // Disable interrupts, then enable required
GMAC_REGS->GMAC_IER = GMAC_IER_HRESP_Msk | GMAC_IER_ROVR_Msk |
GMAC_IER_TCOMP_Msk | GMAC_IER_TFC_Msk |
GMAC_IER_RLEX_Msk | GMAC_IER_TUR_Msk |
GMAC_IER_RXUBR_Msk | GMAC_IER_RCOMP_Msk;
GMAC_REGS->GMAC_NCR |= GMAC_NCR_TXEN_Msk | GMAC_NCR_RXEN_Msk;
NVIC_EnableIRQ(GMAC_IRQn);
return true;
}
static size_t mg_tcpip_driver_same54_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // Frame is too big
} else if ((s_txdesc[s_txno][1] & BIT(31)) == 0) {
ifp->nerr++;
MG_ERROR(("No free descriptors"));
len = 0; // All descriptors are busy, fail
} else {
uint32_t status = len | BIT(15); // Frame length, last chunk
if (s_txno == ETH_DESC_CNT - 1) status |= BIT(30); // wrap
memcpy(s_txbuf[s_txno], buf, len); // Copy data
s_txdesc[s_txno][1] = status;
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
__DSB(); // Ensure descriptors have been written
GMAC_REGS->GMAC_NCR |= GMAC_NCR_TSTART_Msk; // Enable transmission
return len;
}
static bool mg_tcpip_driver_same54_up(struct mg_tcpip_if *ifp) {
uint16_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
bool up = bsr & PHY_BSR_LINK_STATUS_Msk ? 1 : 0;
// If PHY is ready, update NCFGR accordingly
if (ifp->state == MG_TCPIP_STATE_DOWN && up) {
uint16_t bcr = eth_read_phy(PHY_ADDR, PHY_BCR);
bool fd = bcr & PHY_BCR_DUPLEX_MODE_Msk ? 1 : 0;
bool spd = bcr & PHY_BCR_SPEED_Msk ? 1 : 0;
GMAC_REGS->GMAC_NCFGR |= GMAC_NCFGR_SPD(spd) | GMAC_NCFGR_FD(fd);
}
return up;
}
void GMAC_Handler(void);
void GMAC_Handler(void) {
uint32_t isr = GMAC_REGS->GMAC_ISR;
uint32_t rsr = GMAC_REGS->GMAC_RSR;
uint32_t tsr = GMAC_REGS->GMAC_TSR;
if (isr & GMAC_ISR_RCOMP_Msk) {
if (rsr & GMAC_ISR_RCOMP_Msk) {
for (uint8_t i = 0; i < ETH_DESC_CNT; i++) {
if ((s_rxdesc[s_rxno][0] & BIT(0)) == 0) break;
size_t len = s_rxdesc[s_rxno][1] & (BIT(13) - 1);
mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
s_rxdesc[s_rxno][0] &= ~BIT(0); // Disown
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
}
}
if ((tsr & (GMAC_TSR_HRESP_Msk | GMAC_TSR_UND_Msk | GMAC_TSR_TXCOMP_Msk |
GMAC_TSR_TFC_Msk | GMAC_TSR_TXGO_Msk | GMAC_TSR_RLE_Msk |
GMAC_TSR_COL_Msk | GMAC_TSR_UBR_Msk)) != 0) {
// MG_INFO((" --> %#x %#x", s_txdesc[s_txno][1], tsr));
if (!(s_txdesc[s_txno][1] & BIT(31))) s_txdesc[s_txno][1] |= BIT(31);
}
GMAC_REGS->GMAC_RSR = rsr;
GMAC_REGS->GMAC_TSR = tsr;
}
struct mg_tcpip_driver mg_tcpip_driver_same54 = {
mg_tcpip_driver_same54_init, mg_tcpip_driver_same54_tx, NULL,
mg_tcpip_driver_same54_up};
#endif
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && MG_ENABLE_DRIVER_SAME54
struct mg_tcpip_driver_same54_data {
int mdc_cr;
};
#endif
+231
View File
@@ -0,0 +1,231 @@
#include "tcpip.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32) && MG_ENABLE_DRIVER_STM32
struct stm32_eth {
volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,
MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR,
MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR,
RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR,
RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR,
RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR,
PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564],
DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER,
DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR,
DMACHRBAR;
};
#undef ETH
#define ETH ((struct stm32_eth *) (uintptr_t) 0x40028000)
#undef DSB
#if defined(__CC_ARM)
#define DSB() __dsb(0xF)
#elif defined(__ARMCC_VERSION)
#define DSB() __builtin_arm_dsb(0xF)
#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__)
#define DSB() asm("DSB 0xF")
#elif defined(__ICCARM__)
#define DSB() __iar_builtin_DSB()
#else
#define DSB()
#endif
#undef BIT
#define BIT(x) ((uint32_t) 1 << (x))
#define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words)
static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
static uint8_t s_txno; // Current TX descriptor
static uint8_t s_rxno; // Current RX descriptor
static struct mg_tcpip_if *s_ifp; // MIP interface
enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1, PHY_CSCR = 31 };
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
ETH->MACMIIAR &= (7 << 2);
ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
ETH->MACMIIAR |= BIT(0);
while (ETH->MACMIIAR & BIT(0)) (void) 0;
return ETH->MACMIIDR;
}
static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
ETH->MACMIIDR = val;
ETH->MACMIIAR &= (7 << 2);
ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
ETH->MACMIIAR |= BIT(0);
while (ETH->MACMIIAR & BIT(0)) (void) 0;
}
static uint32_t get_hclk(void) {
struct rcc {
volatile uint32_t CR, PLLCFGR, CFGR;
} *rcc = (struct rcc *) 0x40023800;
uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */;
if (rcc->CFGR & (1 << 2)) {
clk = hse;
} else if (rcc->CFGR & (1 << 3)) {
uint32_t vco, m, n, p;
m = (rcc->PLLCFGR & (0x3f << 0)) >> 0;
n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6;
p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2;
clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi;
vco = (uint32_t) ((uint64_t) clk * n / m);
clk = vco / p;
} else {
clk = hsi;
}
uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4;
if (hpre < 8) return clk;
uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
return ((uint32_t) clk) >> ahbptab[hpre - 8];
}
// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
// from the HSI (internal RC), and it can go above specs, the datasheets
// specify a range of frequencies and activate one of a series of dividers to
// keep the MDC clock safely below 2.5MHz. We guess a divider setting based on
// HCLK with a +5% drift. If the user uses a different clock from our
// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
static int guess_mdc_cr(void) {
uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values
uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
uint32_t hclk = get_hclk(); // Guess system HCLK
int result = -1; // Invalid CR value
if (hclk < 25000000) {
MG_ERROR(("HCLK too low"));
} else {
for (int i = 0; i < 6; i++) {
if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
result = crs[i];
break;
}
}
if (result < 0) MG_ERROR(("HCLK too high"));
}
MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
return result;
}
static bool mg_tcpip_driver_stm32_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_stm32_data *d =
(struct mg_tcpip_driver_stm32_data *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = BIT(31); // Own
s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
s_rxdesc[i][3] =
(uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
}
// Init TX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
s_txdesc[i][3] =
(uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
}
ETH->DMABMR |= BIT(0); // Software reset
while ((ETH->DMABMR & BIT(0)) != 0) (void) 0; // Wait until done
// Set MDC clock divider. If user told us the value, use it. Otherwise, guess
int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
ETH->MACMIIAR = ((uint32_t) cr & 7) << 2;
// NOTE(cpq): we do not use extended descriptor bit 7, and do not use
// hardware checksum. Therefore, descriptor size is 4, not 8
// ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT
ETH->MACFCR = BIT(7); // Disable zero quarta pause
// ETH->MACFFR = BIT(31); // Receive all
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors
ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE
ETH->MACCR = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
// MAC address filtering
ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
((uint32_t) ifp->mac[2] << 16) |
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
return true;
}
static size_t mg_tcpip_driver_stm32_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // Frame is too big
} else if ((s_txdesc[s_txno][0] & BIT(31))) {
ifp->nerr++;
MG_ERROR(("No free descriptors"));
// printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR);
len = 0; // All descriptors are busy, fail
} else {
memcpy(s_txbuf[s_txno], buf, len); // Copy data
s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
s_txdesc[s_txno][0] = BIT(20) | BIT(28) | BIT(29); // Chain,FS,LS
s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
DSB(); // ensure descriptors have been written
ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS
ETH->DMATPDR = 0; // and resume
return len;
}
static bool mg_tcpip_driver_stm32_up(struct mg_tcpip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
bool up = bsr & BIT(2) ? 1 : 0;
if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(11); // 100M, Full-duplex
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
if ((scsr & BIT(4)) == 0) maccr &= ~BIT(11); // Half-duplex
ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
maccr & BIT(11) ? "full" : "half"));
}
return up;
}
void ETH_IRQHandler(void);
void ETH_IRQHandler(void) {
if (ETH->DMASR & BIT(6)) { // Frame received, loop
ETH->DMASR = BIT(16) | BIT(6); // Clear flag
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
!(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
// printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
// ETH->DMASR);
mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
}
s_rxdesc[s_rxno][0] = BIT(31);
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
}
ETH->DMASR = BIT(7); // Clear possible RBUS while processing
ETH->DMARPDR = 0; // and resume RX
}
struct mg_tcpip_driver mg_tcpip_driver_stm32 = {mg_tcpip_driver_stm32_init,
mg_tcpip_driver_stm32_tx, NULL,
mg_tcpip_driver_stm32_up};
#endif
+16
View File
@@ -0,0 +1,16 @@
#pragma once
struct mg_tcpip_driver_stm32_data {
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
// HCLK range DIVIDER mdc_cr VALUE
// -------------------------------------
// -1 <-- tell driver to guess the value
// 60-100 MHz HCLK/42 0
// 100-150 MHz HCLK/62 1
// 20-35 MHz HCLK/16 2
// 35-60 MHz HCLK/26 3
// 150-216 MHz HCLK/102 4 <-- value for Nucleo-F* on max speed
// 216-310 MHz HCLK/124 5
// 110, 111 Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
};
+275
View File
@@ -0,0 +1,275 @@
#include "tcpip.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \
MG_ENABLE_DRIVER_STM32H
struct stm32h_eth {
volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R,
RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR,
RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR,
MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR,
MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10,
MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR,
RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR,
MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR,
MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR,
RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR,
RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR,
MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R,
RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R,
RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R,
MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR,
MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR,
RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28,
MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR,
RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR,
MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR,
RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR,
MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR,
RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR,
DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR,
DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER,
DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR,
RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2],
DMACMFCR;
};
#undef ETH
#define ETH \
((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL))
#undef BIT
#define BIT(x) ((uint32_t) 1 << (x))
#define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words)
static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
static struct mg_tcpip_if *s_ifp; // MIP interface
enum {
PHY_ADDR = 0,
PHY_BCR = 0,
PHY_BSR = 1,
PHY_CSCR = 31
}; // PHY constants
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
ETH->MACMDIOAR &= (0xF << 8);
ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2;
ETH->MACMDIOAR |= BIT(0);
while (ETH->MACMDIOAR & BIT(0)) (void) 0;
return ETH->MACMDIODR;
}
static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
ETH->MACMDIODR = val;
ETH->MACMDIOAR &= (0xF << 8);
ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2;
ETH->MACMDIOAR |= BIT(0);
while (ETH->MACMDIOAR & BIT(0)) (void) 0;
}
static uint32_t get_hclk(void) {
struct rcc {
volatile uint32_t CR, HSICFGR, CRRCR, CSICFGR, CFGR, RESERVED1, D1CFGR,
D2CFGR, D3CFGR, RESERVED2, PLLCKSELR, PLLCFGR, PLL1DIVR, PLL1FRACR,
PLL2DIVR, PLL2FRACR, PLL3DIVR, PLL3FRACR, RESERVED3, D1CCIPR, D2CCIP1R,
D2CCIP2R, D3CCIPR, RESERVED4, CIER, CIFR, CICR, RESERVED5, BDCR, CSR,
RESERVED6, AHB3RSTR, AHB1RSTR, AHB2RSTR, AHB4RSTR, APB3RSTR, APB1LRSTR,
APB1HRSTR, APB2RSTR, APB4RSTR, GCR, RESERVED8, D3AMR, RESERVED11[9],
RSR, AHB3ENR, AHB1ENR, AHB2ENR, AHB4ENR, APB3ENR, APB1LENR, APB1HENR,
APB2ENR, APB4ENR, RESERVED12, AHB3LPENR, AHB1LPENR, AHB2LPENR,
AHB4LPENR, APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR,
RESERVED13[4];
} *rcc = ((struct rcc *) (0x40000000 + 0x18020000 + 0x4400));
uint32_t clk = 0, hsi = 64000000 /* 64 MHz */, hse = 8000000 /* 8MHz */,
csi = 4000000 /* 4MHz */;
unsigned int sel = (rcc->CFGR & (7 << 3)) >> 3;
if (sel == 1) {
clk = csi;
} else if (sel == 2) {
clk = hse;
} else if (sel == 3) {
uint32_t vco, m, n, p;
unsigned int src = (rcc->PLLCKSELR & (3 << 0)) >> 0;
m = ((rcc->PLLCKSELR & (0x3F << 4)) >> 4);
n = ((rcc->PLL1DIVR & (0x1FF << 0)) >> 0) + 1 +
((rcc->PLLCFGR & BIT(0)) ? 1 : 0); // round-up in fractional mode
p = ((rcc->PLL1DIVR & (0x7F << 9)) >> 9) + 1;
if (src == 1) {
clk = csi;
} else if (src == 2) {
clk = hse;
} else {
clk = hsi;
clk >>= ((rcc->CR & 3) >> 3);
}
vco = (uint32_t) ((uint64_t) clk * n / m);
clk = vco / p;
} else {
clk = hsi;
clk >>= ((rcc->CR & 3) >> 3);
}
const uint8_t cptab[12] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
uint32_t d1cpre = (rcc->D1CFGR & (0x0F << 8)) >> 8;
if (d1cpre >= 8) clk >>= cptab[d1cpre - 8];
MG_DEBUG(("D1 CLK: %u", clk));
uint32_t hpre = (rcc->D1CFGR & (0x0F << 0)) >> 0;
if (hpre < 8) return clk;
return ((uint32_t) clk) >> cptab[hpre - 8];
}
// Guess CR from AHB1 clock. MDC clock is generated from the ETH peripheral
// clock (AHB1); as per 802.3, it must not exceed 2. As the AHB clock can
// be derived from HSI or CSI (internal RC) clocks, and those can go above
// specs, the datasheets specify a range of frequencies and activate one of a
// series of dividers to keep the MDC clock safely below 2.5MHz. We guess a
// divider setting based on HCLK with some drift. If the user uses a different
// clock from our defaults, needs to set the macros on top. Valid for
// STM32H74xxx/75xxx (58.11.4)(4.5% worst case drift)(CSI clock has a 7.5 %
// worst case drift @ max temp)
static int guess_mdc_cr(void) {
const uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMDIOAR::CR values
const uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
uint32_t hclk = get_hclk(); // Guess system HCLK
int result = -1; // Invalid CR value
for (int i = 0; i < 6; i++) {
if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
result = crs[i];
break;
}
}
if (result < 0) MG_ERROR(("HCLK too high"));
MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
return result;
}
static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_stm32h_data *d =
(struct mg_tcpip_driver_stm32h_data *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
s_rxdesc[i][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
}
// Init TX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
}
ETH->DMAMR |= BIT(0); // Software reset
while ((ETH->DMAMR & BIT(0)) != 0) (void) 0; // Wait until done
// Set MDC clock divider. If user told us the value, use it. Otherwise, guess
int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8;
// NOTE(scaprile): We do not use timing facilities so the DMA engine does not
// re-write buffer address
ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value)
ETH->DMASBMR |= BIT(12); // AAL NOTE(scaprile): is this actually needed
ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
ETH->MACTFCR = BIT(7); // Disable zero-quanta pause
// ETH->MACPFR = BIT(31); // Receive all
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
ETH->DMACRDLAR =
(uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address
ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length
ETH->DMACRDTPR =
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT -
1]; // last valid descriptor address
ETH->DMACTDLAR =
(uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address
ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length
ETH->DMACTDTPR =
(uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value)
ETH->DMACIER = BIT(6) | BIT(15); // RIE, NIE
ETH->MACCR = BIT(0) | BIT(1) | BIT(13) | BIT(14) |
BIT(15); // RE, TE, Duplex, Fast, Reserved
ETH->MTLTQOMR |= BIT(1); // TSF
ETH->MTLRQOMR |= BIT(5); // RSF
ETH->DMACTCR |= BIT(0); // ST
ETH->DMACRCR |= BIT(0); // SR
// MAC address filtering
ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
((uint32_t) ifp->mac[2] << 16) |
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
return true;
}
static uint32_t s_txno;
static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // Frame is too big
} else if ((s_txdesc[s_txno][3] & BIT(31))) {
MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno,
s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR));
for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3]));
len = 0; // All descriptors are busy, fail
} else {
memcpy(s_txbuf[s_txno], buf, len); // Copy data
s_txdesc[s_txno][2] = (uint32_t) len; // Set data len
s_txdesc[s_txno][3] = BIT(28) | BIT(29); // FD, LD
s_txdesc[s_txno][3] |= BIT(31); // Set OWN bit - let DMA take over
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
ETH->DMACSR |= BIT(2) | BIT(1); // Clear any prior TBU, TPS
ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume
return len;
(void) ifp;
}
static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
bool up = bsr & BIT(2) ? 1 : 0;
if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
if ((scsr & BIT(4)) == 0) maccr &= ~BIT(13); // Half-duplex
ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
maccr & BIT(13) ? "full" : "half"));
}
return up;
}
void ETH_IRQHandler(void);
static uint32_t s_rxno;
void ETH_IRQHandler(void) {
if (ETH->DMACSR & BIT(6)) { // Frame received, loop
ETH->DMACSR = BIT(15) | BIT(6); // Clear flag
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
if (s_rxdesc[s_rxno][3] & BIT(31)) break; // exit when done
if (((s_rxdesc[s_rxno][3] & (BIT(28) | BIT(29))) ==
(BIT(28) | BIT(29))) &&
!(s_rxdesc[s_rxno][3] & BIT(15))) { // skip partial/errored frames
uint32_t len = s_rxdesc[s_rxno][3] & (BIT(15) - 1);
// MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3],
// ETH->DMACSR));
mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
}
s_rxdesc[s_rxno][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
}
ETH->DMACSR = BIT(7) | BIT(8); // Clear possible RBU RPS while processing
ETH->DMACRDTPR =
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
}
struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
mg_tcpip_driver_stm32h_init, mg_tcpip_driver_stm32h_tx, NULL,
mg_tcpip_driver_stm32h_up};
#endif
+16
View File
@@ -0,0 +1,16 @@
#pragma once
struct mg_tcpip_driver_stm32h_data {
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
// HCLK range DIVIDER mdc_cr VALUE
// -------------------------------------
// -1 <-- tell driver to guess the value
// 60-100 MHz HCLK/42 0
// 100-150 MHz HCLK/62 1
// 20-35 MHz HCLK/16 2
// 35-60 MHz HCLK/26 3
// 150-250 MHz HCLK/102 4 <-- value for Nucleo-H* on max speed driven by HSI
// 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on max speed driven by CSI
// 110, 111 Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
};
+249
View File
@@ -0,0 +1,249 @@
#include "tcpip.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C
struct tm4c_emac {
volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL,
EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS,
EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H,
EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H,
EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL,
EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4,
EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4],
EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR,
EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239],
EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC,
EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD,
EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL,
RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294],
EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR,
EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT,
RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA,
RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS,
EMACEPHYIM, EMACEPHYIMSC;
};
#undef EMAC
#define EMAC ((struct tm4c_emac *) (uintptr_t) 0x400EC000)
#undef BIT
#define BIT(x) ((uint32_t) 1 << (x))
#define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words)
static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
static struct mg_tcpip_if *s_ifp; // MIP interface
enum {
EPHY_ADDR = 0,
EPHYBMCR = 0,
EPHYBMSR = 1,
EPHYSTS = 16
}; // PHY constants
static inline void tm4cspin(volatile uint32_t count) {
while (count--) (void) 0;
}
static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) {
EMAC->EMACMIIADDR &= (0xf << 2);
EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
EMAC->EMACMIIADDR |= BIT(0);
while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
return EMAC->EMACMIIDATA;
}
static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
EMAC->EMACMIIDATA = val;
EMAC->EMACMIIADDR &= (0xf << 2);
EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
EMAC->EMACMIIADDR |= BIT(0);
while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
}
static uint32_t get_sysclk(void) {
struct sysctl {
volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0,
PLLFREQ1;
} *sysctl = (struct sysctl *) 0x400FE000;
uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */;
if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL
uint32_t fin, vco, mdiv, n, q, psysdiv;
uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24;
if (pllsrc == 0) {
clk = piosc;
} else if (pllsrc == 3) {
clk = mosc;
} else {
MG_ERROR(("Unsupported clock source"));
}
q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8;
n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0;
fin = clk / ((q + 1) * (n + 1));
mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >>
0; // mint + (mfrac / 1024); MFRAC not supported
psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0;
vco = (uint32_t) ((uint64_t) fin * mdiv);
return vco / (psysdiv + 1);
}
uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20;
if (oscsrc == 0) {
clk = piosc;
} else if (oscsrc == 3) {
clk = mosc;
} else {
MG_ERROR(("Unsupported clock source"));
}
uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16;
return clk / (osysdiv + 1);
}
// Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per
// 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be
// derived from the PIOSC (internal RC), and it can go above specs, the
// datasheets specify a range of frequencies and activate one of a series of
// dividers to keep the MDC clock safely below 2.5MHz. We guess a divider
// setting based on SYSCLK with a +5% drift. If the user uses a different clock
// from our defaults, needs to set the macros on top Valid for TM4C129x (20.7)
// (4.5% worst case drift)
// The PHY receives the main oscillator (MOSC) (20.3.1)
static int guess_mdc_cr(void) {
uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values
uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers
uint32_t sysclk = get_sysclk(); // Guess system SYSCLK
int result = -1; // Invalid CR value
if (sysclk < 25000000) {
MG_ERROR(("SYSCLK too low"));
} else {
for (int i = 0; i < 4; i++) {
if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
result = crs[i];
break;
}
}
if (result < 0) MG_ERROR(("SYSCLK too high"));
}
MG_DEBUG(("SYSCLK: %u, CR: %d", sysclk, result));
return result;
}
static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_tm4c_data *d =
(struct mg_tcpip_driver_tm4c_data *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = BIT(31); // Own
s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
s_rxdesc[i][3] =
(uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
// MG_DEBUG(("%d %p", i, s_rxdesc[i]));
}
// Init TX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
s_txdesc[i][3] =
(uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
}
EMAC->EMACDMABUSMOD |= BIT(0); // Software reset
while ((EMAC->EMACDMABUSMOD & BIT(0)) != 0) tm4cspin(1); // Wait until done
// Set MDC clock divider. If user told us the value, use it. Otherwise, guess
int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
EMAC->EMACMIIADDR = ((uint32_t) cr & 0xf) << 2;
// NOTE(cpq): we do not use extended descriptor bit 7, and do not use
// hardware checksum. Therefore, descriptor size is 4, not 8
// EMAC->EMACDMABUSMOD = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
EMAC->EMACIM = BIT(3) | BIT(9); // Mask timestamp & PMT IT
EMAC->EMACFLOWCTL = BIT(7); // Disable zero-quanta pause
// EMAC->EMACFRAMEFLTR = BIT(31); // Receive all
// EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode
emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(15)); // Reset internal PHY (EPHY)
emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(12)); // Set autonegotiation
EMAC->EMACRXDLADDR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
EMAC->EMACTXDLADDR = (uint32_t) (uintptr_t) s_txdesc; // TX descriptors
EMAC->EMACDMAIM = BIT(6) | BIT(16); // RIE, NIE
EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
EMAC->EMACDMAOPMODE =
BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) |
((uint32_t) ifp->mac[2] << 16) |
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
// NOTE(scaprile) There are 3 additional slots for filtering, disabled by
// default. This also applies to the STM32 driver (at least for F7)
return true;
}
static uint32_t s_txno;
static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // fail
} else if ((s_txdesc[s_txno][0] & BIT(31))) {
MG_ERROR(("No descriptors available"));
// printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long)
// EMAC->EMACDMARIS);
len = 0; // fail
} else {
memcpy(s_txbuf[s_txno], buf, len); // Copy data
s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
s_txdesc[s_txno][0] =
BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS,IC
s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF
EMAC->EMACTXPOLLD = 0; // and resume
return len;
(void) ifp;
}
static bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if *ifp) {
uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
bool up = (bmsr & BIT(2)) ? 1 : 0;
if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS);
uint32_t emaccfg = EMAC->EMACCFG | BIT(14) | BIT(11); // 100M, Full-duplex
if (sts & BIT(1)) emaccfg &= ~BIT(14); // 10M
if ((sts & BIT(2)) == 0) emaccfg &= ~BIT(11); // Half-duplex
EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register
MG_DEBUG(("Link is %uM %s-duplex", emaccfg & BIT(14) ? 100 : 10,
emaccfg & BIT(11) ? "full" : "half"));
}
return up;
}
void EMAC0_IRQHandler(void);
static uint32_t s_rxno;
void EMAC0_IRQHandler(void) {
if (EMAC->EMACDMARIS & BIT(6)) { // Frame received, loop
EMAC->EMACDMARIS = BIT(16) | BIT(6); // Clear flag
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
!(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
// printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
// EMAC->EMACDMARIS);
mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
}
s_rxdesc[s_rxno][0] = BIT(31);
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
}
EMAC->EMACDMARIS = BIT(7); // Clear possible RU while processing
EMAC->EMACRXPOLLD = 0; // and resume RX
}
struct mg_tcpip_driver mg_tcpip_driver_tm4c = {mg_tcpip_driver_tm4c_init,
mg_tcpip_driver_tm4c_tx, NULL,
mg_tcpip_driver_tm4c_up};
#endif
+14
View File
@@ -0,0 +1,14 @@
#pragma once
struct mg_tcpip_driver_tm4c_data {
// MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz
// SYSCLK range DIVIDER mdc_cr VALUE
// -------------------------------------
// -1 <-- tell driver to guess the value
// 60-100 MHz SYSCLK/42 0
// 100-150 MHz SYSCLK/62 1 <-- value for EK-TM4C129* on max speed
// 20-35 MHz SYSCLK/16 2
// 35-60 MHz SYSCLK/26 3
// 0x4-0xF Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3
};
+89
View File
@@ -0,0 +1,89 @@
#include "tcpip.h"
#if MG_ENABLE_TCPIP
enum { W5500_CR = 0, W5500_S0 = 1, W5500_TX0 = 2, W5500_RX0 = 3 };
static void w5500_txn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, bool wr,
void *buf, size_t len) {
uint8_t *p = (uint8_t *) buf;
uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255),
(uint8_t) ((block << 3) | (wr ? 4 : 0))};
s->begin(s->spi);
for (size_t i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]);
for (size_t i = 0; i < len; i++) {
uint8_t r = s->txn(s->spi, p[i]);
if (!wr) p[i] = r;
}
s->end(s->spi);
}
// clang-format off
static void w5500_wn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, true, buf, len); }
static void w5500_w1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint8_t val) { w5500_wn(s, block, addr, &val, 1); }
static void w5500_w2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5500_wn(s, block, addr, buf, sizeof(buf)); }
static void w5500_rn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, false, buf, len); }
static uint8_t w5500_r1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t r = 0; w5500_rn(s, block, addr, &r, 1); return r; }
static uint16_t w5500_r2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); }
// clang-format on
static size_t w5500_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len
while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable
// printf("RSR: %d\n", (int) n);
if (n > 0) {
uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer
n = w5500_r2(s, W5500_RX0, ptr); // Read frame length
if (n <= len + 2 && n > 1) {
r = (uint16_t) (n - 2);
w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r);
}
w5500_w2(s, W5500_S0, 0x28, (uint16_t) (ptr + n)); // Advance read pointer
w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV
// printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r);
}
return r;
}
static size_t w5500_tx(const void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
uint16_t n = 0, len = (uint16_t) buflen;
while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer
w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data
w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer
w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND
for (int i = 0; i < 40; i++) {
uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR
if (ir == 0) continue;
// printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr);
w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it!
if (ir & 8) len = 0; // Timeout. Report error
if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout
}
return len;
}
static bool w5500_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
s->end(s->spi);
w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80
w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset
w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set
// w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC
w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size
w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size
w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW
w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN
return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW
}
static bool w5500_up(struct mg_tcpip_if *ifp) {
struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data;
uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e);
return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up)
}
struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up};
#endif