Merge "defconfig: sdxlemur: Enable USB function remote wakeup functionality"

This commit is contained in:
qctecmdr 2020-12-20 00:48:55 -08:00 • committed by Gerrit - the friendly Code Review server
commit f291b2fb17
6 changed files with 181 additions and 27 deletions

View file

@ -179,6 +179,7 @@ CONFIG_QTI_TZ_LOG=y
CONFIG_SLAB_FREELIST_RANDOM=y
CONFIG_QCOM_SOCINFO=y
CONFIG_USB_DWC3=y
CONFIG_USB_FUNC_WAKEUP_SUPPORTED=y
CONFIG_USB_DWC3_MSM=y
CONFIG_USB_QCOM_EMU_PHY=y
CONFIG_USB_MSM_SSPHY_QMP=y

View file

@ -1622,6 +1622,7 @@ static int dwc3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dwc);
init_waitqueue_head(&dwc->wait_linkstate);
spin_lock_init(&dwc->lock);
pm_runtime_no_callbacks(dev);

View file

@ -1151,6 +1151,8 @@ struct dwc3_scratchpad_array {
* @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt
* @last_run_stop: timestamp denoting the last run_stop update
* @is_remote_wakeup_enabled: remote wakeup status from host perspective
* @wait_linkstate: waitqueue for waiting LINK to move into required state
* @remote_wakeup_work: use to perform remote wakeup from this context
*/
struct dwc3 {
struct work_struct drd_work;
@ -1397,6 +1399,8 @@ struct dwc3 {
u32 gen2_tx_de_emph3;
ktime_t last_run_stop;
bool is_remote_wakeup_enabled;
wait_queue_head_t wait_linkstate;
struct work_struct remote_wakeup_work;
};
#define INCRX_BURST_MODE 0

View file

@ -30,6 +30,8 @@
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
static int dwc3_ep0_delegate_req(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl);
static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
dma_addr_t buf_dma, u32 len, u32 type, bool chain)
@ -352,7 +354,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
* Function Remote Wake Capable D0
* Function Remote Wakeup D1
*/
break;
return dwc3_ep0_delegate_req(dwc, ctrl);
case USB_RECIP_ENDPOINT:
dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
@ -507,6 +509,9 @@ static int dwc3_ep0_handle_intf(struct dwc3 *dwc,
* For now, we're not doing anything, just making sure we return
* 0 so USB Command Verifier tests pass without any errors.
*/
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (ret)
return ret;
break;
default:
ret = -EINVAL;

View file

@ -32,6 +32,7 @@
static int __dwc3_gadget_start(struct dwc3 *dwc);
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc);
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, bool remote_wakeup);
/**
* dwc3_gadget_set_test_mode - enables usb2 test modes
@ -1924,15 +1925,17 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
return __dwc3_gadget_get_frame(dwc);
}
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
static int dwc3_gadget_remote_wakeup(struct dwc3 *dwc)
{
int retries;
int ret;
int ret = 0;
u32 reg;
u8 link_state;
unsigned long flags;
bool link_recover_only = false;
dev_dbg(dwc->dev, "%s(): Entry\n", __func__);
disable_irq(dwc->irq);
spin_lock_irqsave(&dwc->lock, flags);
/*
* According to the Databook Remote wakeup request should
* be issued only when the device is in early suspend state.
@ -1942,21 +1945,45 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
link_state = DWC3_DSTS_USBLNKST(reg);
switch (link_state) {
case DWC3_LINK_STATE_RESET:
case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
case DWC3_LINK_STATE_RESUME:
break;
case DWC3_LINK_STATE_U1:
if (dwc->gadget.speed != USB_SPEED_SUPER) {
link_recover_only = true;
break;
}
fallthrough;
default:
return -EINVAL;
dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
link_state);
ret = -EINVAL;
goto out;
}
/* Enable LINK STATUS change event */
reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
reg |= DWC3_DEVTEN_ULSTCNGEN;
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
/*
* memory barrier is required to make sure that required events
* with core is enabled before performing RECOVERY mechnism.
*/
mb();
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
if (ret < 0) {
dev_err(dwc->dev, "failed to put link in Recovery\n");
return ret;
/* Disable LINK STATUS change */
reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
reg &= ~DWC3_DEVTEN_ULSTCNGEN;
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
/* Required to complete this operation before returning */
mb();
goto out;
}
/* Recent versions do this automatically */
@ -1967,30 +1994,115 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
}
/* poll until Link State changes to ON */
retries = 20000;
spin_unlock_irqrestore(&dwc->lock, flags);
enable_irq(dwc->irq);
while (retries--) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
/*
* Have bigger value (16 sec) for timeout since some host PCs driving
* resume for very long time (e.g. 8 sec)
*/
ret = wait_event_interruptible_timeout(dwc->wait_linkstate,
(dwc->link_state < DWC3_LINK_STATE_U3) ||
(dwc->link_state == DWC3_LINK_STATE_SS_DIS),
msecs_to_jiffies(16000));
/* in HS, means ON */
if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
break;
spin_lock_irqsave(&dwc->lock, flags);
/* Disable link status change event */
reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
reg &= ~DWC3_DEVTEN_ULSTCNGEN;
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
/*
* Complete this write before we go ahead and perform resume
* as we don't need link status change notificaiton anymore.
*/
mb();
if (!ret) {
dev_dbg(dwc->dev, "Timeout moving into state(%d)\n",
dwc->link_state);
ret = -EINVAL;
spin_unlock_irqrestore(&dwc->lock, flags);
goto out1;
} else {
ret = 0;
/*
* If USB is disconnected OR received RESET from host,
* don't perform resume
*/
if (dwc->link_state == DWC3_LINK_STATE_SS_DIS ||
dwc->gadget.state == USB_STATE_DEFAULT)
link_recover_only = true;
}
if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
dev_err(dwc->dev, "failed to send remote wakeup\n");
return -EINVAL;
}
/*
* According to DWC3 databook, the controller does not
* trigger a wakeup event when remote-wakeup is used.
* Hence, after remote-wakeup sequence is complete, and
* the device is back at U0 state, it is required that
* the resume sequence is initiated by SW.
*/
if (!link_recover_only)
dwc3_gadget_wakeup_interrupt(dwc, true);
return 0;
spin_unlock_irqrestore(&dwc->lock, flags);
dev_dbg(dwc->dev, "%s: Exit\n", __func__);
return ret;
out:
spin_unlock_irqrestore(&dwc->lock, flags);
enable_irq(dwc->irq);
out1:
return ret;
}
#define DWC3_PM_RESUME_RETRIES 20 /* Max Number of retries */
#define DWC3_PM_RESUME_DELAY 100 /* 100 msec */
static void dwc3_gadget_remote_wakeup_work(struct work_struct *w)
{
struct dwc3 *dwc;
int ret;
static int retry_count;
dwc = container_of(w, struct dwc3, remote_wakeup_work);
ret = pm_runtime_get_sync(dwc->dev);
if (ret) {
/* pm_runtime_get_sync returns -EACCES error between
* late_suspend and early_resume, wait for system resume to
* finish and queue work again
*/
dev_dbg(dwc->dev, "PM runtime get sync failed, ret %d\n", ret);
if (ret == -EACCES) {
pm_runtime_put_noidle(dwc->dev);
if (retry_count == DWC3_PM_RESUME_RETRIES) {
retry_count = 0;
dev_err(dwc->dev, "pm_runtime_get_sync timed out\n");
return;
}
msleep(DWC3_PM_RESUME_DELAY);
retry_count++;
schedule_work(&dwc->remote_wakeup_work);
return;
}
}
retry_count = 0;
dbg_event(0xFF, "Gdgwake gsyn",
atomic_read(&dwc->dev->power.usage_count));
ret = dwc3_gadget_remote_wakeup(dwc);
if (ret)
dev_err(dwc->dev, "Remote wakeup failed. ret = %d\n", ret);
pm_runtime_put_noidle(dwc->dev);
dbg_event(0xFF, "Gdgwake put",
atomic_read(&dwc->dev->power.usage_count));
}
static int dwc3_gadget_wakeup(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
int ret;
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
if (!dwc->is_remote_wakeup_enabled) {
@ -1999,11 +2111,32 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
return -EINVAL;
}
ret = __dwc3_gadget_wakeup(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
schedule_work(&dwc->remote_wakeup_work);
return 0;
}
#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
static int dwc_gadget_func_wakeup(struct usb_gadget *g, int interface_id)
{
int ret = 0;
struct dwc3 *dwc = gadget_to_dwc(g);
if (dwc3_gadget_is_suspended(dwc)) {
dev_dbg(dwc->dev, "USB bus is suspended, scheduling wakeup\n");
dwc3_gadget_wakeup(&dwc->gadget);
return -EAGAIN;
}
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_XMIT_DEV,
0x1 | (interface_id << 4));
if (ret)
dev_err(dwc->dev, "Function wakeup HW command failed, ret %d\n",
ret);
return ret;
}
#endif
static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
int is_selfpowered)
@ -2659,6 +2792,9 @@ static void __maybe_unused dwc3_gadget_set_speed(struct usb_gadget *g,
static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
.func_wakeup = dwc_gadget_func_wakeup,
#endif
.set_selfpowered = dwc3_gadget_set_selfpowered,
.vbus_session = dwc3_gadget_vbus_session,
.vbus_draw = dwc3_gadget_vbus_draw,
@ -3286,6 +3422,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
dwc->connected = false;
wake_up_interruptible(&dwc->wait_linkstate);
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
@ -3371,6 +3508,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->link_state = DWC3_LINK_STATE_U0;
dwc->is_remote_wakeup_enabled = false;
wake_up_interruptible(&dwc->wait_linkstate);
}
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
@ -3503,7 +3641,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
*/
}
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, bool remote_wakeup)
{
enum dwc3_link_state link_state = dwc->link_state;
@ -3511,7 +3649,7 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
dwc->link_state = DWC3_LINK_STATE_U0;
/* For L1 resume case, don't perform resume */
if (link_state != DWC3_LINK_STATE_U3)
if (!remote_wakeup && link_state != DWC3_LINK_STATE_U3)
return;
/* Handle bus resume case */
@ -3617,7 +3755,9 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
}
dev_dbg(dwc->dev, "Going from (%d)--->(%d)\n", dwc->link_state, next);
dbg_log_string("link state from %d to %d", dwc->link_state, next);
dwc->link_state = next;
wake_up_interruptible(&dwc->wait_linkstate);
}
static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
@ -3694,7 +3834,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
dwc->dbg_gadget_events.connect++;
break;
case DWC3_DEVICE_EVENT_WAKEUP:
dwc3_gadget_wakeup_interrupt(dwc);
dwc3_gadget_wakeup_interrupt(dwc, false);
dwc->dbg_gadget_events.wakeup++;
break;
case DWC3_DEVICE_EVENT_HIBER_REQ:
@ -3982,7 +4122,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
}
dwc->irq_gadget = irq;
INIT_WORK(&dwc->remote_wakeup_work, dwc3_gadget_remote_wakeup_work);
dwc->ep0_trb = dma_alloc_coherent(dwc->sysdev,
sizeof(*dwc->ep0_trb) * 2,
&dwc->ep0_trb_addr, GFP_KERNEL);

View file

@ -39,6 +39,9 @@ static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f)
if (f->config->cdev->gadget->speed >= USB_SPEED_SUPER)
remote_wakeup_allowed = gsi->func_wakeup_allowed;
else
remote_wakeup_allowed =
usb_get_remote_wakeup_status(f->config->cdev->gadget);
log_event_dbg("%s: remote_wakeup_allowed:%s", __func__,
(remote_wakeup_allowed ? "true" : "false"));