mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 11:26:02 +09:00
usb: dwc3-rockchip: delay suspend until gadget finish transfer
DWC3 controller still need to read or write memory after dwc3 rockchip receive disconnect notify, the dwc3 controller will not suspend immediately and the clock is still enabled even if we put dwc_rockchip sync. So if we reconnect to PC quickly, the transer will start before dwc3 rockchip receive connect notify and the dwc3 controller will reset during normal transer which result in the gadget function break. In order to suspend dwc3 controller and disable clock when transfer finish immediately, this patch wait for usage_count of dwc3 controller change to 1 and disconnect interrupt occur then suspend dwc3 controller and disable clock, make the dwc3 continue transfer after dwc3 rockchip receive connect notify, otherwise do not reset and get or put dwc3 controller if timeout. Change-Id: If65344557d77370e9b6cf4bfea84175c37f00057 Signed-off-by: Meng Dongyang <daniel.meng@rock-chips.com>
This commit is contained in:
committed by
Huang, Tao
parent
58f09e08b3
commit
78c84e83bb
@@ -28,6 +28,7 @@
|
||||
#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>
|
||||
@@ -37,10 +38,12 @@
|
||||
#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;
|
||||
@@ -87,6 +90,7 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
|
||||
struct xhci_hcd *xhci;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int val;
|
||||
u32 reg, count;
|
||||
|
||||
mutex_lock(&rockchip->lock);
|
||||
@@ -115,12 +119,16 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
|
||||
* 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);
|
||||
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);
|
||||
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);
|
||||
@@ -132,6 +140,12 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
|
||||
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.
|
||||
@@ -269,8 +283,23 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
|
||||
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");
|
||||
|
||||
Reference in New Issue
Block a user