@@ -831,6 +831,11 @@ static hcd_port_event_t _intr_hdlr_hprt(port_t *port, usb_dwc_hal_port_event_t h
831831 port -> flags .conn_dev_ena = 0 ;
832832 break ;
833833 }
834+ case USB_DWC_HAL_PORT_EVENT_REMOTE_WAKEUP : {
835+ esp_rom_printf ("WAKE\n" );
836+ port_event = HCD_PORT_EVENT_REMOTE_WAKEUP ;
837+ break ;
838+ }
834839 default : {
835840 abort ();
836841 break ;
@@ -1217,6 +1222,34 @@ static inline bool _is_fifo_config_by_bias(const usb_dwc_hal_fifo_config_t *cfg)
12171222 cfg -> ptx_fifo_lines == 0 );
12181223}
12191224
1225+ /**
1226+ * @brief Gate internal clock
1227+ *
1228+ * @param[in] port Pointer to the port object
1229+ * @param[in] enable enable/disable internal clock gating
1230+ * @return True internal clk successfully gated/un-gated
1231+ * @return False internal clk not gated/un-gated
1232+ */
1233+ static inline bool _internal_clk_gate (port_t * port , bool enable )
1234+ {
1235+ // Stop PHY Clock and gate HCLK
1236+ usb_dwc_hal_pwr_clk_internal_clock_gate (port -> hal , enable );
1237+
1238+ // Wait 10 PHY clock cycles, PHY Clock is 30MHz when using 16bit interface, 60MHz when 8bit interface
1239+ // which makes 33.3 nS. Busy wait for 1uS just to be sure
1240+ esp_rom_delay_us (1 );
1241+
1242+ const bool phy_clk_stopped = usb_dwc_hal_pwr_clk_check_phy_clk_stopped (port -> hal );
1243+ const bool hclk_gated = usb_dwc_hal_pwr_clk_check_hclk_gated (port -> hal );
1244+
1245+ // enable == phy_clk_stopped == hclk_gated
1246+ // When gating the clock, all 3 variables must be 1. When un-gating, all 3 must be 0.
1247+ if ((enable == phy_clk_stopped ) && (enable == hclk_gated )) {
1248+ return true;
1249+ }
1250+ return false;
1251+ }
1252+
12201253// ---------------------- Commands -------------------------
12211254
12221255static esp_err_t _port_cmd_power_on (port_t * port )
@@ -1225,6 +1258,7 @@ static esp_err_t _port_cmd_power_on(port_t *port)
12251258 // Port can only be powered on if it's currently unpowered
12261259 if (port -> state == HCD_PORT_STATE_NOT_POWERED ) {
12271260 port -> state = HCD_PORT_STATE_DISCONNECTED ;
1261+ //assert(_internal_clk_gate(port, false)); // Un-gate the phy clock TODO: Test this
12281262 usb_dwc_hal_port_init (port -> hal );
12291263 usb_dwc_hal_port_toggle_power (port -> hal , true);
12301264 ret = ESP_OK ;
@@ -1239,7 +1273,13 @@ static esp_err_t _port_cmd_power_off(port_t *port)
12391273 esp_err_t ret ;
12401274 // Port can only be unpowered if already powered
12411275 if (port -> state != HCD_PORT_STATE_NOT_POWERED ) {
1276+
1277+ if (port -> state == HCD_PORT_STATE_SUSPENDED ) {
1278+ // un-gate internal clock, to be able to toggle power on the port
1279+ assert (_internal_clk_gate (port , false)); // Un-gate the phy clock
1280+ }
12421281 port -> state = HCD_PORT_STATE_NOT_POWERED ;
1282+ //assert(_internal_clk_gate(port, false)); // Gate the phy clock TODO: Test this
12431283 usb_dwc_hal_port_deinit (port -> hal );
12441284 usb_dwc_hal_port_toggle_power (port -> hal , false);
12451285 // If a device is currently connected, this should trigger a disconnect event
@@ -1267,6 +1307,10 @@ static esp_err_t _port_cmd_reset(port_t *port)
12671307 ret = ESP_ERR_INVALID_STATE ;
12681308 goto exit ;
12691309 }
1310+ // If resetting from suspended state, we must un-gate the internal clock
1311+ if (port -> state == HCD_PORT_STATE_SUSPENDED ) {
1312+ _internal_clk_gate (port , false);
1313+ }
12701314 /*
12711315 Proceed to resetting the bus
12721316 - Update the port's state variable
@@ -1323,6 +1367,21 @@ static esp_err_t _port_cmd_reset(port_t *port)
13231367 return ret ;
13241368}
13251369
1370+ /**
1371+ * @brief Suspend the root port
1372+ *
1373+ * This sequence equals to a sequence from the DesignWare Cores USB 2.0 Programming Guide version 4.00a
1374+ * 14.2.3.3 External HCLK Gating When the Host Core is in Partial Power Down Mode
1375+ * Sequence Entering suspend state
1376+ *
1377+ * @param[in] port Pointer to the port object
1378+ * @return:
1379+ * - ESP_ERR_INVALID_STATE Port is not in a correct state to be suspended, or pipe(s) routed through this port is not halted
1380+ * - ESP_ERR_INVALID_RESPONSE Port state unexpectedly changed (for example: device was disconnected)
1381+ * - ESP_ERR_NOT_FINISHED Port did not finish the suspending sequence and is not in the suspended state
1382+ * - ESP_ERR_NOT_ALLOWED PHY clk was not suspended
1383+ * - ESP_OK Root port suspended
1384+ */
13261385static esp_err_t _port_cmd_bus_suspend (port_t * port )
13271386{
13281387 esp_err_t ret ;
@@ -1356,13 +1415,34 @@ static esp_err_t _port_cmd_bus_suspend(port_t *port)
13561415 goto exit ;
13571416 }
13581417
1418+ if (! _internal_clk_gate (port , true)) {
1419+ ret = ESP_ERR_NOT_ALLOWED ;
1420+ goto exit ;
1421+ }
1422+
13591423 port -> state = HCD_PORT_STATE_SUSPENDED ;
13601424 ret = ESP_OK ;
13611425
13621426exit :
13631427 return ret ;
13641428}
13651429
1430+ /**
1431+ * @brief Resume the root port
1432+ *
1433+ * This sequence equals to a sequence from the DesignWare Cores USB 2.0 Programming Guide version 4.00a
1434+ * 14.2.3.3 External HCLK Gating When the Host Core is in Partial Power Down Mode
1435+ * Sequence Exiting Suspend State Through Host Initiated Resume
1436+ * Exiting Suspend State Through Device Initiated Remote Wakeup
1437+ *
1438+ * @note this sequence is used for both resume scenarios: the host initiated and the device initiated (remote wakeup) resume
1439+ * @param[in] port Pointer to the port object
1440+ * @return:
1441+ * - ESP_ERR_INVALID_STATE Port is not in a correct state to be resumed
1442+ * - ESP_ERR_NOT_ALLOWED PHY clk was not resumed
1443+ * - ESP_ERR_INVALID_RESPONSE Port state unexpectedly changed (for example: device was disconnected)
1444+ * - ESP_OK Root port resumed
1445+ */
13661446static esp_err_t _port_cmd_bus_resume (port_t * port )
13671447{
13681448 esp_err_t ret ;
@@ -1371,6 +1451,12 @@ static esp_err_t _port_cmd_bus_resume(port_t *port)
13711451 ret = ESP_ERR_INVALID_STATE ;
13721452 goto exit ;
13731453 }
1454+
1455+ if (!_internal_clk_gate (port , false)) {
1456+ ret = ESP_ERR_NOT_ALLOWED ;
1457+ goto exit ;
1458+ }
1459+
13741460 // Put and hold the bus in the K state.
13751461 usb_dwc_hal_port_toggle_resume (port -> hal , true);
13761462 port -> state = HCD_PORT_STATE_RESUMING ;
@@ -1596,6 +1682,7 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl)
15961682 ESP_ERR_INVALID_STATE );
15971683
15981684 // We are about to do a soft reset on the peripheral. Disable the peripheral throughout
1685+ //assert(_internal_clk_gate(port, false)); // Gate the phy clock
15991686 esp_intr_disable (port -> isr_hdl );
16001687 usb_dwc_hal_core_soft_reset (port -> hal );
16011688 port -> state = HCD_PORT_STATE_NOT_POWERED ;
@@ -1605,6 +1692,7 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl)
16051692 // Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset
16061693 memset (port -> frame_list , 0 , FRAME_LIST_LEN * sizeof (uint32_t ));
16071694 esp_intr_enable (port -> isr_hdl );
1695+ //assert(_internal_clk_gate(port, true)); // Gate the phy clock
16081696 HCD_EXIT_CRITICAL ();
16091697 return ESP_OK ;
16101698}
0 commit comments