Skip to content

Commit f765026

Browse files
tmon-nordicfabiobaltieri
authored andcommitted
drivers: usb: udc_dwc2: Enter hibernation in thread
Enter hibernation in thread context with the lock held to make sure to not queue any transfers when the core is hibernated. Signed-off-by: Tomasz Moń <[email protected]>
1 parent 5fc0405 commit f765026

File tree

1 file changed

+83
-24
lines changed

1 file changed

+83
-24
lines changed

drivers/usb/udc/udc_dwc2.c

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ enum dwc2_drv_event_type {
3131
DWC2_DRV_EVT_SETUP,
3232
/* Transaction on endpoint is finished */
3333
DWC2_DRV_EVT_EP_FINISHED,
34+
/* Core should enter hibernation */
35+
DWC2_DRV_EVT_ENTER_HIBERNATION,
3436
/* Core should exit hibernation due to bus reset */
3537
DWC2_DRV_EVT_HIBERNATION_EXIT_BUS_RESET,
3638
/* Core should exit hibernation due to host resume */
@@ -1075,6 +1077,18 @@ static void dwc2_exit_hibernation(const struct device *dev)
10751077
LOG_DBG("Hibernation exit complete");
10761078
}
10771079

1080+
static void cancel_hibernation_request(struct udc_dwc2_data *const priv)
1081+
{
1082+
k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_ENTER_HIBERNATION));
1083+
}
1084+
1085+
static void request_hibernation(struct udc_dwc2_data *const priv)
1086+
{
1087+
if (priv->suspend_type == DWC2_SUSPEND_HIBERNATION) {
1088+
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_ENTER_HIBERNATION));
1089+
}
1090+
}
1091+
10781092
static void dwc2_unset_unused_fifo(const struct device *dev)
10791093
{
10801094
struct udc_dwc2_data *const priv = udc_get_private(dev);
@@ -2624,6 +2638,8 @@ static void udc_dwc2_isr_handler(const struct device *dev)
26242638
sys_write32(USB_DWC2_GINTSTS_USBRST, gintsts_reg);
26252639
dwc2_on_bus_reset(dev);
26262640
LOG_DBG("USB Reset interrupt");
2641+
2642+
cancel_hibernation_request(priv);
26272643
}
26282644

26292645
if (int_status & USB_DWC2_GINTSTS_ENUMDONE) {
@@ -2638,6 +2654,8 @@ static void udc_dwc2_isr_handler(const struct device *dev)
26382654
sys_write32(USB_DWC2_GINTSTS_WKUPINT, gintsts_reg);
26392655
udc_set_suspended(dev, false);
26402656
udc_submit_event(dev, UDC_EVT_RESUME, 0);
2657+
2658+
cancel_hibernation_request(priv);
26412659
}
26422660

26432661
if (int_status & USB_DWC2_GINTSTS_IEPINT) {
@@ -2664,30 +2682,53 @@ static void udc_dwc2_isr_handler(const struct device *dev)
26642682
}
26652683

26662684
if (int_status & USB_DWC2_GINTSTS_USBSUSP) {
2685+
/* Clear USB Suspend interrupt. */
2686+
sys_write32(USB_DWC2_GINTSTS_USBSUSP, gintsts_reg);
2687+
26672688
if (!priv->enumdone) {
2668-
/* Clear stale suspend interrupt */
2669-
sys_write32(USB_DWC2_GINTSTS_USBSUSP, gintsts_reg);
2689+
/* Ignore stale suspend interrupt */
26702690
continue;
26712691
}
26722692

26732693
/* Notify the stack */
26742694
udc_set_suspended(dev, true);
26752695
udc_submit_event(dev, UDC_EVT_SUSPEND, 0);
26762696

2677-
if (priv->suspend_type == DWC2_SUSPEND_HIBERNATION) {
2678-
dwc2_enter_hibernation(dev);
2679-
/* Next interrupt will be from PMU */
2680-
break;
2681-
}
2682-
2683-
/* Clear USB Suspend interrupt. */
2684-
sys_write32(USB_DWC2_GINTSTS_USBSUSP, gintsts_reg);
2697+
request_hibernation(priv);
26852698
}
26862699
}
26872700

26882701
(void)dwc2_quirk_irq_clear(dev);
26892702
}
26902703

