mongoose/mip/driver_stm32.c
2022-09-02 15:03:46 -03:00

220 lines
8.5 KiB
C

#include "mip.h"
#if MG_ENABLE_MIP && defined(__arm__)
// define to your own clock if using external clocking
#if !defined(MG_STM32_CLK_HSE)
#define MG_STM32_CLK_HSE 8000000UL
#endif
// define to your chip internal clock if different
#if !defined(MG_STM32_CLK_HSI)
#define MG_STM32_CLK_HSI 16000000UL
#endif
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;
};
#define ETH ((struct stm32_eth *) (uintptr_t) 0x40028000)
#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 void (*s_rx)(void *, size_t, void *); // Recv callback
static void *s_rxdata; // Recv callback data
enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1 }; // PHY constants
static inline void spin(volatile uint32_t count) {
while (count--) asm("nop");
}
static uint32_t hclk_get(void);
static uint8_t cr_guess(uint32_t hclk);
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)) spin(1);
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)) spin(1);
}
static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) {
// 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) spin(1); // Wait until done
// 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->MACMIIAR = cr_guess(hclk_get()) << 2; // MDC clock
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
// TODO(cpq): setup MAC filtering
(void) userdata, (void) mac;
return true;
}
static void mip_driver_stm32_setrx(void (*rx)(void *, size_t, void *),
void *rxdata) {
s_rx = rx;
s_rxdata = rxdata;
}
static uint32_t s_txno;
static size_t mip_driver_stm32_tx(const void *buf, size_t len, void *userdata) {
if (len > sizeof(s_txbuf[s_txno])) {
printf("%s: frame too big, %ld\n", __func__, (long) len);
len = 0; // Frame is too big
} else if ((s_txdesc[s_txno][0] & BIT(31))) {
printf("%s: no free descr\n", __func__);
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) | BIT(30); // 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;
}
uint32_t sr = ETH->DMASR;
if (sr & BIT(2)) ETH->DMASR = BIT(2), ETH->DMATPDR = 0; // Resume
if (sr & BIT(5)) ETH->DMASR = BIT(5), ETH->DMATPDR = 0; // if busy
if (len == 0) printf("E: D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) sr);
return len;
(void) userdata;
}
static bool mip_driver_stm32_up(void *userdata) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
(void) userdata;
return bsr & BIT(2) ? 1 : 0;
}
void ETH_IRQHandler(void);
void ETH_IRQHandler(void) {
volatile uint32_t sr = ETH->DMASR;
if (sr & BIT(6)) { // Frame received, loop
for (uint32_t i = 0; i < ETH_DESC_CNT; i++) {
if (s_rxdesc[i][0] & BIT(31)) continue;
uint32_t len = ((s_rxdesc[i][0] >> 16) & (BIT(14) - 1));
// printf("%lx %lu %lx %lx\n", i, len, s_rxdesc[i][0], sr);
if (s_rx != NULL) s_rx(s_rxbuf[i], len > 4 ? len - 4 : len, s_rxdata);
s_rxdesc[i][0] = BIT(31);
}
}
if (sr & BIT(7)) ETH->DMARPDR = 0; // Resume RX
ETH->DMASR = sr & ~(BIT(2) | BIT(7)); // Clear status
}
struct mip_driver mip_driver_stm32 = {.init = mip_driver_stm32_init,
.tx = mip_driver_stm32_tx,
.setrx = mip_driver_stm32_setrx,
.up = mip_driver_stm32_up};
/* Calculate HCLK from clock settings,
valid for STM32F74xxx/75xxx (5.3) and STM32F42xxx/43xxx (6.3) */
static const uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
struct rcc {
volatile uint32_t CR, PLLCFGR, CFGR;
};
#define RCC ((struct rcc *) 0x40023800)
static uint32_t hclk_get(void) {
uint32_t clk = 0;
if (RCC->CFGR & (1 << 2)) {
clk = MG_STM32_CLK_HSE;
} else if (RCC->CFGR & (1 << 3)) {
uint32_t vco, m, n, p;
m = (RCC->PLLCFGR & (0x3FUL << 0)) >> 0;
n = (RCC->PLLCFGR & (0x1FFUL << 6)) >> 6;
p = (((RCC->PLLCFGR & (0x03UL << 16)) >> 16) + 1) * 2;
if (RCC->PLLCFGR & (1UL << 22))
clk = MG_STM32_CLK_HSE;
else
clk = MG_STM32_CLK_HSI;
vco = (uint32_t)((uint64_t)(((uint32_t) clk * (uint32_t) n)) /
((uint32_t) m));
clk = vco / p;
} else {
clk = MG_STM32_CLK_HSI;
}
int hpre = (RCC->CFGR & (0x0F << 4)) >> 4;
if (hpre < 8) return clk;
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) */
#define CRDTAB_LEN 6
static const uint8_t crdtab[CRDTAB_LEN][2] = {
// [{setting, div ratio},...]
{2, 16}, {3, 26}, {0, 42}, {1, 62}, {4, 102}, {5, 124},
};
static uint8_t cr_guess(uint32_t hclk) {
MG_DEBUG(("HCLK: %u", hclk));
if (hclk < 25000000) {
MG_ERROR(("HCLK too low"));
return CRDTAB_LEN;
}
for (int i = 0; i < CRDTAB_LEN; i++)
if (hclk / crdtab[i][1] <= 2375000UL) return crdtab[i][0]; // 2.5MHz - 5%
MG_ERROR(("HCLK too high"));
return CRDTAB_LEN;
}
#endif // MG_ENABLE_MIP