Skip to content

Commit 8f4420f

Browse files
authored
Merge pull request #123 from adafruit/support-nrf-suspend-resume
add nrf5x suspend, resume, remote wakeup support
2 parents 633fa06 + b8c50c9 commit 8f4420f

File tree

8 files changed

+165
-39
lines changed

8 files changed

+165
-39
lines changed

src/class/audio/audio_device.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@
8989
(CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) || \
9090
CFG_TUSB_MCU == OPT_MCU_RX63X || \
9191
CFG_TUSB_MCU == OPT_MCU_RX65X || \
92-
CFG_TUSB_MCU == OPT_MCU_RX72N
92+
CFG_TUSB_MCU == OPT_MCU_RX72N || \
93+
CFG_TUSB_MCU == OPT_MCU_GD32VF103
9394
#define USE_LINEAR_BUFFER 0
9495
#else
9596
#define USE_LINEAR_BUFFER 1

src/device/dcd_attr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@
147147
//#elif TU_CHECK_MCU(MM32F327X)
148148
// #define DCD_ATTR_ENDPOINT_MAX not known yet
149149

150+
//------------- GigaDevice -------------//
151+
#elif TU_CHECK_MCU(GD32VF103)
152+
#define DCD_ATTR_ENDPOINT_MAX 4
153+
150154
#else
151155
#warning "DCD_ATTR_ENDPOINT_MAX is not defined for this MCU, default to 8"
152156
#define DCD_ATTR_ENDPOINT_MAX 8

src/device/usbd.c

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
// USBD Configuration
3838
//--------------------------------------------------------------------+
3939

40+
// Debug level of USBD
41+
#define USBD_DBG_LVL 2
42+
4043
#ifndef CFG_TUD_TASK_QUEUE_SZ
4144
#define CFG_TUD_TASK_QUEUE_SZ 16
4245
#endif
@@ -556,7 +559,7 @@ void tud_task (void)
556559
break;
557560

558561
case DCD_EVENT_SUSPEND:
559-
TU_LOG2("\r\n");
562+
TU_LOG2(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en);
560563
if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
561564
break;
562565

@@ -684,6 +687,8 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
684687
// Only support remote wakeup for device feature
685688
TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
686689

690+
TU_LOG(USBD_DBG_LVL, " Enable Remote Wakeup\r\n");
691+
687692
// Host may enable remote wake up before suspending especially HID device
688693
_usbd_dev.remote_wakeup_en = true;
689694
tud_control_status(rhport, p_request);
@@ -693,6 +698,8 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
693698
// Only support remote wakeup for device feature
694699
TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
695700

701+
TU_LOG(USBD_DBG_LVL, " Disable Remote Wakeup\r\n");
702+
696703
// Host may disable remote wake up after resuming
697704
_usbd_dev.remote_wakeup_en = false;
698705
tud_control_status(rhport, p_request);
@@ -1036,10 +1043,6 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
10361043
}
10371044
break;
10381045

1039-
case DCD_EVENT_SOF:
1040-
return; // skip SOF event for now
1041-
break;
1042-
10431046
case DCD_EVENT_SUSPEND:
10441047
// NOTE: When plugging/unplugging device, the D+/D- state are unstable and
10451048
// can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ).
@@ -1061,6 +1064,17 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
10611064
}
10621065
break;
10631066

1067+
case DCD_EVENT_SOF:
1068+
// Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup
1069+
// which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational
1070+
if ( _usbd_dev.suspended )
1071+
{
1072+
_usbd_dev.suspended = 0;
1073+
dcd_event_t const event_resume = { .rhport = event->rhport, .event_id = DCD_EVENT_RESUME };
1074+
osal_queue_send(_usbd_q, &event_resume, in_isr);
1075+
}
1076+
break;
1077+
10641078
default:
10651079
osal_queue_send(_usbd_q, event, in_isr);
10661080
break;
@@ -1312,6 +1326,8 @@ bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
13121326

