Skip to content

Fix reboot into download from TinyUSB on ESP32-S3 #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 17, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 115 additions & 67 deletions cores/esp32/esp32-hal-tinyusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "hal/usb_hal.h"
#include "hal/gpio_ll.h"
#include "hal/usb_serial_jtag_ll.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
Expand Down Expand Up @@ -376,6 +377,120 @@ __attribute__ ((weak)) int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_
static bool usb_persist_enabled = false;
static restart_type_t usb_persist_mode = RESTART_NO_PERSIST;

#if CONFIG_IDF_TARGET_ESP32S3

static void hw_cdc_reset_handler(void *arg) {
portBASE_TYPE xTaskWoken = 0;
uint32_t usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask();
usb_serial_jtag_ll_clr_intsts_mask(usbjtag_intr_status);

if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
xSemaphoreGiveFromISR((xSemaphoreHandle)arg, &xTaskWoken);
}

if (xTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}

static void usb_switch_to_cdc_jtag(){
// Disable USB-OTG
periph_module_reset(PERIPH_USB_MODULE);
//periph_module_enable(PERIPH_USB_MODULE);
periph_module_disable(PERIPH_USB_MODULE);

// Switch to hardware CDC+JTAG
CLEAR_PERI_REG_MASK(RTC_CNTL_USB_CONF_REG, (RTC_CNTL_SW_HW_USB_PHY_SEL|RTC_CNTL_SW_USB_PHY_SEL|RTC_CNTL_USB_PAD_ENABLE));

// Do not use external PHY
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_PHY_SEL);

// Release GPIO pins from CDC+JTAG
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);

// Force the host to re-enumerate (BUS_RESET)
pinMode(USBPHY_DM_NUM, OUTPUT_OPEN_DRAIN);
pinMode(USBPHY_DP_NUM, OUTPUT_OPEN_DRAIN);
digitalWrite(USBPHY_DM_NUM, LOW);
digitalWrite(USBPHY_DP_NUM, LOW);

// Initialize CDC+JTAG ISR to listen for BUS_RESET
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
intr_handle_t intr_handle = NULL;
xSemaphoreHandle reset_sem = xSemaphoreCreateBinary();
if(reset_sem){
if(esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0, hw_cdc_reset_handler, reset_sem, &intr_handle) != ESP_OK){
vSemaphoreDelete(reset_sem);
reset_sem = NULL;
log_e("HW USB CDC failed to init interrupts");
}
} else {
log_e("reset_sem init failed");
}

// Connect GPIOs to integrated CDC+JTAG
SET_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);

// Wait for BUS_RESET to give us back the semaphore
if(reset_sem){
if(xSemaphoreTake(reset_sem, 1000 / portTICK_PERIOD_MS) != pdPASS){
log_e("reset_sem timeout");
}
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
esp_intr_free(intr_handle);
vSemaphoreDelete(reset_sem);
}
}
#endif

static void IRAM_ATTR usb_persist_shutdown_handler(void)
{
if(usb_persist_mode != RESTART_NO_PERSIST){
if (usb_persist_enabled) {
usb_dc_prepare_persist();
}
if (usb_persist_mode == RESTART_BOOTLOADER) {
//USB CDC Download
if (usb_persist_enabled) {
chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
#if CONFIG_IDF_TARGET_ESP32S2
} else {
periph_module_reset(PERIPH_USB_MODULE);
periph_module_enable(PERIPH_USB_MODULE);
#endif
}
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
} else if (usb_persist_mode == RESTART_BOOTLOADER_DFU) {
//DFU Download
#if CONFIG_IDF_TARGET_ESP32S2
// Reset USB Core
USB0.grstctl |= USB_CSFTRST;
while ((USB0.grstctl & USB_CSFTRST) == USB_CSFTRST){}
#endif
chip_usb_set_persist_flags(USBDC_BOOT_DFU);
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
} else if (usb_persist_enabled) {
//USB Persist reboot
chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
}
}
}

