forked from cesanta/mongoose
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdriver_stm32.c
208 lines (187 loc) · 8.5 KB
/
driver_stm32.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include "mip.h"
#if MG_ENABLE_MIP && defined(__arm__)
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 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 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;
}
int hpre = (RCC->CFGR & (0x0F << 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 mip_driver_stm32_init(uint8_t *mac, void *userdata) {
struct mip_driver_stm32 *d = (struct mip_driver_stm32 *) 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
// 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 = (cr & 3) << 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) mac[5] << 8U) | mac[4];
ETH->MACA0LR = (uint32_t) (mac[3] << 24) | ((uint32_t) mac[2] << 16) |
((uint32_t) mac[1] << 8) | mac[0];
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) {
qp_mark(QP_IRQTRIGGERED, 0);
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};
#endif