13131327
void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
13141328
{
1329+
TU_LOG(USBD_DBG_LVL, " Stall EP %02X", ep_addr);
1330+
13151331
uint8_t const epnum = tu_edpt_number(ep_addr);
13161332
uint8_t const dir = tu_edpt_dir(ep_addr);
13171333

@@ -1322,9 +1338,12 @@ void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
13221338

13231339
void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
13241340
{
1341+
TU_LOG(USBD_DBG_LVL, " Clear Stall EP %02X", ep_addr);
1342+
13251343
uint8_t const epnum = tu_edpt_number(ep_addr);
13261344
uint8_t const dir = tu_edpt_dir(ep_addr);
13271345

1346+
13281347
dcd_edpt_clear_stall(rhport, ep_addr);
13291348
_usbd_dev.ep_status[epnum][dir].stalled = false;
13301349
_usbd_dev.ep_status[epnum][dir].busy = false;

src/portable/nordic/nrf5x/dcd_nrf5x.c

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ static struct
8585
// Number of pending DMA that is started but not handled yet by dcd_int_handler().
8686
// Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
8787
// However, in critical section with interrupt disabled, the DMA can be finished and added up
88-
// until handled by dcd_init_handler() when exiting critical section.
88+
// until handled by dcd_int_handler() when exiting critical section.
8989
volatile uint8_t dma_pending;
9090
}_dcd;
9191

@@ -277,14 +277,8 @@ void dcd_remote_wakeup(uint8_t rhport)
277277
(void) rhport;
278278

279279
// Bring controller out of low power mode
280+
// will start wakeup when USBWUALLOWED is set
280281
NRF_USBD->LOWPOWER = 0;
281-
282-
// Initiate RESUME signal
283-
NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
284-
NRF_USBD->TASKS_DPDMDRIVE = 1;
285-
286-
// TODO There is no USBEVENT Resume interrupt
287-
// We may manually raise DCD_EVENT_RESUME event here
288282
}
289283