2704+
static void dwc2_handle_hibernation_exit(const struct device *dev,
2705+
bool bus_reset)
2706+
{
2707+
struct udc_dwc2_data *const priv = udc_get_private(dev);
2708+
2709+
dwc2_exit_hibernation(dev);
2710+
2711+
/* Let stack know we are no longer suspended */
2712+
udc_set_suspended(dev, false);
2713+
udc_submit_event(dev, UDC_EVT_RESUME, 0);
2714+
2715+
if (bus_reset) {
2716+
/* Clear all pending transfers */
2717+
k_event_clear(&priv->xfer_new, UINT32_MAX);
2718+
k_event_clear(&priv->xfer_finished, UINT32_MAX);
2719+
dwc2_on_bus_reset(dev);
2720+
} else {
2721+
/* Resume any pending transfer handling */
2722+
if (k_event_test(&priv->xfer_new, UINT32_MAX)) {
2723+
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_XFER));
2724+
}
2725+
2726+
if (k_event_test(&priv->xfer_finished, UINT32_MAX)) {
2727+
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));
2728+
}
2729+
}
2730+
}
2731+
26912732
static uint8_t pull_next_ep_from_bitmap(uint32_t *bitmap)
26922733
{
26932734
unsigned int bit;
@@ -2727,9 +2768,14 @@ static ALWAYS_INLINE void dwc2_thread_handler(void *const arg)
27272768
if (evt & BIT(DWC2_DRV_EVT_XFER)) {
27282769
k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_XFER));
27292770

2730-
LOG_DBG("New transfer(s) in the queue");
2731-
eps = k_event_test(&priv->xfer_new, UINT32_MAX);
2732-
k_event_clear(&priv->xfer_new, eps);
2771+
if (!priv->hibernated) {
2772+
LOG_DBG("New transfer(s) in the queue");
2773+
eps = k_event_test(&priv->xfer_new, UINT32_MAX);
2774+
k_event_clear(&priv->xfer_new, eps);
2775+
} else {
2776+
/* Events will be handled after hibernation exit */
2777+
eps = 0;
2778+
}
27332779

27342780
while (eps) {
27352781
ep = pull_next_ep_from_bitmap(&eps);
@@ -2746,8 +2792,13 @@ static ALWAYS_INLINE void dwc2_thread_handler(void *const arg)
27462792
if (evt & BIT(DWC2_DRV_EVT_EP_FINISHED)) {
27472793
k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));
27482794

2749-
eps = k_event_test(&priv->xfer_finished, UINT32_MAX);
2750-
k_event_clear(&priv->xfer_finished, eps);
2795+
if (!priv->hibernated) {
2796+
eps = k_event_test(&priv->xfer_finished, UINT32_MAX);
2797+
k_event_clear(&priv->xfer_finished, eps);
2798+
} else {
2799+
/* Events will be handled after hibernation exit */
2800+
eps = 0;
2801+
}
27512802

27522803
while (eps) {
27532804
ep = pull_next_ep_from_bitmap(&eps);
@@ -2776,22 +2827,30 @@ static ALWAYS_INLINE void dwc2_thread_handler(void *const arg)
27762827
dwc2_handle_evt_setup(dev);
27772828
}
27782829

2830+
if (evt & BIT(DWC2_DRV_EVT_ENTER_HIBERNATION)) {
2831+
config->irq_disable_func(dev);
2832+
2833+
prev = k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_ENTER_HIBERNATION));
2834+
2835+
/* Only enter hibernation if IRQ did not cancel the request */
2836+
if (prev & BIT(DWC2_DRV_EVT_ENTER_HIBERNATION)) {
2837+
dwc2_enter_hibernation(dev);
2838+
}
2839+
2840+
config->irq_enable_func(dev);
2841+
}
2842+
27792843
if (evt & hibernation_exit_events) {
2844+
bool bus_reset;
2845+
27802846
LOG_DBG("Hibernation exit event");
27812847
config->irq_disable_func(dev);
27822848

27832849
prev = k_event_clear(&priv->drv_evt, hibernation_exit_events);
2850+
bus_reset = prev & BIT(DWC2_DRV_EVT_HIBERNATION_EXIT_BUS_RESET);
27842851

27852852
if (priv->hibernated) {
2786-
dwc2_exit_hibernation(dev);
2787-
2788-
/* Let stack know we are no longer suspended */
2789-
udc_set_suspended(dev, false);
2790-
udc_submit_event(dev, UDC_EVT_RESUME, 0);
2791-
2792-
if (prev & BIT(DWC2_DRV_EVT_HIBERNATION_EXIT_BUS_RESET)) {
2793-
dwc2_on_bus_reset(dev);
2794-
}
2853+
dwc2_handle_hibernation_exit(dev, bus_reset);
27952854
}
27962855

27972856
config->irq_enable_func(dev);

0 commit comments

Comments
 (0)