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
|
/*
* SPDX-FileCopyrightText: Copyright (c) 2021, Ha Thach (tinyusb.org)
* SPDX-License-Identifier: MIT
*
* This file is part of the TinyUSB stack.
*/
#ifndef TUSB_DWC2_ESP32_H_
#define TUSB_DWC2_ESP32_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_intr_alloc.h"
#include "soc/periph_defs.h"
// ESP32-S31 does not have USB_WRAP peripheral (HS-only with UTMI PHY)
#if !TU_CHECK_MCU(OPT_MCU_ESP32S31)
#include "soc/usb_wrap_struct.h"
#endif
#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
#define DWC2_FS_REG_BASE 0x60080000UL
#define DWC2_EP_MAX 7
static const dwc2_controller_t _dwc2_controller[] = {
{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .otg_dfifo_depth = 256 }
};
#elif TU_CHECK_MCU(OPT_MCU_ESP32H4)
// H4's USB_WRAP register block uses "wrap_*" field names. Map them to the
// names used by TinyUSB's DWC2 port to keep the source unchanged.
#define otg_conf wrap_otg_conf
#define pad_pull_override wrap_pad_pull_override
#define dp_pullup wrap_dp_pullup
#define dp_pulldown wrap_dp_pulldown
#define dm_pullup wrap_dm_pullup
#define dm_pulldown wrap_dm_pulldown
#define DWC2_FS_REG_BASE 0x60040000UL
#define DWC2_EP_MAX 7
static const dwc2_controller_t _dwc2_controller[] = {
{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .otg_dfifo_depth = 256 }
};
#elif TU_CHECK_MCU(OPT_MCU_ESP32P4)
#define DWC2_FS_REG_BASE 0x50040000UL
#define DWC2_HS_REG_BASE 0x50000000UL
#define DWC2_EP_MAX 16
// On ESP32 for consistency we associate
// - Port0 to OTG_FS, and Port1 to OTG_HS
static const dwc2_controller_t _dwc2_controller[] = {
{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .otg_dfifo_depth = 256 },
{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .otg_dfifo_depth = 1024 }
};
#elif TU_CHECK_MCU(OPT_MCU_ESP32S31)
#define DWC2_HS_REG_BASE 0x20300000UL
#define DWC2_EP_MAX 16
static const dwc2_controller_t _dwc2_controller[] = {
{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTGHS_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .otg_dfifo_depth = 1024 }
};
#endif
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
static intr_handle_t usb_ih[TU_ARRAY_SIZE(_dwc2_controller)];
static void dwc2_int_handler_wrap(void* arg) {
const uint8_t rhport = tu_u16_low((uint16_t)(uintptr_t)arg);
const tusb_role_t role = (tusb_role_t) tu_u16_high((uint16_t)(uintptr_t)arg);
#if CFG_TUD_ENABLED
if (role == TUSB_ROLE_DEVICE) {
dcd_int_handler(rhport);
}
#endif
#if CFG_TUH_ENABLED && !CFG_TUH_MAX3421
if (role == TUSB_ROLE_HOST) {
hcd_int_handler(rhport, true);
}
#endif
}
// 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) {
if (enabled) {
esp_intr_alloc(_dwc2_controller[rhport].irqnum, ESP_INTR_FLAG_LOWMED,
dwc2_int_handler_wrap, (void*)(uintptr_t)tu_u16(role, rhport), &usb_ih[rhport]);
} else {
esp_intr_free(usb_ih[rhport]);
}
}
#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) {
vTaskDelay(pdMS_TO_TICKS(1));
}
// MCU specific PHY init, called BEFORE core reset
TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
// maybe usb_utmi_hal_init()
}
// MCU specific PHY deinit, disable PHY power
TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_deinit(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
// PHY managed by ESP-IDF
}
// MCU specific PHY update, it is called AFTER init() and core reset
TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
(void)dwc2;
(void)hs_phy_type;
// maybe usb_utmi_hal_disable()
}
//--------------------------------------------------------------------+
// Data Cache
//--------------------------------------------------------------------+
#if CFG_TUD_DWC2_DMA_ENABLE || CFG_TUH_DWC2_DMA_ENABLE
#if defined(SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE) && SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#include "esp_cache.h"
#if CFG_TUD_MEM_DCACHE_LINE_SIZE != CONFIG_CACHE_L1_CACHE_LINE_SIZE || \
CFG_TUH_MEM_DCACHE_LINE_SIZE != CONFIG_CACHE_L1_CACHE_LINE_SIZE
#error "CFG_TUD/TUH_MEM_DCACHE_LINE_SIZE must match CONFIG_CACHE_L1_CACHE_LINE_SIZE"
#endif
TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
if (size & (CONFIG_CACHE_L1_CACHE_LINE_SIZE-1)) {
size = (size & ~(CONFIG_CACHE_L1_CACHE_LINE_SIZE-1)) + CONFIG_CACHE_L1_CACHE_LINE_SIZE;
}
return size;
}
TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean(const void* addr, uint32_t data_size) {
const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_C2M;
data_size = round_up_to_cache_line_size(data_size);
return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
}
TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_invalidate(const void* addr, uint32_t data_size) {
const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_M2C;
data_size = round_up_to_cache_line_size(data_size);
return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
}
TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_DIR_M2C;
data_size = round_up_to_cache_line_size(data_size);
return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
}
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif
|