mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-26 06:33:19 -04:00
Merge "defconfig: sdxlemur: Enable USB function remote wakeup functionality"
This commit is contained in:
commit
f291b2fb17
6 changed files with 181 additions and 27 deletions
1
arch/arm/configs/vendor/sdxlemur.config
vendored
1
arch/arm/configs/vendor/sdxlemur.config
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
Loading…
Reference in a new issue