#include <linux/pm_runtime.h>
#include <linux/extcon.h>
#include <linux/freezer.h>
+#include <linux/iopoll.h>
#include <linux/reset.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include "../host/xhci.h"
#define DWC3_ROCKCHIP_AUTOSUSPEND_DELAY 500 /* ms */
+#define PERIPHERAL_DISCONNECT_TIMEOUT 1000000 /* us */
struct dwc3_rockchip {
int num_clocks;
bool connected;
+ bool skip_suspend;
bool suspended;
struct device *dev;
struct clk **clks;
struct xhci_hcd *xhci;
unsigned long flags;
int ret;
+ int val;
u32 reg, count;
mutex_lock(&rockchip->lock);
* the dwc3 core code could at least in theory access chip
* registers while the reset is asserted, with unknown impact.
*/
- reset_control_assert(rockchip->otg_rst);
- usleep_range(1000, 1200);
- reset_control_deassert(rockchip->otg_rst);
-
- pm_runtime_get_sync(rockchip->dev);
- pm_runtime_get_sync(dwc->dev);
+ if (!rockchip->skip_suspend) {
+ reset_control_assert(rockchip->otg_rst);
+ usleep_range(1000, 1200);
+ reset_control_deassert(rockchip->otg_rst);
+
+ pm_runtime_get_sync(rockchip->dev);
+ pm_runtime_get_sync(dwc->dev);
+ } else {
+ rockchip->skip_suspend = false;
+ }
spin_lock_irqsave(&dwc->lock, flags);
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
if (rockchip->connected)
goto out;
+ if (rockchip->skip_suspend) {
+ pm_runtime_put(dwc->dev);
+ pm_runtime_put(rockchip->dev);
+ rockchip->skip_suspend = false;
+ }
+
/*
* If dr_mode is device only, never to
* set the mode to the host mode.
phy_power_off(dwc->usb3_generic_phy);
}
- pm_runtime_put_sync(rockchip->dev);
- pm_runtime_put_sync_suspend(dwc->dev);
+ if (DWC3_GCTL_PRTCAP(reg) == DWC3_GCTL_PRTCAP_DEVICE) {
+ ret = readx_poll_timeout(atomic_read,
+ &dwc->dev->power.usage_count,
+ val,
+ val < 2 && !dwc->connected,
+ 1000,
+ PERIPHERAL_DISCONNECT_TIMEOUT);
+ if (ret < 0) {
+ rockchip->skip_suspend = true;
+ dev_warn(rockchip->dev, "Peripheral disconnect timeout\n");
+ }
+ }
+
+ if (!rockchip->skip_suspend) {
+ pm_runtime_put_sync_suspend(dwc->dev);
+ pm_runtime_put_sync_suspend(rockchip->dev);
+ }
rockchip->connected = false;
dev_info(rockchip->dev, "USB unconnected\n");