summaryrefslogtreecommitdiff
path: root/src/portable/st/stm32_fsdev/fsdev_stm32.h
blob: 23e24752b208f52ac40fb7fa138512f534766a75 (plain)
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
/*
 * SPDX-FileCopyrightText: Copyright (c) 2019 Nathan Conrad
 * SPDX-FileCopyrightText: Copyright (c) 2019 Ha Thach (tinyusb.org)
 * SPDX-License-Identifier: MIT
 *
 * This file is part of the TinyUSB stack.
 */

#ifndef TUSB_FSDEV_STM32_H
#define TUSB_FSDEV_STM32_H

#if CFG_TUSB_MCU == OPT_MCU_STM32C0
  #include "stm32c0xx.h"
  #define FSDEV_HAS_SBUF_ISO 1

#elif CFG_TUSB_MCU == OPT_MCU_STM32C5
  #include "stm32c5xx.h"
  #define FSDEV_HAS_SBUF_ISO 1

#elif CFG_TUSB_MCU == OPT_MCU_STM32F0
  #include "stm32f0xx.h"
  #define FSDEV_HAS_SBUF_ISO 0
  // F0x2 models are crystal-less
  // All have internal D+ pull-up
  // 070RB:    2 x 16 bits/word memory     LPM Support, BCD Support
  // PMA dedicated to USB (no sharing with CAN)

#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
  #include "stm32f1xx.h"
  #define FSDEV_HAS_SBUF_ISO 0
  // NO internal Pull-ups
  //         *B, and *C:    2 x 16 bits/word

#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
  #include "stm32f3xx.h"
  #define FSDEV_HAS_SBUF_ISO 0
  // NO internal Pull-ups. PMA dedicated to USB (no sharing with CAN)
  // xB, and xC: 512 bytes
  // x6, x8, xD, and xE: 1024 bytes + LPM Support. When CAN clock is enabled, USB can use the first 768 bytes ONLY.

#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
  #include "stm32g0xx.h"
  #define FSDEV_HAS_SBUF_ISO 1

#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
  #include "stm32g4xx.h"
  #define FSDEV_HAS_SBUF_ISO 0

#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
  #include "stm32h5xx.h"
  #define FSDEV_HAS_SBUF_ISO 1

#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
  #include "stm32l0xx.h"
  #define FSDEV_HAS_SBUF_ISO 0

#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
  #include "stm32l1xx.h"
  #define FSDEV_HAS_SBUF_ISO 0

#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
  #include "stm32l4xx.h"
  #define FSDEV_HAS_SBUF_ISO 0

#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
  #include "stm32l5xx.h"
  #define FSDEV_HAS_SBUF_ISO 0

  #ifndef USB_PMAADDR
    #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
  #endif

#elif CFG_TUSB_MCU == OPT_MCU_STM32U0
  #include "stm32u0xx.h"
  #define FSDEV_HAS_SBUF_ISO 1

#elif CFG_TUSB_MCU == OPT_MCU_STM32U3
  #include "stm32u3xx.h"
  #define FSDEV_HAS_SBUF_ISO 1

#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
  #include "stm32u5xx.h"
  #define FSDEV_HAS_SBUF_ISO 1

#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
  #include "stm32wbxx.h"
  #define FSDEV_HAS_SBUF_ISO 0

#else
  #error You are using an untested or unimplemented STM32 variant. Please update the driver.
#endif

//--------------------------------------------------------------------+
// Register and PMA Base Address
//--------------------------------------------------------------------+
#ifndef FSDEV_REG_BASE
#if defined(USB_BASE)
  #define FSDEV_REG_BASE USB_BASE
#elif defined(USB_DRD_BASE)
  #define FSDEV_REG_BASE USB_DRD_BASE
#elif defined(USB_DRD_FS_BASE)
  #define FSDEV_REG_BASE USB_DRD_FS_BASE
#else
  #error "FSDEV_REG_BASE not defined"
#endif
#endif

#ifndef FSDEV_PMA_BASE
#if defined(USB_PMAADDR)
  #define FSDEV_PMA_BASE USB_PMAADDR
