Move stm32h driver to synopsys

This commit is contained in:
Sergio R. Caprile 2024-05-21 16:45:19 -03:00
parent 8085e21a05
commit 6942de4f57
4 changed files with 94 additions and 182 deletions

View File

@ -16511,9 +16511,11 @@ struct mg_tcpip_driver mg_tcpip_driver_stm32f = {
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \
MG_ENABLE_DRIVER_STM32H
struct stm32h_eth {
#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN)
// STM32H: vendor modded single-queue Synopsys v4.2
// MCXNx4x: dual-queue Synopsys v5.2
// RT1170 ENET_QOS: quad-queue Synopsys v5.1
struct synopsys_enet_qos {
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,
@ -16544,8 +16546,13 @@ struct stm32h_eth {
DMACMFCR;
};
#undef ETH
#define ETH \
((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL))
#if MG_ENABLE_DRIVER_STM32H
#define ETH \
((struct synopsys_enet_qos *) (uintptr_t) (0x40000000UL + 0x00020000UL + \
0x8000UL))
#elif MG_ENABLE_DRIVER_MCXN
#define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40100000UL)
#endif
#define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
@ -16573,82 +16580,6 @@ static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
while (ETH->MACMDIOAR & MG_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 & MG_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;
@ -16667,11 +16598,13 @@ static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
}
ETH->DMAMR |= MG_BIT(0); // Software reset
ETH->DMAMR |= MG_BIT(0); // Software reset
for (int i = 0; i < 4; i++)
(void) 0; // wait at least 4 clocks before reading
while ((ETH->DMAMR & MG_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;
// Set MDC clock divider. Get user value, else, assume max freq
int cr = (d == NULL || d->mdc_cr < 0) ? 7 : d->mdc_cr;
ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8;
// NOTE(scaprile): We do not use timing facilities so the DMA engine does not
@ -16695,13 +16628,23 @@ static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
ETH->DMACTDTPR =
(uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value)
#if !MG_ENABLE_DRIVER_STM32H
MG_SET_BITS(ETH->DMACTCR, 0x3F << 16, MG_BIT(16));
MG_SET_BITS(ETH->DMACRCR, 0x3F << 16, MG_BIT(16));
#endif
ETH->DMACIER = MG_BIT(6) | MG_BIT(15); // RIE, NIE
ETH->MACCR = MG_BIT(0) | MG_BIT(1) | MG_BIT(13) | MG_BIT(14) |
MG_BIT(15); // RE, TE, Duplex, Fast, Reserved
MG_BIT(15); // RE, TE, Duplex, Fast, Reserved
#if MG_ENABLE_DRIVER_STM32H
ETH->MTLTQOMR |= MG_BIT(1); // TSF
ETH->MTLRQOMR |= MG_BIT(5); // RSF
ETH->DMACTCR |= MG_BIT(0); // ST
ETH->DMACRCR |= MG_BIT(0); // SR
#else
ETH->MTLTQOMR |= (7 << 16) | MG_BIT(3) | MG_BIT(1); // 2KB Q0, TSF
ETH->MTLRQOMR |= (7 << 20) | MG_BIT(5); // 2KB Q, RSF
MG_SET_BITS(ETH->RESERVED6[3], 3, 2); // Enable RxQ0 (MAC_RXQ_CTRL0)
#endif
ETH->DMACTCR |= MG_BIT(0); // ST
ETH->DMACRCR |= MG_BIT(0); // SR
// MAC address filtering
ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
@ -16758,9 +16701,14 @@ static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) {
return up;
}
void ETH_IRQHandler(void);
static uint32_t s_rxno;
#if MG_ENABLE_DRIVER_MCXN
void ETHERNET_IRQHandler(void);
void ETHERNET_IRQHandler(void) {
#else
void ETH_IRQHandler(void);
void ETH_IRQHandler(void) {
#endif
if (ETH->DMACSR & MG_BIT(6)) { // Frame received, loop
ETH->DMACSR = MG_BIT(15) | MG_BIT(6); // Clear flag
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever

View File

@ -2848,8 +2848,14 @@ struct mg_tcpip_driver_stm32f_data {
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \
MG_ENABLE_DRIVER_STM32H
#if MG_ENABLE_TCPIP
#if !defined(MG_ENABLE_DRIVER_STM32H)
#define MG_ENABLE_DRIVER_STM32H 0
#endif
#if !defined(MG_ENABLE_DRIVER_MCXN)
#define MG_ENABLE_DRIVER_MCXN 0
#endif
#if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN
struct mg_tcpip_driver_stm32h_data {
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
@ -2862,7 +2868,8 @@ struct mg_tcpip_driver_stm32h_data {
// 35-60 MHz HCLK/26 3
// 150-250 MHz HCLK/102 4 <-- value for max speed HSI
// 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on CSI
// 110, 111 Reserved
// 300-500 MHz HCLK/204 6
// 500-800 MHz HCLK/324 7
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr; // PHY address
@ -2894,6 +2901,7 @@ struct mg_tcpip_driver_stm32h_data {
} while (0)
#endif
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C

View File

@ -1,8 +1,10 @@
#include "net_builtin.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \
MG_ENABLE_DRIVER_STM32H
struct stm32h_eth {
#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN)
// STM32H: vendor modded single-queue Synopsys v4.2
// MCXNx4x: dual-queue Synopsys v5.2
// RT1170 ENET_QOS: quad-queue Synopsys v5.1
struct synopsys_enet_qos {
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,
@ -33,8 +35,13 @@ struct stm32h_eth {
DMACMFCR;
};
#undef ETH
#define ETH \
((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL))
#if MG_ENABLE_DRIVER_STM32H
#define ETH \
((struct synopsys_enet_qos *) (uintptr_t) (0x40000000UL + 0x00020000UL + \
0x8000UL))
#elif MG_ENABLE_DRIVER_MCXN
#define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40100000UL)
#endif
#define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
@ -62,82 +69,6 @@ static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
while (ETH->MACMDIOAR & MG_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 & MG_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;
@ -156,11 +87,13 @@ static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
}
ETH->DMAMR |= MG_BIT(0); // Software reset
ETH->DMAMR |= MG_BIT(0); // Software reset
for (int i = 0; i < 4; i++)
(void) 0; // wait at least 4 clocks before reading
while ((ETH->DMAMR & MG_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;
// Set MDC clock divider. Get user value, else, assume max freq
int cr = (d == NULL || d->mdc_cr < 0) ? 7 : d->mdc_cr;
ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8;
// NOTE(scaprile): We do not use timing facilities so the DMA engine does not
@ -184,13 +117,23 @@ static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
ETH->DMACTDTPR =
(uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value)
#if !MG_ENABLE_DRIVER_STM32H
MG_SET_BITS(ETH->DMACTCR, 0x3F << 16, MG_BIT(16));
MG_SET_BITS(ETH->DMACRCR, 0x3F << 16, MG_BIT(16));
#endif
ETH->DMACIER = MG_BIT(6) | MG_BIT(15); // RIE, NIE
ETH->MACCR = MG_BIT(0) | MG_BIT(1) | MG_BIT(13) | MG_BIT(14) |
MG_BIT(15); // RE, TE, Duplex, Fast, Reserved
MG_BIT(15); // RE, TE, Duplex, Fast, Reserved
#if MG_ENABLE_DRIVER_STM32H
ETH->MTLTQOMR |= MG_BIT(1); // TSF
ETH->MTLRQOMR |= MG_BIT(5); // RSF
ETH->DMACTCR |= MG_BIT(0); // ST
ETH->DMACRCR |= MG_BIT(0); // SR
#else
ETH->MTLTQOMR |= (7 << 16) | MG_BIT(3) | MG_BIT(1); // 2KB Q0, TSF
ETH->MTLRQOMR |= (7 << 20) | MG_BIT(5); // 2KB Q, RSF
MG_SET_BITS(ETH->RESERVED6[3], 3, 2); // Enable RxQ0 (MAC_RXQ_CTRL0)
#endif
ETH->DMACTCR |= MG_BIT(0); // ST
ETH->DMACRCR |= MG_BIT(0); // SR
// MAC address filtering
ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
@ -247,9 +190,14 @@ static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) {
return up;
}
void ETH_IRQHandler(void);
static uint32_t s_rxno;
#if MG_ENABLE_DRIVER_MCXN
void ETHERNET_IRQHandler(void);
void ETHERNET_IRQHandler(void) {
#else
void ETH_IRQHandler(void);
void ETH_IRQHandler(void) {
#endif
if (ETH->DMACSR & MG_BIT(6)) { // Frame received, loop
ETH->DMACSR = MG_BIT(15) | MG_BIT(6); // Clear flag
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever

View File

@ -1,7 +1,13 @@
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \
MG_ENABLE_DRIVER_STM32H
#if MG_ENABLE_TCPIP
#if !defined(MG_ENABLE_DRIVER_STM32H)
#define MG_ENABLE_DRIVER_STM32H 0
#endif
#if !defined(MG_ENABLE_DRIVER_MCXN)
#define MG_ENABLE_DRIVER_MCXN 0
#endif
#if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN
struct mg_tcpip_driver_stm32h_data {
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
@ -14,7 +20,8 @@ struct mg_tcpip_driver_stm32h_data {
// 35-60 MHz HCLK/26 3
// 150-250 MHz HCLK/102 4 <-- value for max speed HSI
// 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on CSI
// 110, 111 Reserved
// 300-500 MHz HCLK/204 6
// 500-800 MHz HCLK/324 7
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr; // PHY address
@ -46,3 +53,4 @@ struct mg_tcpip_driver_stm32h_data {
} while (0)
#endif
#endif