290284
// disconnect by disabling internal pull-up resistor on D+/D-
@@ -339,21 +333,27 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
339333
{
340334
// SPLIT ISO buffer when ISO IN endpoint is already opened.
341335
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
336+
342337
// Clear old events
343338
NRF_USBD->EVENTS_ENDISOOUT = 0;
339+
344340
// Clear SOF event in case interrupt was not enabled yet.
345341
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
342+
346343
// Enable SOF and ISOOUT interrupts, and ISOOUT endpoint.
347344
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
348345
NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
349346
}
350347
else
351348
{
352349
NRF_USBD->EVENTS_ENDISOIN = 0;
350+
353351
// SPLIT ISO buffer when ISO OUT endpoint is already opened.
354352
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
353+
355354
// Clear SOF event in case interrupt was not enabled yet.
356355
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
356+
357357
// Enable SOF and ISOIN interrupts, and ISOIN endpoint.
358358
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
359359
NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
@@ -507,6 +507,8 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
507507
*------------------------------------------------------------------*/
508508
void bus_reset(void)
509509
{
510+
// 6.35.6 USB controller automatically disabled all endpoints (except control)
511+
// i.e EPOUTEN and EPINEN and reset USBADDR to 0
510512
for(int i=0; i<8; i++)
511513
{
512514
NRF_USBD->TASKS_STARTEPIN[i] = 0;
@@ -516,6 +518,15 @@ void bus_reset(void)
516518
NRF_USBD->TASKS_STARTISOIN = 0;
517519
NRF_USBD->TASKS_STARTISOOUT = 0;
518520

521+
// Clear USB Event Interrupt
522+
NRF_USBD->EVENTS_USBEVENT = 0;
523+
NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE;
524+
525+
// Reset interrupt
526+
NRF_USBD->INTENCLR = NRF_USBD->INTEN;
527+
NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk |
528+
USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
529+
519530
tu_varclr(&_dcd);
520531
_dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
521532
_dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
@@ -561,39 +572,71 @@ void dcd_int_handler(uint8_t rhport)
561572

562573
if ( int_status & USBD_INTEN_SOF_Msk )
563574
{
575+
bool iso_enabled = false;
576+
564577
// ISOOUT: Transfer data gathered in previous frame from buffer to RAM
565578
if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
566579
{
580+
iso_enabled = true;
567581
xact_out_dma(EP_ISO_NUM);
568582
}
583+
569584
// ISOIN: Notify client that data was transferred
570-
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
571-
if ( xfer->iso_in_transfer_ready )
585+
if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk)
572586
{
573-
xfer->iso_in_transfer_ready = false;
574-
dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
587+
iso_enabled = true;
588+
589+
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
590+
if ( xfer->iso_in_transfer_ready )
591+
{
592+
xfer->iso_in_transfer_ready = false;
593+
dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
594+
}
575595
}
596+
597+
if ( !iso_enabled )
598+
{
599+
// ISO endpoint is not used, SOF is only enabled one-time for remote wakeup
600+
// so we disable it now
601+
NRF_USBD->INTENCLR = USBD_INTENSET_SOF_Msk;
602+
}
603+
576604
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
577605
}
578606

579607
if ( int_status & USBD_INTEN_USBEVENT_Msk )
580608
{
581-
uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & (USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk);
609+
TU_LOG(2, "EVENTCAUSE = 0x%04lX\r\n", NRF_USBD->EVENTCAUSE);
610+
611+
enum { EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk };
612+
uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & EVT_CAUSE_MASK;
582613
NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt
583614

584615
if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk )
585616
{
586-
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
587-
588617
// Put controller into low power mode
618+
// Leave HFXO disable to application, since it may be used by other peripherals
589619
NRF_USBD->LOWPOWER = 1;
590620

591-
// Leave HFXO disable to application, since it may be used by other
621+
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
592622
}
593623

594-
if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk )
624+
if ( evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk )
595625
{
596-
dcd_event_bus_signal(0, DCD_EVENT_RESUME , true);
626+
// USB is out of low power mode, and wakeup is allowed
627+
// Initiate RESUME signal
628+
NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
629+
NRF_USBD->TASKS_DPDMDRIVE = 1;
630+
631+
// There is no Resume interrupt for remote wakeup, enable SOF for to report bus ready state
632+
// Clear SOF event in case interrupt was not enabled yet.
633+
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
634+
NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk;
635+
}
636+
637+
if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk )
638+
{
639+
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
597640
}
598641
}
599642

@@ -845,18 +888,22 @@ void tusb_hal_nrf_power_event (uint32_t event)
845888
USB_EVT_READY = 2
846889
};
847890

