Skip to content

Commit fc8bb91

Browse files
author
Felipe Balbi
committed
usb: dwc3: implement runtime PM
this patch implements the most basic pm_runtime support for dwc3. Whenever USB cable is dettached, then we will allow core to runtime_suspend. Runtime suspending will involve completely tearing down event buffers and require a full soft-reset of the IP. Note that a further optimization could be implemented once we decide to support hibernation, which is to allow runtime_suspend with cable connected when bus is in U3. That's subject to a separate patch, however. Tested-by: Baolin Wang <[email protected]> Signed-off-by: Felipe Balbi <[email protected]>
1 parent 4cb4221 commit fc8bb91

File tree

3 files changed

+174
-15
lines changed

3 files changed

+174
-15
lines changed

drivers/usb/dwc3/core.c

Lines changed: 134 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848

4949
#include "debug.h"
5050

51-
/* -------------------------------------------------------------------------- */
51+
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
5252

5353
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
5454
{
@@ -996,6 +996,9 @@ static int dwc3_probe(struct platform_device *pdev)
996996
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
997997
}
998998

999+
pm_runtime_set_active(dev);
1000+
pm_runtime_use_autosuspend(dev);
1001+
pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
9991002
pm_runtime_enable(dev);
10001003
pm_runtime_get_sync(dev);
10011004
pm_runtime_forbid(dev);
@@ -1057,7 +1060,7 @@ static int dwc3_probe(struct platform_device *pdev)
10571060
goto err3;
10581061

10591062
dwc3_debugfs_init(dwc);
1060-
pm_runtime_allow(dev);
1063+
pm_runtime_put(dev);
10611064

10621065
return 0;
10631066

@@ -1087,6 +1090,7 @@ static int dwc3_remove(struct platform_device *pdev)
10871090
struct dwc3 *dwc = platform_get_drvdata(pdev);
10881091
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
10891092

1093+
pm_runtime_get_sync(&pdev->dev);
10901094
/*
10911095
* restore res->start back to its original value so that, in case the
10921096
* probe is deferred, we don't end up getting error in request the
@@ -1100,24 +1104,27 @@ static int dwc3_remove(struct platform_device *pdev)
11001104
dwc3_core_exit(dwc);
11011105
dwc3_ulpi_exit(dwc);
11021106

1103-
dwc3_free_event_buffers(dwc);
1104-
dwc3_free_scratch_buffers(dwc);
1105-
11061107
pm_runtime_put_sync(&pdev->dev);
1108+
pm_runtime_allow(&pdev->dev);
11071109
pm_runtime_disable(&pdev->dev);
11081110

1111+
dwc3_free_event_buffers(dwc);
1112+
dwc3_free_scratch_buffers(dwc);
1113+
11091114
return 0;
11101115
}
11111116

1112-
#ifdef CONFIG_PM_SLEEP
1113-
static int dwc3_suspend(struct device *dev)
1117+
#ifdef CONFIG_PM
1118+
static int dwc3_suspend_common(struct dwc3 *dwc)
11141119
{
1115-
struct dwc3 *dwc = dev_get_drvdata(dev);
1120+
unsigned long flags;
11161121

11171122
switch (dwc->dr_mode) {
11181123
case USB_DR_MODE_PERIPHERAL:
11191124
case USB_DR_MODE_OTG:
1125+
spin_lock_irqsave(&dwc->lock, flags);
11201126
dwc3_gadget_suspend(dwc);
1127+
spin_unlock_irqrestore(&dwc->lock, flags);
11211128
break;
11221129
case USB_DR_MODE_HOST:
11231130
default:
@@ -1127,33 +1134,144 @@ static int dwc3_suspend(struct device *dev)
11271134

11281135
dwc3_core_exit(dwc);
11291136

1130-
pinctrl_pm_select_sleep_state(dev);
1131-
11321137
return 0;
11331138
}
11341139

1135-
static int dwc3_resume(struct device *dev)
1140+
static int dwc3_resume_common(struct dwc3 *dwc)
11361141
{
1137-
struct dwc3 *dwc = dev_get_drvdata(dev);
1142+
unsigned long flags;
11381143
int ret;
11391144

1140-
pinctrl_pm_select_default_state(dev);
1141-
11421145
ret = dwc3_core_init(dwc);
11431146
if (ret)
11441147
return ret;
11451148

11461149
switch (dwc->dr_mode) {
11471150
case USB_DR_MODE_PERIPHERAL:
11481151
case USB_DR_MODE_OTG:
1152+
spin_lock_irqsave(&dwc->lock, flags);
11491153
dwc3_gadget_resume(dwc);
1154+
spin_unlock_irqrestore(&dwc->lock, flags);
11501155
/* FALLTHROUGH */
11511156
case USB_DR_MODE_HOST:
11521157
default:
11531158
/* do nothing */
11541159
break;
11551160
}
11561161

