Просмотр исходного кода

complete suspend, resume, remote wakeup for nrf52

hathach 4 лет назад
Родитель
Сommit
ab2eec77d4
2 измененных файлов с 97 добавлено и 35 удалено
  1. 24 5
      src/device/usbd.c
  2. 73 30
      src/portable/nordic/nrf5x/dcd_nrf5x.c

+ 24 - 5
src/device/usbd.c

@@ -37,6 +37,9 @@
 // USBD Configuration
 //--------------------------------------------------------------------+
 
+// Debug level of USBD
+#define USBD_DBG_LVL   2
+
 #ifndef CFG_TUD_TASK_QUEUE_SZ
   #define CFG_TUD_TASK_QUEUE_SZ   16
 #endif
@@ -556,7 +559,7 @@ void tud_task (void)
       break;
 
       case DCD_EVENT_SUSPEND:
-        TU_LOG2("\r\n");
+        TU_LOG2(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en);
         if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
       break;
 
@@ -684,6 +687,8 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
           // Only support remote wakeup for device feature
           TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
 
+          TU_LOG(USBD_DBG_LVL, "    Enable Remote Wakeup\r\n");
+
           // Host may enable remote wake up before suspending especially HID device
           _usbd_dev.remote_wakeup_en = true;
           tud_control_status(rhport, p_request);
@@ -693,6 +698,8 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
           // Only support remote wakeup for device feature
           TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
 
+          TU_LOG(USBD_DBG_LVL, "    Disable Remote Wakeup\r\n");
+
           // Host may disable remote wake up after resuming
           _usbd_dev.remote_wakeup_en = false;
           tud_control_status(rhport, p_request);
@@ -1036,10 +1043,6 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
       }
     break;
 
-    case DCD_EVENT_SOF:
-      return;   // skip SOF event for now
-    break;
-
     case DCD_EVENT_SUSPEND:
       // NOTE: When plugging/unplugging device, the D+/D- state are unstable and
       // 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)
       }
     break;
 
+    case DCD_EVENT_SOF:
+      // Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup
+      // which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational
+      if ( _usbd_dev.suspended )
+      {
+        _usbd_dev.suspended = 0;
+        dcd_event_t const event_resume = { .rhport = event->rhport, .event_id = DCD_EVENT_RESUME };
+        osal_queue_send(_usbd_q, &event_resume, in_isr);
+      }
+    break;
+
     default:
       osal_queue_send(_usbd_q, event, in_isr);
     break;
@@ -1312,6 +1326,8 @@ bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
 
 void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
 {
+  TU_LOG(USBD_DBG_LVL, "    Stall EP %02X", ep_addr);
+
   uint8_t const epnum = tu_edpt_number(ep_addr);
   uint8_t const dir   = tu_edpt_dir(ep_addr);
 
@@ -1322,9 +1338,12 @@ void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
 
 void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
 {
+  TU_LOG(USBD_DBG_LVL, "    Clear Stall EP %02X", ep_addr);
+
   uint8_t const epnum = tu_edpt_number(ep_addr);
   uint8_t const dir   = tu_edpt_dir(ep_addr);
 
+
   dcd_edpt_clear_stall(rhport, ep_addr);
   _usbd_dev.ep_status[epnum][dir].stalled = false;
   _usbd_dev.ep_status[epnum][dir].busy = false;

+ 73 - 30
src/portable/nordic/nrf5x/dcd_nrf5x.c

@@ -85,7 +85,7 @@ static struct
   // Number of pending DMA that is started but not handled yet by dcd_int_handler().
   // Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
   // However, in critical section with interrupt disabled, the DMA can be finished and added up
-  // until handled by dcd_init_handler() when exiting critical section.
+  // until handled by dcd_int_handler() when exiting critical section.
   volatile uint8_t dma_pending;
 }_dcd;
 
@@ -277,14 +277,8 @@ void dcd_remote_wakeup(uint8_t rhport)
   (void) rhport;
 
   // Bring controller out of low power mode
+  // will start wakeup when USBWUALLOWED is set
   NRF_USBD->LOWPOWER = 0;
-
-  // Initiate RESUME signal
-  NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
-  NRF_USBD->TASKS_DPDMDRIVE = 1;
-
-  // TODO There is no USBEVENT Resume interrupt
-  // We may manually raise DCD_EVENT_RESUME event here
 }
 
 // disconnect by disabling internal pull-up resistor on D+/D-