891+
#if CFG_TUSB_DEBUG >= 2
892+
const char* const power_evt_str[] = { "Detected", "Removed", "Ready" };
893+
TU_LOG(2, "Power USB event: %s\r\n", power_evt_str[event]);
894+
#endif
895+
848896
switch ( event )
849897
{
850898
case USB_EVT_DETECTED:
851-
TU_LOG2("Power USB Detect\r\n");
852-
853899
if ( !NRF_USBD->ENABLE )
854900
{
855-
/* Prepare for READY event receiving */
901+
// Prepare for receiving READY event: disable interrupt since we will blocking wait
902+
NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk;
856903
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
857904
__ISB(); __DSB(); // for sync
858905

859-
#ifdef NRF52_SERIES
906+
#ifdef NRF52_SERIES // NRF53 does not need this errata
860907
// ERRATA 171, 187, 166
861908
if ( nrfx_usbd_errata_187() )
862909
{
@@ -891,7 +938,7 @@ void tusb_hal_nrf_power_event (uint32_t event)
891938
}
892939
#endif
893940

894-
/* Enable the peripheral */
941+
// Enable the peripheral (will cause Ready event)
895942
NRF_USBD->ENABLE = 1;
896943
__ISB(); __DSB(); // for sync
897944

@@ -901,13 +948,11 @@ void tusb_hal_nrf_power_event (uint32_t event)
901948
break;
902949

903950
case USB_EVT_READY:
904-
TU_LOG2("Power USB Ready\r\n");
905-
906951
// Skip if pull-up is enabled and HCLK is already running.
907952
// Application probably call this more than necessary.
908953
if ( NRF_USBD->USBPULLUP && hfclk_running() ) break;
909954

910-
/* Waiting for USBD peripheral enabled */
955+
// Waiting for USBD peripheral enabled
911956
while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { }
912957

913958
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
@@ -959,9 +1004,8 @@ void tusb_hal_nrf_power_event (uint32_t event)
9591004
// ISO buffer Lower half for IN, upper half for OUT
9601005
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
9611006

962-
// Enable interrupt
963-
NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_EPDATA_Msk |
964-
USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
1007+
// Enable bus-reset interrupt
1008+
NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk;
9651009

9661010
// Enable interrupt, priorities should be set by application
9671011
NVIC_ClearPendingIRQ(USBD_IRQn);
@@ -976,7 +1020,6 @@ void tusb_hal_nrf_power_event (uint32_t event)
9761020
break;
9771021

9781022
case USB_EVT_REMOVED:
979-
TU_LOG2("Power USB Removed\r\n");
9801023
if ( NRF_USBD->ENABLE )
9811024
{
9821025
// Abort all transfers

src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@
113113
(CFG_TUSB_MCU == OPT_MCU_STM32F0 ) || \
114114
(CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_FSDEV)) || \
115115
(CFG_TUSB_MCU == OPT_MCU_STM32F3 ) || \
116-
(CFG_TUSB_MCU == OPT_MCU_STM32L0 ) \
116+
(CFG_TUSB_MCU == OPT_MCU_STM32L0 ) || \
117+
(CFG_TUSB_MCU == OPT_MCU_STM32L1 ) \
117118
)
118119

119120
// In order to reduce the dependance on HAL, we undefine this.
@@ -273,6 +274,20 @@ void dcd_connect(uint8_t rhport)
273274
USB->BCDR |= USB_BCDR_DPPU;
274275
}
275276

277+
#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151
278+
// Disable internal D+ PU
279+
void dcd_disconnect(uint8_t rhport)
280+
{
281+
(void) rhport;
282+
SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
283+
}
284+
285+
// Enable internal D+ PU
286+
void dcd_connect(uint8_t rhport)
287+
{
288+
(void) rhport;
289+
SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
290+
}
276291
#endif
277292

278293
// Enable device interrupt
@@ -284,6 +299,8 @@ void dcd_int_enable (uint8_t rhport)
284299
__ISB();
285300
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
286301
NVIC_EnableIRQ(USB_IRQn);
302+
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
303+
NVIC_EnableIRQ(USB_LP_IRQn);
287304
#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
288305
// Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
289306
// shared USB/CAN IRQs to separate CAN and USB IRQs.
@@ -318,6 +335,8 @@ void dcd_int_disable(uint8_t rhport)
318335

319336
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
320337
NVIC_DisableIRQ(USB_IRQn);
338+
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
339+
NVIC_DisableIRQ(USB_LP_IRQn);
321340
#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
322341
// Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
323342
// shared USB/CAN IRQs to separate CAN and USB IRQs.

src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@
8383
#include "stm32l0xx.h"
8484
#define PMA_LENGTH (1024u)
8585

86+
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
87+
#include "stm32l1xx.h"
88+
#define PMA_LENGTH (512u)
89+
8690
#else
8791
#error You are using an untested or unimplemented STM32 variant. Please update the driver.
8892
// This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4

0 commit comments

Comments
 (0)