void usb_persist_restart(restart_type_t mode)
{
if (mode < RESTART_TYPE_MAX && esp_register_shutdown_handler(usb_persist_shutdown_handler) == ESP_OK) {
usb_persist_mode = mode;
#if CONFIG_IDF_TARGET_ESP32S3
if (mode == RESTART_BOOTLOADER) {
usb_switch_to_cdc_jtag();
}
#endif
esp_restart();
}
}

static bool tinyusb_reserve_in_endpoint(uint8_t endpoint){
if(endpoint > 6 || (tinyusb_endpoints.in & BIT(endpoint)) != 0){
return false;
Expand Down Expand Up @@ -521,60 +636,6 @@ static void tinyusb_apply_device_config(tinyusb_device_config_t *config){
tinyusb_device_descriptor.bDeviceProtocol = config->usb_protocol;
}

static void IRAM_ATTR usb_persist_shutdown_handler(void)
{
if(usb_persist_mode != RESTART_NO_PERSIST){
if (usb_persist_enabled) {
usb_dc_prepare_persist();
}
if (usb_persist_mode == RESTART_BOOTLOADER) {
//USB CDC Download
if (usb_persist_enabled) {
chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
} else {
#if CONFIG_IDF_TARGET_ESP32S3
/*
* This currently does not work!
* Integrated CDC+JTAG refuses to communicate, once into Download mode
*/
// Disable USB-OTG
periph_module_reset(PERIPH_USB_MODULE);
//periph_module_enable(PERIPH_USB_MODULE);
periph_module_disable(PERIPH_USB_MODULE);
// Switch to hardware CDC+JTAG
REG_CLR_BIT(RTC_CNTL_USB_CONF_REG, (RTC_CNTL_SW_HW_USB_PHY_SEL|RTC_CNTL_SW_USB_PHY_SEL));
// Release GPIO pins from CDC+JTAG
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);
// Force the host to re-enumerate (BUS_RESET)
pinMode(USBPHY_DM_NUM, OUTPUT_OPEN_DRAIN);
pinMode(USBPHY_DP_NUM, OUTPUT_OPEN_DRAIN);
digitalWrite(USBPHY_DM_NUM, LOW);
digitalWrite(USBPHY_DP_NUM, LOW);
delay(20);
// Connect GPIOs to integrated CDC+JTAG
SET_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);
// Do not use external PHY
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_PHY_SEL);
#else
periph_module_reset(PERIPH_USB_MODULE);
periph_module_enable(PERIPH_USB_MODULE);
#endif
}
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
} else if (usb_persist_mode == RESTART_BOOTLOADER_DFU) {
//DFU Download
// Reset USB Core
USB0.grstctl |= USB_CSFTRST;
while ((USB0.grstctl & USB_CSFTRST) == USB_CSFTRST){}
chip_usb_set_persist_flags(USBDC_BOOT_DFU);
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
} else if (usb_persist_enabled) {
//USB Persist reboot
chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
}
}
}

// USB Device Driver task
// This top level thread processes all usb events and invokes callbacks
static void usb_device_task(void *param) {
Expand Down Expand Up @@ -638,11 +699,6 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
periph_module_enable(PERIPH_USB_MODULE);
}

if (esp_register_shutdown_handler(usb_persist_shutdown_handler) != ESP_OK) {
tinyusb_is_initialized = false;
return ESP_FAIL;
}

tinyusb_config_t tusb_cfg = {
.external_phy = false // In the most cases you need to use a `false` value
};
Expand All @@ -655,14 +711,6 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
return err;
}

void usb_persist_restart(restart_type_t mode)
{
if (mode < RESTART_TYPE_MAX) {
usb_persist_mode = mode;
esp_restart();
}
}

uint8_t tinyusb_add_string_descriptor(const char * str){
if(str == NULL || tinyusb_string_descriptor_len >= MAX_STRING_DESCRIPTORS){
return 0;
Expand Down