@@ -339,10 +333,13 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
     {
       // SPLIT ISO buffer when ISO IN endpoint is already opened.
       if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
+
       // Clear old events
       NRF_USBD->EVENTS_ENDISOOUT = 0;
+
       // Clear SOF event in case interrupt was not enabled yet.
       if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
+
       // Enable SOF and ISOOUT interrupts, and ISOOUT endpoint.
       NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
       NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
@@ -350,10 +347,13 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
     else
     {
       NRF_USBD->EVENTS_ENDISOIN = 0;
+
       // SPLIT ISO buffer when ISO OUT endpoint is already opened.
       if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
+
       // Clear SOF event in case interrupt was not enabled yet.
       if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
+
       // Enable SOF and ISOIN interrupts, and ISOIN endpoint.
       NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
       NRF_USBD->EPINEN  |= USBD_EPINEN_ISOIN_Msk;
@@ -507,6 +507,8 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
  *------------------------------------------------------------------*/
 void bus_reset(void)
 {
+  // 6.35.6 USB controller automatically disabled all endpoints (except control)
+  // i.e EPOUTEN and EPINEN and reset USBADDR to 0
   for(int i=0; i<8; i++)
   {
     NRF_USBD->TASKS_STARTEPIN[i] = 0;
@@ -516,6 +518,15 @@ void bus_reset(void)
   NRF_USBD->TASKS_STARTISOIN  = 0;
   NRF_USBD->TASKS_STARTISOOUT = 0;
 
+  // Clear USB Event Interrupt
+  NRF_USBD->EVENTS_USBEVENT = 0;
+  NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE;
+
+  // Reset interrupt
+  NRF_USBD->INTENCLR = NRF_USBD->INTEN;
+  NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk |
+          USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
+
   tu_varclr(&_dcd);
   _dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
   _dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
@@ -561,39 +572,71 @@ void dcd_int_handler(uint8_t rhport)
 
   if ( int_status & USBD_INTEN_SOF_Msk )
   {
+    bool iso_enabled = false;
+
     // ISOOUT: Transfer data gathered in previous frame from buffer to RAM
     if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
     {
+      iso_enabled = true;
       xact_out_dma(EP_ISO_NUM);
     }
+
     // ISOIN: Notify client that data was transferred
-    xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
-    if ( xfer->iso_in_transfer_ready )
+    if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk)
     {
-      xfer->iso_in_transfer_ready = false;
-      dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
+      iso_enabled = true;
+
+      xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
+      if ( xfer->iso_in_transfer_ready )
+      {
+        xfer->iso_in_transfer_ready = false;
+        dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
+      }
     }
+
+    if ( !iso_enabled )
+    {
+      // ISO endpoint is not used, SOF is only enabled one-time for remote wakeup
+      // so we disable it now
+      NRF_USBD->INTENCLR = USBD_INTENSET_SOF_Msk;
+    }
+
     dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
   }
 
   if ( int_status & USBD_INTEN_USBEVENT_Msk )
   {
-    uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & (USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk);
+    TU_LOG(2, "EVENTCAUSE = 0x%04lX\r\n", NRF_USBD->EVENTCAUSE);
+
+    enum { EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk };
+    uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & EVT_CAUSE_MASK;
     NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt
 
     if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk )
     {
-      dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
-
       // Put controller into low power mode
+      // Leave HFXO disable to application, since it may be used by other peripherals
       NRF_USBD->LOWPOWER = 1;
 
-      // Leave HFXO disable to application, since it may be used by other
+      dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
     }
 
-    if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk  )
+    if ( evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk )
     {
-      dcd_event_bus_signal(0, DCD_EVENT_RESUME , true);
+      // USB is out of low power mode, and wakeup is allowed
+      // Initiate RESUME signal
+      NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
+      NRF_USBD->TASKS_DPDMDRIVE = 1;
+
+      // There is no Resume interrupt for remote wakeup, enable SOF for to report bus ready state
+      // Clear SOF event in case interrupt was not enabled yet.
+      if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
+      NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk;
+    }
+
+    if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk )
+    {
+      dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
     }
   }
 
@@ -845,18 +888,22 @@ void tusb_hal_nrf_power_event (uint32_t event)
     USB_EVT_READY = 2
   };
 
+#if CFG_TUSB_DEBUG >= 2
+  const char* const power_evt_str[] = { "Detected", "Removed", "Ready" };
+  TU_LOG(2, "Power USB event: %s\r\n", power_evt_str[event]);
+#endif
+
   switch ( event )
   {
     case USB_EVT_DETECTED:
-      TU_LOG2("Power USB Detect\r\n");
-
       if ( !NRF_USBD->ENABLE )
       {
-        /* Prepare for READY event receiving */
+        // Prepare for receiving READY event: disable interrupt since we will blocking wait
+        NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk;
         NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
         __ISB(); __DSB(); // for sync
 
-#ifdef NRF52_SERIES
+#ifdef NRF52_SERIES // NRF53 does not need this errata
         // ERRATA 171, 187, 166
         if ( nrfx_usbd_errata_187() )
         {
@@ -891,7 +938,7 @@ void tusb_hal_nrf_power_event (uint32_t event)
         }
 #endif
 
-        /* Enable the peripheral */
+        // Enable the peripheral (will cause Ready event)
         NRF_USBD->ENABLE = 1;
         __ISB(); __DSB(); // for sync
 
@@ -901,13 +948,11 @@ void tusb_hal_nrf_power_event (uint32_t event)
     break;
 
     case USB_EVT_READY:
-      TU_LOG2("Power USB Ready\r\n");
-
       // Skip if pull-up is enabled and HCLK is already running.
       // Application probably call this more than necessary.
       if ( NRF_USBD->USBPULLUP && hfclk_running() ) break;
 
-      /* Waiting for USBD peripheral enabled */
+      // Waiting for USBD peripheral enabled
       while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { }
 
       NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
@@ -959,9 +1004,8 @@ void tusb_hal_nrf_power_event (uint32_t event)
       // ISO buffer Lower half for IN, upper half for OUT
       NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
 
-      // Enable interrupt
-      NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_EPDATA_Msk |
-          USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
+      // Enable bus-reset interrupt
+      NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk;
 
       // Enable interrupt, priorities should be set by application
       NVIC_ClearPendingIRQ(USBD_IRQn);
@@ -976,7 +1020,6 @@ void tusb_hal_nrf_power_event (uint32_t event)
     break;
 
     case USB_EVT_REMOVED:
-      TU_LOG2("Power USB Removed\r\n");
       if ( NRF_USBD->ENABLE )
       {
         // Abort all transfers