1162+
return 0;
1163+
}
1164+
1165+
static int dwc3_runtime_checks(struct dwc3 *dwc)
1166+
{
1167+
switch (dwc->dr_mode) {
1168+
case USB_DR_MODE_PERIPHERAL:
1169+
case USB_DR_MODE_OTG:
1170+
if (dwc->connected)
1171+
return -EBUSY;
1172+
break;
1173+
case USB_DR_MODE_HOST:
1174+
default:
1175+
/* do nothing */
1176+
break;
1177+
}
1178+
1179+
return 0;
1180+
}
1181+
1182+
static int dwc3_runtime_suspend(struct device *dev)
1183+
{
1184+
struct dwc3 *dwc = dev_get_drvdata(dev);
1185+
int ret;
1186+
1187+
if (dwc3_runtime_checks(dwc))
1188+
return -EBUSY;
1189+
1190+
ret = dwc3_suspend_common(dwc);
1191+
if (ret)
1192+
return ret;
1193+
1194+
device_init_wakeup(dev, true);
1195+
1196+
return 0;
1197+
}
1198+
1199+
static int dwc3_runtime_resume(struct device *dev)
1200+
{
1201+
struct dwc3 *dwc = dev_get_drvdata(dev);
1202+
int ret;
1203+
1204+
device_init_wakeup(dev, false);
1205+
1206+
ret = dwc3_resume_common(dwc);
1207+
if (ret)
1208+
return ret;
1209+
1210+
switch (dwc->dr_mode) {
1211+
case USB_DR_MODE_PERIPHERAL:
1212+
case USB_DR_MODE_OTG:
1213+
dwc3_gadget_process_pending_events(dwc);
1214+
break;
1215+
case USB_DR_MODE_HOST:
1216+
default:
1217+
/* do nothing */
1218+
break;
1219+
}
1220+
1221+
pm_runtime_mark_last_busy(dev);
1222+
1223+
return 0;
1224+
}
1225+
1226+
static int dwc3_runtime_idle(struct device *dev)
1227+
{
1228+
struct dwc3 *dwc = dev_get_drvdata(dev);
1229+
1230+
switch (dwc->dr_mode) {
1231+
case USB_DR_MODE_PERIPHERAL:
1232+
case USB_DR_MODE_OTG:
1233+
if (dwc3_runtime_checks(dwc))
1234+
return -EBUSY;
1235+
break;
1236+
case USB_DR_MODE_HOST:
1237+
default:
1238+
/* do nothing */
1239+
break;
1240+
}
1241+
1242+
pm_runtime_mark_last_busy(dev);
1243+
pm_runtime_autosuspend(dev);
1244+
1245+
return 0;
1246+
}
1247+
#endif /* CONFIG_PM */
1248+
1249+
#ifdef CONFIG_PM_SLEEP
1250+
static int dwc3_suspend(struct device *dev)
1251+
{
1252+
struct dwc3 *dwc = dev_get_drvdata(dev);
1253+
int ret;
1254+
1255+
ret = dwc3_suspend_common(dwc);
1256+
if (ret)
1257+
return ret;
1258+
1259+
pinctrl_pm_select_sleep_state(dev);
1260+
1261+
return 0;
1262+
}
1263+
1264+
static int dwc3_resume(struct device *dev)
1265+
{
1266+
struct dwc3 *dwc = dev_get_drvdata(dev);
1267+
int ret;
1268+
1269+
pinctrl_pm_select_default_state(dev);
1270+
1271+
ret = dwc3_resume_common(dwc);
1272+
if (ret)
1273+
return ret;
1274+
11571275
pm_runtime_disable(dev);
11581276
pm_runtime_set_active(dev);
11591277
pm_runtime_enable(dev);
@@ -1164,6 +1282,8 @@ static int dwc3_resume(struct device *dev)
11641282

11651283
static const struct dev_pm_ops dwc3_dev_pm_ops = {
11661284
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
1285+
SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
1286+
dwc3_runtime_idle)
11671287
};
11681288

11691289
#ifdef CONFIG_OF

