Răsfoiți Sursa

samd support suspend and resume. Though cannot distinguish between Suspend and Disconnect

should work with #47
hathach 7 ani în urmă
părinte
comite
dd9c441a61

+ 5 - 0
examples/device/cdc_msc_hid/ses/samd51/samd51.emProject

@@ -71,6 +71,11 @@
                     <file file_name="../../../../../hw/mcu/microchip/samd/asf4/samd51/hpl/gclk/hpl_gclk.c" />
                   </folder>
                 </folder>
+                <folder Name="hal">
+                  <folder Name="src">
+                    <file file_name="../../../../../hw/mcu/microchip/samd/asf4/samd51/hal/src/hal_atomic.c" />
+                  </folder>
+                </folder>
               </folder>
             </folder>
           </folder>

+ 47 - 36
src/portable/microchip/samd21/dcd_samd21.c

@@ -38,19 +38,20 @@ static ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
 static ATTR_ALIGNED(4) uint8_t _setup_packet[8];
 
 // Setup the control endpoint 0.
-static void bus_reset(void) {
-    // Max size of packets is 64 bytes.
-    UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT];
-    bank_out->PCKSIZE.bit.SIZE = 0x3;
-    UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN];
-    bank_in->PCKSIZE.bit.SIZE = 0x3;
-
-    UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0];
-    ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1);
-    ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP;
-
-    // Prepare for setup packet
-    dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet));
+static void bus_reset(void)
+{
+  // Max size of packets is 64 bytes.
+  UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT];
+  bank_out->PCKSIZE.bit.SIZE = 0x3;
+  UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN];
+  bank_in->PCKSIZE.bit.SIZE = 0x3;
+
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0];
+  ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1);
+  ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP;
+
+  // Prepare for setup packet
+  dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet));
 }
 
 
@@ -79,6 +80,7 @@ void dcd_init (uint8_t rhport)
   USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
   while (USB->DEVICE.SYNCBUSY.bit.ENABLE == 1) {}
 
+  USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending
   USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST;
 }
 
@@ -103,6 +105,10 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
   while (USB->DEVICE.DeviceEndpoint[0].EPSTATUS.bit.BK1RDY == 1) {}
 
   USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN;
+
+  // Enable SUSPEND interrupt since the bus signal D+/D- are stable now.
+  USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending
+  USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SUSPEND;
 }
 
 void dcd_set_config (uint8_t rhport, uint8_t config_num)
@@ -111,17 +117,13 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
   (void) config_num;
   // Nothing to do
 
-#if 0
-  // Enable suspend to detect disconnection
-  // TODO need to distinguish Suspend vs Disconnect
-  USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND;
-  USB->DEVICE.INTENSET.reg |= USB_DEVICE_INTENSET_SUSPEND;
-#endif
 }
 
 void dcd_remote_wakeup(uint8_t rhport)
 {
   (void) rhport;
+
+  USB->DEVICE.CTRLB.bit.UPRSM = 1;
 }
 
 /*------------------------------------------------------------------*/
@@ -305,35 +307,44 @@ void maybe_transfer_complete(void) {
 void USB_Handler(void)
 {
   uint32_t int_status = USB->DEVICE.INTFLAG.reg & USB->DEVICE.INTENSET.reg;
+  USB->DEVICE.INTFLAG.reg = int_status; // clear interrupt
 
   /*------------- Interrupt Processing -------------*/
-  if ( int_status & USB_DEVICE_INTFLAG_EORST )
-  {
-    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST;
-    bus_reset();
-    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
-  }
-
   if ( int_status & USB_DEVICE_INTFLAG_SOF )
   {
-    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;
     dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
   }
 
-#if 0
+  // SAMD doesn't distinguish between Suspend and Disconnect state.
+  // Both condition will cause SUSPEND interrupt triggered.
+  // To prevent being triggered when D+/D- are not stable, SUSPEND interrupt is only
+  // enabled when we received SET_ADDRESS request and cleared on Bus Reset
   if ( int_status & USB_DEVICE_INTFLAG_SUSPEND )
   {
-    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SUSPEND;
+    // Enable wakeup interrupt
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; // clear pending
+    USB->DEVICE.INTENSET.reg = USB_DEVICE_INTFLAG_WAKEUP;
 
-    // disable interrupt to prevent it continue to trigger
-    USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTENCLR_SUSPEND;
+    dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+  }
 
-    // TODO need to distinguish Suspend vs Disconnect
-    // reset address to 0, force host to re-enumerate after resume
-    USB->DEVICE.DADD.reg = 0;
-    dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
+  // Wakeup interrupt is only enabled when we got suspended.
+  // Wakeup interrupt will disable itself
+  if ( int_status & USB_DEVICE_INTFLAG_WAKEUP )
+  {
+    // disable wakeup interrupt itself
+    USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP;
+    dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+  }
+
+  if ( int_status & USB_DEVICE_INTFLAG_EORST )
+  {
+    // Disable both suspend and wakeup interrupt
+    USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND;
+
+    bus_reset();
+    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
   }
-#endif
 
   // Setup packet received.
   maybe_handle_setup_packet();

+ 55 - 15
src/portable/microchip/samd51/dcd_samd51.c

@@ -38,19 +38,20 @@ static UsbDeviceDescBank sram_registers[8][2];
 static ATTR_ALIGNED(4) uint8_t _setup_packet[8];
 
 // Setup the control endpoint 0.
-static void bus_reset(void) {
-    // Max size of packets is 64 bytes.
-    UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT];
-    bank_out->PCKSIZE.bit.SIZE = 0x3;
-    UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN];
-    bank_in->PCKSIZE.bit.SIZE = 0x3;
-
-    UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0];
-    ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1);
-    ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP;
-
-    // Prepare for setup packet
-    dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet));
+static void bus_reset(void)
+{
+  // Max size of packets is 64 bytes.
+  UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT];
+  bank_out->PCKSIZE.bit.SIZE = 0x3;
+  UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN];
+  bank_in->PCKSIZE.bit.SIZE = 0x3;
+
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0];
+  ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1);
+  ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP;
+
+  // Prepare for setup packet
+  dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet));
 }
 
 