#elif defined(USB_DRD_PMAADDR)
  #define FSDEV_PMA_BASE USB_DRD_PMAADDR
#else
  #error "FSDEV_PMA_BASE not defined"
#endif
#endif

#ifndef FSDEV_HAS_SBUF_ISO
  #error "FSDEV_HAS_SBUF_ISO not defined"
#endif

#ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP
  // Default configuration for double-buffered isochronous endpoints:
  // - Enable double buffering on devices with >1KB Packet Memory Area (PMA)
  //   to improve isochronous transfer reliability and performance
  // - Disable on devices with limited PMA to conserve memory space
  #if CFG_TUSB_FSDEV_PMA_SIZE > 1024u
    #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 1
  #else
    #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0
  #endif
#endif

#if FSDEV_HAS_SBUF_ISO != 0 && CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP == 0
  // SBUF_ISO configuration:
  // - Some STM32 devices have special hardware support for single-buffered isochronous endpoints
  // - When SBUF_ISO bit is available and double buffering is disabled:
  //   Enable SBUF_ISO to optimize endpoint register usage (one half of endpoint pair register)
  #define FSDEV_USE_SBUF_ISO 1
#else
  // When either:
  // - Hardware doesn't support SBUF_ISO feature, or
  // - Double buffering is enabled for isochronous endpoints
  // We must use the entire endpoint pair register
  #define FSDEV_USE_SBUF_ISO 0
#endif

// STM32L1 calls it USB_FS_WKUP_IRQn; alias so the commented USBWakeUp_IRQn below
// can be uncommented as-is.
#if TU_CHECK_MCU(OPT_MCU_STM32L1) && !defined(USBWakeUp_IRQn)
  #define USBWakeUp_IRQn USB_FS_WKUP_IRQn
#endif

// USB interrupt vectors to enable in NVIC. The EXTI-line USB wakeup interrupt
// (USBWakeUp_IRQn, and USBWakeUp_RMP_IRQn on F3) is left commented out: resume is
// handled in-band via ISTR.WKUP in the USB_LP/HP ISR; the EXTI line is only needed to
// wake the core from STOP mode, which this driver does not implement (it never arms or
// clears that EXTI line, so enabling its NVIC vector can only spuriously fire/freeze).
// TODO: uncomment USBWakeUp_IRQn (+ arm/clear its EXTI line) when adding STOP-mode wakeup.
static const IRQn_Type fsdev_irq[] = {
  #if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4, OPT_MCU_STM32U5)
    USB_IRQn,
  #elif TU_CHECK_MCU(OPT_MCU_STM32L5, OPT_MCU_STM32U3)
    USB_FS_IRQn,
  #elif TU_CHECK_MCU(OPT_MCU_STM32C0, OPT_MCU_STM32C5, OPT_MCU_STM32H5, OPT_MCU_STM32U0)
    USB_DRD_FS_IRQn,
  #elif CFG_TUSB_MCU == OPT_MCU_STM32G0
    #ifdef STM32G0B0xx
    USB_IRQn,
    #else
    USB_UCPD1_2_IRQn,
    #endif
  #elif CFG_TUSB_MCU == OPT_MCU_STM32F1
    USB_HP_CAN1_TX_IRQn,
    USB_LP_CAN1_RX0_IRQn,
    //USBWakeUp_IRQn,
  #elif CFG_TUSB_MCU == OPT_MCU_STM32F3
    USB_HP_CAN_TX_IRQn,
    USB_LP_CAN_RX0_IRQn,
    //USBWakeUp_IRQn,
  #elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1)
    USB_HP_IRQn,
    USB_LP_IRQn,
    //USBWakeUp_IRQn,
  #elif CFG_TUSB_MCU == OPT_MCU_STM32WB
    USB_HP_IRQn,
    USB_LP_IRQn,
  #else
    #error Unknown arch in USB driver
  #endif
};
enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };

TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_enable(uint8_t rhport) {
  (void)rhport;

  // forces write to RAM before allowing ISR to execute
  __DSB(); __ISB();

  #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP)
  // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
  // shared USB/CAN IRQs to separate CAN and USB IRQs.
  // This dynamically checks if this remap is active to enable the right IRQs.
  if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
    NVIC_EnableIRQ(USB_HP_IRQn);
    NVIC_EnableIRQ(USB_LP_IRQn);
    //NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
  } else
  #endif
  {
    for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) {
      NVIC_EnableIRQ(fsdev_irq[i]);
    }
  }
}

TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) {
  (void)rhport;

  #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP)
  // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
  // shared USB/CAN IRQs to separate CAN and USB IRQs.
  // This dynamically checks if this remap is active to enable the right IRQs.
  if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
    NVIC_DisableIRQ(USB_HP_IRQn);
    NVIC_DisableIRQ(USB_LP_IRQn);
    //NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
  } else
  #endif
  {
    for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) {
      NVIC_DisableIRQ(fsdev_irq[i]);
    }
  }

  // CMSIS has a membar after disabling interrupts
}

//--------------------------------------------------------------------+
// STM32 FSDEV PMA Buffer Description Table errata workaround
//--------------------------------------------------------------------+

#ifdef CFG_TUSB_FSDEV_32BIT
/* Errata: Buffer description table update completes after CTR interrupt triggers
 * https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf
 * https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf
 *
 * CTR may trigger before final PMA SRAM accesses complete on OUT transfers.
 * Insert delay before reading PMA count/data.
 * Max CPU frequency in Hz, used to derive conservative FSDEV PMA delay defaults.
 */
#if CFG_TUSB_MCU == OPT_MCU_STM32H5
  #define FSDEV_STM32_CPU_HZ 250000000U
#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
  #define FSDEV_STM32_CPU_HZ 160000000U
#elif CFG_TUSB_MCU == OPT_MCU_STM32U3
  #define FSDEV_STM32_CPU_HZ 96000000U
#elif CFG_TUSB_MCU == OPT_MCU_STM32U0
  #define FSDEV_STM32_CPU_HZ 56000000U
#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
  #define FSDEV_STM32_CPU_HZ 64000000U
#elif CFG_TUSB_MCU == OPT_MCU_STM32C0
  #define FSDEV_STM32_CPU_HZ 48000000U
#elif CFG_TUSB_MCU == OPT_MCU_STM32C5
  #define FSDEV_STM32_CPU_HZ 144000000U
#endif

// 11 cycles / 800ns = ~13750000 cycles per second, used to derive conservative FSDEV PMA delay defaults
#ifndef CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT
  #define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT (FSDEV_STM32_CPU_HZ / 13750000U)
#endif

// 11 cycles / 6.4us = ~1718750 cycles per second, used to derive conservative FSDEV PMA delay defaults
#ifndef CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT
  #define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT (FSDEV_STM32_CPU_HZ / 1718750U)
#endif

/**
 * LDR from SP-relative: 2 cycles
 * SUBS: 1 cycle
 * STR to SP-relative: 2 cycles
 * LDR from SP-relative: 2 cycles
 * CMP: 1 cycle
 * BNE:
 * taken: 3 cycles total (often shown as 1 + pipeline refill)
 * not taken: 1 cycle
 * Total cycles if delay is needed: 11 cycles
 */
TU_ATTR_ALWAYS_INLINE static inline void fsdev_btable_workaround_delay(bool low_speed) {
  volatile uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT;
  while (cycle_count > 0U) {
    cycle_count--;
  }
}
#endif

//--------------------------------------------------------------------+
// Connect / Disconnect
//--------------------------------------------------------------------+

#if defined(USB_BCDR_DPPU)

TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) {
  (void)rhport;
  FSDEV_REG->BCDR &= ~U_BCDR_DPPU;
}

TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) {
  (void)rhport;
  FSDEV_REG->BCDR |= U_BCDR_DPPU;
}

#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151

TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) {
  (void)rhport;
  SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
}

TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) {
  (void)rhport;
  SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
}
#endif

#endif /* TUSB_FSDEV_STM32_H */