mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-17 10:49:47 +00:00
11 files changed
+765
-85
No files matched your search
+7
-7
@@ -38,14 +38,14 @@ struct enet_desc {
|
||||
};
|
||||
|
||||
// TODO(): handle these in a portable compiler-independent CMSIS-friendly way
|
||||
#define MG_64BIT_ALIGNED __attribute__((aligned((64U))))
|
||||
#define MG_64BYTE_ALIGNED __attribute__((aligned((64U))))
|
||||
|
||||
// Descriptors: in non-cached area (TODO(scaprile)), 64-bit aligned
|
||||
// Buffers: 64-bit aligned
|
||||
static volatile struct enet_desc s_rxdesc[ETH_DESC_CNT] MG_64BIT_ALIGNED;
|
||||
static volatile struct enet_desc s_txdesc[ETH_DESC_CNT] MG_64BIT_ALIGNED;
|
||||
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BIT_ALIGNED;
|
||||
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BIT_ALIGNED;
|
||||
// Descriptors: in non-cached area (TODO(scaprile)), (37.5.1.22.2 37.5.1.23.2)
|
||||
// Buffers: 64-byte aligned (37.3.14)
|
||||
static volatile struct enet_desc s_rxdesc[ETH_DESC_CNT] MG_64BYTE_ALIGNED;
|
||||
static volatile struct enet_desc s_txdesc[ETH_DESC_CNT] MG_64BYTE_ALIGNED;
|
||||
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BYTE_ALIGNED;
|
||||
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BYTE_ALIGNED;
|
||||
static struct mg_tcpip_if *s_ifp; // MIP interface
|
||||
|
||||
enum {
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
#include "net_builtin.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RA) && MG_ENABLE_DRIVER_RA
|
||||
struct ra_etherc {
|
||||
volatile uint32_t ECMR, RESERVED, RFLR, RESERVED1, ECSR, RESERVED2, ECSIPR,
|
||||
RESERVED3, PIR, RESERVED4, PSR, RESERVED5[5], RDMLR, RESERVED6[3], IPGR,
|
||||
APR, MPR, RESERVED7, RFCF, TPAUSER, TPAUSECR, BCFRR, RESERVED8[20], MAHR,
|
||||
RESERVED9, MALR, RESERVED10, TROCR, CDCR, LCCR, CNDCR, RESERVED11, CEFCR,
|
||||
FRECR, TSFRCR, TLFRCR, RFCR, MAFCR;
|
||||
};
|
||||
|
||||
struct ra_edmac {
|
||||
volatile uint32_t EDMR, RESERVED, EDTRR, RESERVED1, EDRRR, RESERVED2, TDLAR,
|
||||
RESERVED3, RDLAR, RESERVED4, EESR, RESERVED5, EESIPR, RESERVED6, TRSCER,
|
||||
RESERVED7, RMFCR, RESERVED8, TFTR, RESERVED9, FDR, RESERVED10, RMCR,
|
||||
RESERVED11[2], TFUCR, RFOCR, IOSR, FCFTR, RESERVED12, RPADIR, TRIMD,
|
||||
RESERVED13[18], RBWAR, RDFAR, RESERVED14, TBRAR, TDFAR;
|
||||
};
|
||||
|
||||
#undef ETHERC
|
||||
#define ETHERC ((struct ra_etherc *) (uintptr_t) 0x40114100U)
|
||||
#undef EDMAC
|
||||
#define EDMAC ((struct ra_edmac *) (uintptr_t) 0x40114000U)
|
||||
#undef RASYSC
|
||||
#define RASYSC ((uint32_t *) (uintptr_t) 0x4001E000U)
|
||||
#undef ICU_IELSR
|
||||
#define ICU_IELSR ((uint32_t *) (uintptr_t) 0x40006300U)
|
||||
|
||||
#define ETH_PKT_SIZE 1536 // Max frame size, multiple of 32
|
||||
#define ETH_DESC_CNT 4 // Descriptors count
|
||||
|
||||
// TODO(): handle these in a portable compiler-independent CMSIS-friendly way
|
||||
#define MG_16BYTE_ALIGNED __attribute__((aligned((16U))))
|
||||
#define MG_32BYTE_ALIGNED __attribute__((aligned((32U))))
|
||||
|
||||
// Descriptors: 16-byte aligned
|
||||
// Buffers: 32-byte aligned (27.3.1)
|
||||
static volatile uint32_t s_rxdesc[ETH_DESC_CNT][4] MG_16BYTE_ALIGNED;
|
||||
static volatile uint32_t s_txdesc[ETH_DESC_CNT][4] MG_16BYTE_ALIGNED;
|
||||
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_32BYTE_ALIGNED;
|
||||
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_32BYTE_ALIGNED;
|
||||
static struct mg_tcpip_if *s_ifp; // MIP interface
|
||||
|
||||
enum {
|
||||
MG_PHYREG_BCR = 0,
|
||||
MG_PHYREG_BSR = 1,
|
||||
MG_PHYREG_ID1 = 2,
|
||||
MG_PHYREG_ID2 = 3
|
||||
};
|
||||
|
||||
// fastest is 3 cycles (SUB + BNE) on a 3-stage pipeline or equivalent
|
||||
static inline void raspin(volatile uint32_t count) {
|
||||
while (count--) (void) 0;
|
||||
}
|
||||
// count to get the 200ns SMC semi-cycle period (2.5MHz) calling raspin():
|
||||
// SYS_FREQUENCY * 200ns / 3 = SYS_FREQUENCY / 15000000
|
||||
static uint32_t s_smispin;
|
||||
|
||||
// Bit-banged SMI
|
||||
static void smi_preamble(void) {
|
||||
unsigned int i = 32;
|
||||
uint32_t pir = MG_BIT(1) | MG_BIT(2); // write, mdio = 1, mdc = 0
|
||||
ETHERC->PIR = pir;
|
||||
while (i--) {
|
||||
pir &= ~MG_BIT(0); // mdc = 0
|
||||
ETHERC->PIR = pir;
|
||||
raspin(s_smispin);
|
||||
pir |= MG_BIT(0); // mdc = 1
|
||||
ETHERC->PIR = pir;
|
||||
raspin(s_smispin);
|
||||
}
|
||||
}
|
||||
static void smi_wr(uint16_t header, uint16_t data) {
|
||||
uint32_t word = (header << 16) | data;
|
||||
smi_preamble();
|
||||
unsigned int i = 32;
|
||||
while (i--) {
|
||||
uint32_t pir = MG_BIT(1) |
|
||||
(word & 0x80000000 ? MG_BIT(2) : 0); // write, mdc = 0, data
|
||||
ETHERC->PIR = pir;
|
||||
raspin(s_smispin);
|
||||
pir |= MG_BIT(0); // mdc = 1
|
||||
ETHERC->PIR = pir;
|
||||
raspin(s_smispin);
|
||||
word <<= 1;
|
||||
}
|
||||
}
|
||||
static uint16_t smi_rd(uint16_t header) {
|
||||
smi_preamble();
|
||||
unsigned int i = 16; // 2 LSb as turnaround
|
||||
uint32_t pir;
|
||||
while (i--) {
|
||||
pir = (i > 1 ? MG_BIT(1) : 0) |
|
||||
(header & 0x8000
|
||||
? MG_BIT(2)
|
||||
: 0); // mdc = 0, header, set read direction at turnaround
|
||||
ETHERC->PIR = pir;
|
||||
raspin(s_smispin);
|
||||
pir |= MG_BIT(0); // mdc = 1
|
||||
ETHERC->PIR = pir;
|
||||
raspin(s_smispin);
|
||||
header <<= 1;
|
||||
}
|
||||
i = 16;
|
||||
uint16_t data = 0;
|
||||
while (i--) {
|
||||
data <<= 1;
|
||||
pir = 0; // read, mdc = 0
|
||||
ETHERC->PIR = pir;
|
||||
raspin(s_smispin / 2); // 1/4 clock period, 300ns max access time
|
||||
data |= ETHERC->PIR & MG_BIT(3) ? 1 : 0; // read mdio
|
||||
raspin(s_smispin / 2); // 1/4 clock period
|
||||
pir |= MG_BIT(0); // mdc = 1
|
||||
ETHERC->PIR = pir;
|
||||
raspin(s_smispin);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static uint16_t raeth_phy_read(uint8_t addr, uint8_t reg) {
|
||||
return smi_rd((1 << 14) | (2 << 12) | (addr << 7) | (reg << 2) | (2 << 0));
|
||||
}
|
||||
|
||||
static void raeth_phy_write(uint8_t addr, uint8_t reg, uint16_t val) {
|
||||
smi_wr((1 << 14) | (1 << 12) | (addr << 7) | (reg << 2) | (2 << 0), val);
|
||||
}
|
||||
|
||||
static uint32_t raeth_phy_id(uint8_t addr) {
|
||||
uint16_t phy_id1 = raeth_phy_read(addr, MG_PHYREG_ID1);
|
||||
uint16_t phy_id2 = raeth_phy_read(addr, MG_PHYREG_ID2);
|
||||
return (uint32_t) phy_id1 << 16 | phy_id2;
|
||||
}
|
||||
|
||||
// MDC clock is generated manually; as per 802.3, it must not exceed 2.5MHz
|
||||
static bool mg_tcpip_driver_ra_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_ra_data *d =
|
||||
(struct mg_tcpip_driver_ra_data *) ifp->driver_data;
|
||||
s_ifp = ifp;
|
||||
|
||||
// Init SMI clock timing. If user told us the clock value, use it.
|
||||
// TODO(): Otherwise, guess
|
||||
s_smispin = d->clock / 15000000;
|
||||
|
||||
// Init RX descriptors
|
||||
for (int i = 0; i < ETH_DESC_CNT; i++) {
|
||||
s_rxdesc[i][0] = MG_BIT(31); // RACT
|
||||
s_rxdesc[i][1] = ETH_PKT_SIZE << 16; // RBL
|
||||
s_rxdesc[i][2] = (uint32_t) s_rxbuf[i]; // Point to data buffer
|
||||
}
|
||||
s_rxdesc[ETH_DESC_CNT - 1][0] |= MG_BIT(30); // Wrap last descriptor
|
||||
|
||||
// Init TX descriptors
|
||||
for (int i = 0; i < ETH_DESC_CNT; i++) {
|
||||
// TACT = 0
|
||||
s_txdesc[i][2] = (uint32_t) s_txbuf[i];
|
||||
}
|
||||
s_txdesc[ETH_DESC_CNT - 1][0] |= MG_BIT(30); // Wrap last descriptor
|
||||
|
||||
EDMAC->EDMR = MG_BIT(0); // Software reset, wait 64 PCLKA clocks (27.2.1)
|
||||
uint32_t sckdivcr = RASYSC[8]; // get divisors from SCKDIVCR (8.2.2)
|
||||
uint32_t ick = 1 << ((sckdivcr >> 24) & 7); // sys_clock div
|
||||
uint32_t pcka = 1 << ((sckdivcr >> 12) & 7); // pclka div
|
||||
raspin((64U * pcka) / (3U * ick));
|
||||
EDMAC->EDMR = MG_BIT(6); // Initialize, little-endian (27.2.1)
|
||||
|
||||
MG_DEBUG(("PHY addr: %d, smispin: %d", d->phy_addr, s_smispin));
|
||||
raeth_phy_write(d->phy_addr, MG_PHYREG_BCR, MG_BIT(15)); // Reset PHY
|
||||
raeth_phy_write(d->phy_addr, MG_PHYREG_BCR,
|
||||
MG_BIT(12)); // Set autonegotiation
|
||||
|
||||
// PHY: Enable ref clock (preserve defaults)
|
||||
uint32_t id = raeth_phy_id(d->phy_addr);
|
||||
MG_INFO(("PHY ID: %#04x %#04x", (uint16_t) (id >> 16), (uint16_t) id));
|
||||
// 2000 a140 - TI DP83825I
|
||||
// 0007 c0fx - LAN8720
|
||||
// 0022 156x - KSZ8081RNB/KSZ8091RNB
|
||||
|
||||
if ((id & 0xffff0000) == 0x220000) { // KSZ8091RNB, like EK-RA6Mx boards
|
||||
// 25 MHz xtal at XI/XO (default)
|
||||
raeth_phy_write(d->phy_addr, 31, MG_BIT(15) | MG_BIT(8)); // PC2R
|
||||
} else if ((id & 0xffff0000) == 0x20000000) { // DP83825I, like ???
|
||||
// 50 MHz external at XI ???
|
||||
raeth_phy_write(d->phy_addr, 23, 0x81); // 50MHz clock input
|
||||
raeth_phy_write(d->phy_addr, 24, 0x280); // LED status, active high
|
||||
} else { // Default to LAN8720
|
||||
MG_INFO(("Defaulting to LAN8720 PHY...")); // TODO()
|
||||
}
|
||||
|
||||
// Select RMII mode,
|
||||
ETHERC->ECMR = MG_BIT(2) | MG_BIT(1); // 100M, Full-duplex, CRC
|
||||
// ETHERC->ECMR |= MG_BIT(0); // Receive all
|
||||
ETHERC->RFLR = 1518; // Set max rx length
|
||||
|
||||
EDMAC->RDLAR = (uint32_t) (uintptr_t) s_rxdesc;
|
||||
EDMAC->TDLAR = (uint32_t) (uintptr_t) s_txdesc;
|
||||
// MAC address filtering (bytes in reversed order)
|
||||
ETHERC->MAHR = (uint32_t) (ifp->mac[0] << 24U) |
|
||||
((uint32_t) ifp->mac[1] << 16U) |
|
||||
((uint32_t) ifp->mac[2] << 8U) | ifp->mac[3];
|
||||
ETHERC->MALR = ((uint32_t) ifp->mac[4] << 8U) | ifp->mac[5];
|
||||
|
||||
EDMAC->TFTR = 0; // Store and forward (27.2.10)
|
||||
EDMAC->FDR = 0x070f; // (27.2.11)
|
||||
EDMAC->RMCR = MG_BIT(0); // (27.2.12)
|
||||
ETHERC->ECMR |= MG_BIT(6) | MG_BIT(5); // TE RE
|
||||
EDMAC->EESIPR = MG_BIT(18); // Enable Rx IRQ
|
||||
EDMAC->EDRRR = MG_BIT(0); // Receive Descriptors have changed
|
||||
EDMAC->EDTRR = MG_BIT(0); // Transmit Descriptors have changed
|
||||
return true;
|
||||
}
|
||||
|
||||
// Transmit frame
|
||||
static size_t mg_tcpip_driver_ra_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
static int s_txno; // Current descriptor index
|
||||
if (len > sizeof(s_txbuf[ETH_DESC_CNT])) {
|
||||
ifp->nerr++;
|
||||
MG_ERROR(("Frame too big, %ld", (long) len));
|
||||
len = (size_t) -1; // fail
|
||||
} else if ((s_txdesc[s_txno][0] & MG_BIT(31))) {
|
||||
MG_ERROR(("No descriptors available"));
|
||||
len = 0; // retry later
|
||||
} else {
|
||||
memcpy(s_txbuf[s_txno], buf, len); // Copy data
|
||||
s_txdesc[s_txno][1] = len << 16; // Set data len
|
||||
s_txdesc[s_txno][0] |= MG_BIT(31) | 3 << 28; // (27.3.1.1) mark valid
|
||||
EDMAC->EDTRR = MG_BIT(0); // Transmit request
|
||||
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_ra_up(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_ra_data *d =
|
||||
(struct mg_tcpip_driver_ra_data *) ifp->driver_data;
|
||||
uint32_t bsr = raeth_phy_read(d->phy_addr, MG_PHYREG_BSR);
|
||||
bool up = bsr & MG_BIT(2) ? 1 : 0;
|
||||
if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
|
||||
// tmp = reg with flags set to the most likely situation: 100M full-duplex
|
||||
// if(link is slow or half) set flags otherwise
|
||||
// reg = tmp
|
||||
uint32_t ecmr = ETHERC->ECMR | MG_BIT(2) | MG_BIT(1); // 100M Full-duplex
|
||||
uint32_t phy_id = raeth_phy_id(d->phy_addr);
|
||||
if ((phy_id & 0xffff0000) == 0x220000) { // KSZ8091RNB
|
||||
uint16_t pc1r = raeth_phy_read(d->phy_addr, 30); // Read PC1R
|
||||
if ((pc1r & 3) == 1) ecmr &= ~MG_BIT(2); // 10M
|
||||
if ((pc1r & MG_BIT(2)) == 0) ecmr &= ~MG_BIT(1); // Half-duplex
|
||||
} else if ((phy_id & 0xffff0000) == 0x20000000) { // DP83825I
|
||||
uint16_t physts = raeth_phy_read(d->phy_addr, 16); // Read PHYSTS
|
||||
if (physts & MG_BIT(1)) ecmr &= ~MG_BIT(2); // 10M
|
||||
if ((physts & MG_BIT(2)) == 0) ecmr &= ~MG_BIT(1); // Half-duplex
|
||||
} else { // Default to LAN8720
|
||||
uint16_t scsr = raeth_phy_read(d->phy_addr, 31); // Read CSCR
|
||||
if ((scsr & MG_BIT(3)) == 0) ecmr &= ~MG_BIT(2); // 10M
|
||||
if ((scsr & MG_BIT(4)) == 0) ecmr &= ~MG_BIT(1); // Half-duplex
|
||||
}
|
||||
ETHERC->ECMR = ecmr; // IRQ handler does not fiddle with these registers
|
||||
MG_DEBUG(("Link is %uM %s-duplex", ecmr & MG_BIT(2) ? 100 : 10,
|
||||
ecmr & MG_BIT(1) ? "full" : "half"));
|
||||
}
|
||||
return up;
|
||||
}
|
||||
|
||||
void EDMAC_IRQHandler(void);
|
||||
static uint32_t s_rxno;
|
||||
void EDMAC_IRQHandler(void) {
|
||||
struct mg_tcpip_driver_ra_data *d =
|
||||
(struct mg_tcpip_driver_ra_data *) s_ifp->driver_data;
|
||||
EDMAC->EESR = MG_BIT(18); // Ack IRQ in EDMAC 1st
|
||||
ICU_IELSR[d->irqno] &= ~MG_BIT(16); // Ack IRQ in ICU last
|
||||
// Frame received, loop
|
||||
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
|
||||
uint32_t r = s_rxdesc[s_rxno][0];
|
||||
if (r & MG_BIT(31)) break; // exit when done
|
||||
// skip partial/errored frames (27.3.1.2)
|
||||
if ((r & (MG_BIT(29) | MG_BIT(28)) && !(r & MG_BIT(27)))) {
|
||||
size_t len = s_rxdesc[s_rxno][1] & 0xffff;
|
||||
mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp); // CRC already stripped
|
||||
}
|
||||
s_rxdesc[s_rxno][0] |= MG_BIT(31);
|
||||
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
|
||||
}
|
||||
EDMAC->EDRRR = MG_BIT(0); // Receive Descriptors have changed
|
||||
// If b0 == 0, descriptors were exhausted and probably frames were dropped,
|
||||
// (27.2.9 RMFCR counts them)
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_ra = {mg_tcpip_driver_ra_init,
|
||||
mg_tcpip_driver_ra_tx, NULL,
|
||||
mg_tcpip_driver_ra_up};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
struct mg_tcpip_driver_ra_data {
|
||||
// MDC clock "divider". MDC clock is software generated,
|
||||
uint32_t clock; // core clock frequency in Hz
|
||||
uint16_t irqno; // IRQn, R_ICU->IELSR[irqno]
|
||||
uint8_t phy_addr; // PHY address
|
||||
};
|
||||
@@ -59,6 +59,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_stm32h;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_imxrt;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_same54;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_cmsis;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_ra;
|
||||
|
||||
// Drivers that require SPI, can use this SPI abstraction
|
||||
struct mg_tcpip_spi {
|
||||
|
||||
Reference in new issue
Block a user