@@ -78,6 +79,8 @@ void dcd_init (uint8_t rhport)
   USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS;
   USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
   while (USB->DEVICE.SYNCBUSY.bit.ENABLE == 1) {}
+
+  USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending
   USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST;
 }
 
@@ -108,6 +111,10 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
   while (USB->DEVICE.DeviceEndpoint[0].EPSTATUS.bit.BK1RDY == 1) {}
 
   USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN;
+
+  // Enable SUSPEND interrupt since the bus signal D+/D- are stable now.
+  USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending
+  USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SUSPEND;
 }
 
 void dcd_set_config (uint8_t rhport, uint8_t config_num)
@@ -120,6 +127,8 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
 void dcd_remote_wakeup(uint8_t rhport)
 {
   (void) rhport;
+
+  USB->DEVICE.CTRLB.bit.UPRSM = 1;
 }
 
 /*------------------------------------------------------------------*/
@@ -270,12 +279,42 @@ USB_TRFAIL1_PERR_0, USB_TRFAIL1_PERR_1, USB_TRFAIL1_PERR_2,
 USB_TRFAIL1_PERR_3, USB_TRFAIL1_PERR_4, USB_TRFAIL1_PERR_5,
 USB_TRFAIL1_PERR_6, USB_TRFAIL1_PERR_7, USB_UPRSM, USB_WAKEUP */
 void USB_0_Handler(void) {
-  uint32_t int_status = USB->DEVICE.INTFLAG.reg;
+  uint32_t int_status = USB->DEVICE.INTFLAG.reg & USB->DEVICE.INTENSET.reg;
 
   /*------------- Interrupt Processing -------------*/
+  // SAMD doesn't distinguish between Suspend and Disconnect state.
+  // Both condition will cause SUSPEND interrupt triggered.
+  // To prevent being triggered when D+/D- are not stable, SUSPEND interrupt is only
+  // enabled when we received SET_ADDRESS request and cleared on Bus Reset
+  if ( int_status & USB_DEVICE_INTFLAG_SUSPEND )
+  {
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SUSPEND;
+
+    // Enable wakeup interrupt
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; // clear pending
+    USB->DEVICE.INTENSET.reg = USB_DEVICE_INTFLAG_WAKEUP;
+
+    dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+  }
+
+  // Wakeup interrupt is only enabled when we got suspended.
+  // Wakeup interrupt will disable itself
+  if ( int_status & USB_DEVICE_INTFLAG_WAKEUP )
+  {
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP;
+
+    // disable wakeup interrupt itself
+    USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP;
+    dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+  }
+
   if ( int_status & USB_DEVICE_INTFLAG_EORST )
   {
-    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_EORST;
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST;
+
+    // Disable both suspend and wakeup interrupt
+    USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND;
+
     bus_reset();
     dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
   }
@@ -283,6 +322,7 @@ void USB_0_Handler(void) {
   // Setup packet received.
   maybe_handle_setup_packet();
 }
+
 /* USB_SOF_HSOF */
 void USB_1_Handler(void) {
     USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;