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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
|
/*
* SPDX-FileCopyrightText: Copyright (c) 2021, Ha Thach (tinyusb.org)
* SPDX-License-Identifier: MIT
*
* This file is part of the TinyUSB stack.
*/
#ifndef DWC2_STM32_H_
#define DWC2_STM32_H_
#ifdef __cplusplus
extern "C" {
#endif
// EP_MAX : Max number of bi-directional endpoints including EP0
// DFIFO_DEPTH_FS/HS : DFIFO depth in 32-bit words (OTG_DFIFO_DEPTH)
#if CFG_TUSB_MCU == OPT_MCU_STM32F1
#include "stm32f1xx.h"
#define EP_MAX_FS 4
#define DFIFO_DEPTH_FS 320
#elif CFG_TUSB_MCU == OPT_MCU_STM32F2
#include "stm32f2xx.h"
#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
#define DFIFO_DEPTH_FS 320
#define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
#define DFIFO_DEPTH_HS 1024
#elif CFG_TUSB_MCU == OPT_MCU_STM32F4
#include "stm32f4xx.h"
#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
#define DFIFO_DEPTH_FS 320
#define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
#define DFIFO_DEPTH_HS 1024
#elif CFG_TUSB_MCU == OPT_MCU_STM32H7
#include "stm32h7xx.h"
#define EP_MAX_FS 9
#define DFIFO_DEPTH_FS 1024
#define EP_MAX_HS 9
#define DFIFO_DEPTH_HS 1024
// NOTE: H7 with only 1 USB port: H72x / H73x / H7Ax / H7Bx
// USB_OTG_FS_PERIPH_BASE and OTG_FS_IRQn not defined
#if (! defined USB2_OTG_FS)
#define USB_OTG_FS_PERIPH_BASE USB1_OTG_HS_PERIPH_BASE
#define OTG_FS_IRQn OTG_HS_IRQn
#endif
#elif CFG_TUSB_MCU == OPT_MCU_STM32H7RS
#include "stm32h7rsxx.h"
#define EP_MAX_FS 6
#define DFIFO_DEPTH_FS 320
#define EP_MAX_HS 9
#define DFIFO_DEPTH_HS 1024
#elif CFG_TUSB_MCU == OPT_MCU_STM32N6
#include "stm32n6xx.h"
#define EP_MAX_FS 9
#define DFIFO_DEPTH_FS 1024
#define EP_MAX_HS 9
#define DFIFO_DEPTH_HS 1024
#define USB_OTG_FS_PERIPH_BASE USB1_OTG_HS_BASE
#define OTG_FS_IRQn USB1_OTG_HS_IRQn
#define USB_OTG_HS_PERIPH_BASE USB2_OTG_HS_BASE
#define OTG_HS_IRQn USB2_OTG_HS_IRQn
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
#define EP_MAX_FS 6
#define DFIFO_DEPTH_FS 320
#define EP_MAX_HS 9
#define DFIFO_DEPTH_HS 1024
#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
#include "stm32l4xx.h"
#define EP_MAX_FS 6
#define DFIFO_DEPTH_FS 320
#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
#include "stm32u5xx.h"
// U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY
#ifdef USB_OTG_FS
#define USB_OTG_FS_PERIPH_BASE USB_OTG_FS_BASE
#define EP_MAX_FS 6
#define DFIFO_DEPTH_FS 320
#else
#define USB_OTG_HS_PERIPH_BASE USB_OTG_HS_BASE
#define EP_MAX_HS 9
#define DFIFO_DEPTH_HS 1024
#endif
#elif CFG_TUSB_MCU == OPT_MCU_STM32WBA
#if defined(STM32WBA62xx)
#include "stm32wba62xx.h"
#elif defined(STM32WBA64xx)
#include "stm32wba64xx.h"
#elif defined(STM32WBA65xx)
#include "stm32wba65xx.h"
#else
#error "The selected STM32WBA series chip does not support OTG USB HS"
#endif
#define USB_OTG_HS_PERIPH_BASE USB_OTG_HS_BASE_NS
#define OTG_HS_IRQn USB_OTG_HS_IRQn
#define EP_MAX_HS 9
#define DFIFO_DEPTH_HS 1024
#else
#error "Unsupported MCUs"
#endif
// OTG HS always has higher number of endpoints than FS
#ifdef USB_OTG_HS_PERIPH_BASE
#define DWC2_EP_MAX EP_MAX_HS
#else
#define DWC2_EP_MAX EP_MAX_FS
#endif
// On STM32 for consistency we associate
// - Port0 to OTG_FS, and Port1 to OTG_HS
static const dwc2_controller_t _dwc2_controller[] = {
#ifdef USB_OTG_FS_PERIPH_BASE
{ .reg_base = USB_OTG_FS_PERIPH_BASE, .irqnum = OTG_FS_IRQn, .ep_count = EP_MAX_FS, .otg_dfifo_depth = DFIFO_DEPTH_FS },
#endif
#ifdef USB_OTG_HS_PERIPH_BASE
{ .reg_base = USB_OTG_HS_PERIPH_BASE, .irqnum = OTG_HS_IRQn, .ep_count = EP_MAX_HS, .otg_dfifo_depth = DFIFO_DEPTH_HS },
#endif
};
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
// SystemCoreClock is already included by family header
// extern uint32_t SystemCoreClock;
// MCU specific to enable dwc2 clock/power before any access to register
TU_ATTR_ALWAYS_INLINE static inline void dwc2_clock_init(uint8_t rhport, tusb_role_t role) {
(void) rhport;
(void) role;
}
TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
(void) role;
const IRQn_Type irqn = (IRQn_Type) _dwc2_controller[rhport].irqnum;
if (enabled) {
NVIC_EnableIRQ(irqn);
} else {
NVIC_DisableIRQ(irqn);
}
}
#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true)
#define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false)
TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
// try to delay for 1 ms
uint32_t count = SystemCoreClock / 1000;
while (count--) {
__NOP();
}
}
// MCU specific PHY init, called BEFORE core reset
// - dwc2 3.30a (H5) use USB_HS_PHYC
// - dwc2 4.11a (U5) use femtoPHY
// - dwc2 x.xxx (WBA) use USB_OTG_HS
static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) {
// Enable on-chip FS PHY
dwc2->stm32_gccfg |= STM32_GCCFG_PWRDWN;
// https://community.st.com/t5/stm32cubemx-mcus/why-stm32h743-usb-fs-doesn-t-work-if-freertos-tickless-idle/m-p/349480#M18867
// H7 running on full-speed phy need to disable ULPI clock in sleep mode.
// Otherwise, USB won't work when mcu executing WFI/WFE instruction i.e tick-less RTOS.
// Note: there may be other family that is affected by this, but only H7 and F7 is tested so far
#if defined(USB_OTG_FS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB2OTGFSULPILPEN)
if ( USB_OTG_FS_PERIPH_BASE == (uint32_t) dwc2 ) {
RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB2OTGFSULPILPEN;
}
#endif
#if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB1OTGHSULPILPEN)
if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) {
RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB1OTGHSULPILPEN;
}
#endif
#if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_OTGHSULPILPEN)
if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) {
RCC->AHB1LPENR &= ~RCC_AHB1LPENR_OTGHSULPILPEN;
}
#endif
} else {
#if CFG_TUSB_MCU != OPT_MCU_STM32U5 && CFG_TUSB_MCU != OPT_MCU_STM32WBA
// Disable FS PHY, TODO on U5A5 (dwc2 4.11a) 16th bit is 'Host CDP behavior enable'
dwc2->stm32_gccfg &= ~STM32_GCCFG_PWRDWN;
#endif
// Enable on-chip HS PHY
if (hs_phy_type == GHWCFG2_HSPHY_UTMI || hs_phy_type == GHWCFG2_HSPHY_UTMI_ULPI) {
#ifdef USB_HS_PHYC
// Enable UTMI HS PHY
dwc2->stm32_gccfg |= STM32_GCCFG_PHYHSEN;
// Enable LDO
USB_HS_PHYC->USB_HS_PHYC_LDO |= USB_HS_PHYC_LDO_ENABLE;
// Wait until LDO ready
while ( 0 == (USB_HS_PHYC->USB_HS_PHYC_LDO & USB_HS_PHYC_LDO_STATUS) ) {}
uint32_t phyc_pll = 0;
// TODO Try to get HSE_VALUE from registers instead of depending CFLAGS
switch ( HSE_VALUE )
{
case 12000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12MHZ ; break;
case 12500000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12_5MHZ ; break;
case 16000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_16MHZ ; break;
case 24000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_24MHZ ; break;
case 25000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_25MHZ ; break;
case 32000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_Msk ; break; // Value not defined in header
default:
TU_ASSERT(false, );
}
USB_HS_PHYC->USB_HS_PHYC_PLL = phyc_pll;
// Control the tuning interface of the High Speed PHY
// Use magic value (USB_HS_PHYC_TUNE_VALUE) from ST driver for F7
USB_HS_PHYC->USB_HS_PHYC_TUNE |= 0x00000F13U;
// Enable PLL internal PHY
USB_HS_PHYC->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN;
// Wait ~2ms until the PLL is ready (there's no RDY bit to query)
tusb_time_delay_ms_api(2);
#else
#endif
}
}
}
// MCU specific PHY deinit, disable PHY power
static inline void dwc2_phy_deinit(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) {
// Disable on-chip FS PHY
dwc2->stm32_gccfg &= ~STM32_GCCFG_PWRDWN;
} else {
// Disable HS PHY
#ifdef USB_HS_PHYC
dwc2->stm32_gccfg &= ~STM32_GCCFG_PHYHSEN;
// Disable PLL and LDO
USB_HS_PHYC->USB_HS_PHYC_PLL &= ~USB_HS_PHYC_PLL_PLLEN;
USB_HS_PHYC->USB_HS_PHYC_LDO &= ~USB_HS_PHYC_LDO_ENABLE;
#endif
}
}
// MCU specific PHY update, it is called AFTER init() and core reset
static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
// used to set turnaround time for fullspeed, nothing to do in highspeed mode
if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) {
// Turnaround timeout depends on the AHB clock dictated by STM32 Reference Manual
uint32_t turnaround;
if (SystemCoreClock >= 32000000u) {
turnaround = 0x6u;
} else if (SystemCoreClock >= 27500000u) {
turnaround = 0x7u;
} else if (SystemCoreClock >= 24000000u) {
turnaround = 0x8u;
} else if (SystemCoreClock >= 21800000u) {
turnaround = 0x9u;
}
else if (SystemCoreClock >= 20000000u) {
turnaround = 0xAu;
}
else if (SystemCoreClock >= 18500000u) {
turnaround = 0xBu;
}
else if (SystemCoreClock >= 17200000u) {
turnaround = 0xCu;
}
else if (SystemCoreClock >= 16000000u) {
turnaround = 0xDu;
}
else if (SystemCoreClock >= 15000000u) {
turnaround = 0xEu;
}
else {
turnaround = 0xFu;
}
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (turnaround << GUSBCFG_TRDT_Pos);
}
}
//------------- GCCFG configuration -------------//
static inline void dwc2_stm32_gccfg_cfg(dwc2_regs_t* dwc2, bool vbus_sensing, bool is_host) {
if (is_host) {
vbus_sensing = false;
}
uint32_t gccfg = dwc2->stm32_gccfg;
if (dwc2->guid < 0x2000) {
// use VBUSASEN/VBUSBSEN/NOVBUSSENS bits
if (is_host) {
gccfg &= ~(STM32_GCCFG_NOVBUSSENS | STM32_GCCFG_VBUSBSEN | STM32_GCCFG_VBUSASEN);
} else {
if (vbus_sensing) {
gccfg &= ~STM32_GCCFG_NOVBUSSENS;
gccfg |= STM32_GCCFG_VBUSBSEN;
} else {
gccfg |= STM32_GCCFG_NOVBUSSENS;
gccfg &= ~(STM32_GCCFG_VBUSBSEN | STM32_GCCFG_VBUSASEN);
}
}
} else if (dwc2->guid < 0x5000) {
// the later version uses VBDEN with battery charging detection
if (vbus_sensing) {
gccfg |= STM32_GCCFG_VBDEN;
} else {
gccfg &= ~STM32_GCCFG_VBDEN;
}
} else {
// from 0x5000 ST seems to use femtoPHY for UTMI+ HS PHY. Which use VBVALEXTOEN and VBVALOVAL for software override
// external VBUS sensing
// Note: N6 does not support hardware VBUS sensing, so the software override is always active. Therefore, VBDEN and
// VBVALEXTOEN are not available
#if CFG_TUSB_MCU == OPT_MCU_STM32N6
if (is_host) {
gccfg |= STM32_GCCFG_PULLDOWNEN;
gccfg &= ~(STM32_GCCFG_VBVALOVAL);
} else {
gccfg &= ~STM32_GCCFG_PULLDOWNEN;
gccfg |= STM32_GCCFG_VBVALOVAL;
}
#else
if (is_host) {
gccfg |= STM32_GCCFG_PULLDOWNEN;
gccfg &= ~(STM32_GCCFG_VBDEN | STM32_GCCFG_VBVALEXTOEN | STM32_GCCFG_VBVALOVAL);
} else {
gccfg &= ~STM32_GCCFG_PULLDOWNEN;
if (vbus_sensing) {
gccfg |= STM32_GCCFG_VBDEN;
gccfg &= ~(STM32_GCCFG_VBVALEXTOEN | STM32_GCCFG_VBVALOVAL);
} else {
gccfg &= ~STM32_GCCFG_VBDEN;
gccfg |= STM32_GCCFG_VBVALEXTOEN | STM32_GCCFG_VBVALOVAL;
}
}
#endif
}
dwc2->stm32_gccfg = gccfg;
}
//------------- DCache -------------//
#if CFG_TUD_MEM_DCACHE_ENABLE || CFG_TUH_MEM_DCACHE_ENABLE
typedef struct {
uintptr_t start;
uintptr_t end;
} mem_region_t;
// Can be used to define additional uncached regions
#ifndef CFG_DWC2_MEM_UNCACHED_REGIONS
#define CFG_DWC2_MEM_UNCACHED_REGIONS
#endif
static mem_region_t uncached_regions[] = {
// DTCM (although USB DMA can't transfer to/from DTCM)
#if CFG_TUSB_MCU == OPT_MCU_STM32H7
{.start = 0x20000000, .end = 0x2001FFFF},
#elif CFG_TUSB_MCU == OPT_MCU_STM32H7RS
// DTCM (although USB DMA can't transfer to/from DTCM)
{.start = 0x20000000, .end = 0x2002FFFF},
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
// DTCM
{.start = 0x20000000, .end = 0x2000FFFF},
#elif CFG_TUSB_MCU == OPT_MCU_STM32N6
// DTCM NS
{.start = 0x20000000, .end = 0x2003FFFF},
// DTCM S
{.start = 0x30000000, .end = 0x3003FFFF},
#else
#error "Cache maintenance is not supported yet"
#endif
CFG_DWC2_MEM_UNCACHED_REGIONS
};
TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
if (size & (CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT-1)) {
size = (size & ~(CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT-1)) + CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT;
}
return size;
}
TU_ATTR_ALWAYS_INLINE static inline bool is_cache_mem(uintptr_t addr) {
if (0 == (SCB->CCR & SCB_CCR_DC_Msk)) {
return false; // D-Cache is disabled
}
for (unsigned int i = 0; i < TU_ARRAY_SIZE(uncached_regions); i++) {
if (uncached_regions[i].start <= addr && addr <= uncached_regions[i].end) { return false; }
}
return true;
}
TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean(void const* addr, uint32_t data_size) {
const uintptr_t addr32 = (uintptr_t) addr;
if (is_cache_mem(addr32)) {
data_size = round_up_to_cache_line_size(data_size);
SCB_CleanDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
}
return true;
}
TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_invalidate(void const* addr, uint32_t data_size) {
const uintptr_t addr32 = (uintptr_t) addr;
if (is_cache_mem(addr32)) {
data_size = round_up_to_cache_line_size(data_size);
SCB_InvalidateDCache_by_Addr((void*) addr32, (int32_t) data_size);
}
return true;
}
TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
const uintptr_t addr32 = (uintptr_t) addr;
if (is_cache_mem(addr32)) {
data_size = round_up_to_cache_line_size(data_size);
SCB_CleanInvalidateDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
}
return true;
}
#endif
#ifdef __cplusplus
}
#endif
#endif
|