drivers/usb/dwc3/core.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,7 @@ struct dwc3_scratchpad_array {
763763
* @lpm_nyet_threshold: LPM NYET response threshold
764764
* @hird_threshold: HIRD threshold
765765
* @hsphy_interface: "utmi" or "ulpi"
766+
* @connected: true when we're connected to a host, false otherwise
766767
* @delayed_status: true when gadget driver asks for delayed status
767768
* @ep0_bounced: true when we used bounce buffer
768769
* @ep0_expect_in: true when we expect a DATA IN transfer
@@ -773,6 +774,7 @@ struct dwc3_scratchpad_array {
773774
* 0 - utmi_sleep_n
774775
* 1 - utmi_l1_suspend_n
775776
* @is_fpga: true when we are using the FPGA board
777+
* @pending_events: true when we have pending IRQs to be handled
776778
* @pullups_connected: true when Run/Stop bit is set
777779
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
778780
* @start_config_issued: true when StartConfig command has been issued
@@ -907,13 +909,15 @@ struct dwc3 {
907909

908910
const char *hsphy_interface;
909911

912+
unsigned connected:1;
910913
unsigned delayed_status:1;
911914
unsigned ep0_bounced:1;
912915
unsigned ep0_expect_in:1;
913916
unsigned has_hibernation:1;
914917
unsigned has_lpm_erratum:1;
915918
unsigned is_utmi_l1_suspend:1;
916919
unsigned is_fpga:1;
920+
unsigned pending_events:1;
917921
unsigned pullups_connected:1;
918922
unsigned setup_packet_pending:1;
919923
unsigned three_stage_setup:1;
@@ -1139,6 +1143,7 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
11391143
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
11401144
int dwc3_gadget_suspend(struct dwc3 *dwc);
11411145
int dwc3_gadget_resume(struct dwc3 *dwc);
1146+
void dwc3_gadget_process_pending_events(struct dwc3 *dwc);
11421147
#else
11431148
static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
11441149
{
@@ -1149,6 +1154,10 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
11491154
{
11501155
return 0;
11511156
}
1157+
1158+
static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
1159+
{
1160+
}
11521161
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
11531162

11541163
#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)

drivers/usb/dwc3/gadget.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
199199
spin_unlock(&dwc->lock);
200200
usb_gadget_giveback_request(&dep->endpoint, &req->request);
201201
spin_lock(&dwc->lock);
202+
203+
if (dep->number > 1)
204+
pm_runtime_put(dwc->dev);
202205
}
203206

204207
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
@@ -1081,6 +1084,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
10811084
return -EINVAL;
10821085
}
10831086

1087+
pm_runtime_get(dwc->dev);
1088+
10841089
req->request.actual = 0;
10851090
req->request.status = -EINPROGRESS;
10861091
req->direction = dep->direction;
@@ -1509,6 +1514,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
15091514
u32 reg;
15101515
u32 timeout = 500;
15111516

1517+
if (pm_runtime_suspended(dwc->dev))
1518+
return 0;
1519+
15121520
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
15131521
if (is_on) {
15141522
if (dwc->revision <= DWC3_REVISION_187A) {
@@ -1766,7 +1774,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
17661774

17671775
dwc->gadget_driver = driver;
17681776

1769-
__dwc3_gadget_start(dwc);
1777+
if (pm_runtime_active(dwc->dev))
1778+
__dwc3_gadget_start(dwc);
1779+
17701780
spin_unlock_irqrestore(&dwc->lock, flags);
17711781

17721782
return 0;
@@ -2355,12 +2365,16 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
23552365
dwc->gadget.speed = USB_SPEED_UNKNOWN;
23562366
dwc->setup_packet_pending = false;
23572367
usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
2368+
2369+
dwc->connected = false;
23582370
}
23592371

23602372
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
23612373
{
23622374
u32 reg;
23632375

2376+
dwc->connected = true;
2377+
23642378
/*
23652379
* WORKAROUND: DWC3 revisions <1.88a have an issue which
23662380
* would cause a missing Disconnect Event if there's a
@@ -2822,6 +2836,13 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
28222836
u32 count;
28232837
u32 reg;
28242838

2839+
if (pm_runtime_suspended(dwc->dev)) {
2840+
pm_runtime_get(dwc->dev);
2841+
disable_irq_nosync(dwc->irq_gadget);
2842+
dwc->pending_events = true;
2843+
return IRQ_HANDLED;
2844+
}
2845+
28252846
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
28262847
count &= DWC3_GEVNTCOUNT_MASK;
28272848
if (!count)
@@ -3028,3 +3049,12 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
30283049
err0:
30293050
return ret;
30303051
}
3052+
3053+
void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
3054+
{
3055+
if (dwc->pending_events) {
3056+
dwc3_interrupt(dwc->irq_gadget, dwc->ev_buf);
3057+
dwc->pending_events = false;
3058+
enable_irq(dwc->irq_gadget);
3059+
}
3060+
}

0 commit comments

Comments
 (0)