mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-01 13:10:43 +00:00
Unify naming: all network stacks are in src/net_...
This commit is contained in:
1 parent
69d2ba7316
commit
89a9a50b96
16 files changed
+1101
-1103
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
Reference in new issue
Block a user