usb: dwc3: Add changes to support dual-role switching
This patch is a squash of several commits from msm-3.18 that add support for dual-role switching (formerly known as "OTG") to the DWC3 and XHCI platform drivers. Based on the following commits: usb: dwc3: Introduce OTG driver for dwc3 usb: dwc3-msm: Add support for LPM on cable disconnect DWC3: Enable XHCI host in OTG mode USB: dwc3: Add support for host bus suspend usb: dwc3 / xhci_plat: Call xhci_suspend/resume when entering/exiting LPM USB: dwc3: gadget: Implement gadget_vbus_draw() API USB: gadget: dwc3: Fix composition switch issue during cable disconnect usb: dwc3: notify gadget disconnect upon VBUS low usb: dwc3: msm: Remove last of dwc3_otg dwc3: Reset USB controller/PHY after psy connect indication at bootup dwc3: Use otg_sm_work state machine for host and device only mode Signed-off-by: Jack Pham <jackp@codeaurora.org>
This commit is contained in:
parent
92f418332c
commit
7b131410f5
5 changed files with 312 additions and 143 deletions
|
@ -822,38 +822,16 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
|
|||
static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
int ret;
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
|
||||
|
@ -882,6 +860,13 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
|
|||
}
|
||||
}
|
||||
|
||||
/* XHCI reset, resets other CORE registers as well, re-init those */
|
||||
void dwc3_post_host_reset_core_init(struct dwc3 *dwc)
|
||||
{
|
||||
dwc3_core_init(dwc);
|
||||
dwc3_gadget_restart(dwc);
|
||||
}
|
||||
|
||||
static void (*notify_event) (struct dwc3 *, unsigned);
|
||||
void dwc3_set_notifier(void (*notify)(struct dwc3 *, unsigned))
|
||||
{
|
||||
|
@ -902,6 +887,69 @@ int dwc3_notify_event(struct dwc3 *dwc, unsigned event)
|
|||
}
|
||||
EXPORT_SYMBOL(dwc3_notify_event);
|
||||
|
||||
int dwc3_core_pre_init(struct dwc3 *dwc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
ret = dwc3_phy_setup(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
if (!dwc->ev_buffs) {
|
||||
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to allocate event buffers\n");
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to initialize core\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err3;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err4;
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err5;
|
||||
}
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to set mode with dwc3 core\n");
|
||||
goto err6;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err6:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
err5:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
err4:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
err3:
|
||||
dwc3_core_exit(dwc);
|
||||
err2:
|
||||
dwc3_free_event_buffers(dwc);
|
||||
err1:
|
||||
dwc3_ulpi_exit(dwc);
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define DWC3_ALIGN_MASK (16 - 1)
|
||||
|
||||
static int dwc3_probe(struct platform_device *pdev)
|
||||
|
@ -1086,12 +1134,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
|
||||
init_waitqueue_head(&dwc->wait_linkstate);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
ret = dwc3_phy_setup(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
ret = dwc3_core_get_phy(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
@ -1104,85 +1146,64 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
|
||||
}
|
||||
|
||||
pm_runtime_no_callbacks(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to allocate event buffers\n");
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
|
||||
dwc->dr_mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
|
||||
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
|
||||
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) {
|
||||
dwc->dr_mode = USB_DR_MODE_OTG;
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
goto err1;
|
||||
dwc->is_drd = true;
|
||||
}
|
||||
|
||||
/* Adjust Frame Length */
|
||||
dwc3_frame_length_adjustment(dwc, fladj);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err2;
|
||||
/* Hardcode number of eps */
|
||||
dwc->num_in_eps = 16;
|
||||
dwc->num_out_eps = 16;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err3;
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (dwc->dr_mode == USB_DR_MODE_OTG ||
|
||||
dwc->dr_mode == USB_DR_MODE_PERIPHERAL) {
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err4;
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
goto err0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
goto err5;
|
||||
if (dwc->dr_mode == USB_DR_MODE_OTG ||
|
||||
dwc->dr_mode == USB_DR_MODE_HOST) {
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
goto err_gadget;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dwc3_debugfs_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize debugfs\n");
|
||||
goto err6;
|
||||
goto err_host;
|
||||
}
|
||||
|
||||
pm_runtime_allow(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err6:
|
||||
dwc3_core_exit_mode(dwc);
|
||||
|
||||
err5:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
err4:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
err3:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
||||
err2:
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
dwc3_core_exit(dwc);
|
||||
|
||||
err1:
|
||||
dwc3_free_event_buffers(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
err_host:
|
||||
if (dwc->dr_mode == USB_DR_MODE_OTG ||
|
||||
dwc->dr_mode == USB_DR_MODE_HOST)
|
||||
dwc3_host_exit(dwc);
|
||||
err_gadget:
|
||||
if (dwc->dr_mode == USB_DR_MODE_OTG ||
|
||||
dwc->dr_mode == USB_DR_MODE_PERIPHERAL)
|
||||
dwc3_gadget_exit(dwc);
|
||||
err0:
|
||||
/*
|
||||
* restore res->start back to its original value so that, in case the
|
||||
|
|
|
@ -716,7 +716,8 @@ struct dwc3_scratchpad_array {
|
|||
#define DWC3_CORE_PM_RESUME_EVENT 4
|
||||
#define DWC3_CONTROLLER_CONNDONE_EVENT 5
|
||||
#define DWC3_CONTROLLER_NOTIFY_OTG_EVENT 6
|
||||
#define DWC3_CONTROLLER_RESTART_USB_SESSION 10
|
||||
#define DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT 7
|
||||
#define DWC3_CONTROLLER_RESTART_USB_SESSION 8
|
||||
|
||||
#define MAX_INTR_STATS 10
|
||||
/**
|
||||
|
@ -807,6 +808,7 @@ struct dwc3_scratchpad_array {
|
|||
* 1 - -3.5dB de-emphasis
|
||||
* 2 - No de-emphasis
|
||||
* 3 - Reserved
|
||||
* @is_drd: device supports dual-role or not
|
||||
* @err_evt_seen: previous event in queue was erratic error
|
||||
* @usb3_u1u2_disable: if true, disable U1U2 low power modes in Superspeed mode.
|
||||
* @in_lpm: indicates if controller is in low power mode (no clocks)
|
||||
|
@ -818,6 +820,7 @@ struct dwc3_scratchpad_array {
|
|||
* @bh_handled_evt_cnt: no. of events handled by tasklet per interrupt
|
||||
* @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt
|
||||
* @wait_linkstate: waitqueue for waiting LINK to move into required state
|
||||
* @vbus_draw: current to be drawn from USB
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct usb_ctrlrequest *ctrl_req;
|
||||
|
@ -965,6 +968,11 @@ struct dwc3 {
|
|||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
||||
unsigned is_drd:1;
|
||||
/* Indicate if the gadget was powered by the otg driver */
|
||||
unsigned vbus_active:1;
|
||||
/* Indicate if software connect was issued by the usb_gadget_driver */
|
||||
unsigned softconnect:1;
|
||||
unsigned nominal_elastic_buffer:1;
|
||||
unsigned err_evt_seen:1;
|
||||
unsigned usb3_u1u2_disable:1;
|
||||
|
@ -977,6 +985,7 @@ struct dwc3 {
|
|||
atomic_t in_lpm;
|
||||
int tx_fifo_size;
|
||||
bool b_suspend;
|
||||
unsigned vbus_draw;
|
||||
|
||||
/* IRQ timing statistics */
|
||||
int irq;
|
||||
|
@ -1156,6 +1165,7 @@ static inline void dwc3_host_exit(struct dwc3 *dwc)
|
|||
#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
int dwc3_gadget_init(struct dwc3 *dwc);
|
||||
void dwc3_gadget_exit(struct dwc3 *dwc);
|
||||
void dwc3_gadget_restart(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
|
||||
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
|
@ -1167,6 +1177,8 @@ static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
{ return 0; }
|
||||
static inline void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{ }
|
||||
static inline void dwc3_gadget_restart(struct dwc3 *dwc)
|
||||
{ }
|
||||
static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
|
||||
{ return 0; }
|
||||
static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc)
|
||||
|
@ -1212,6 +1224,8 @@ static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
|
|||
|
||||
|
||||
int dwc3_core_init(struct dwc3 *dwc);
|
||||
int dwc3_core_pre_init(struct dwc3 *dwc);
|
||||
void dwc3_post_host_reset_core_init(struct dwc3 *dwc);
|
||||
int dwc3_event_buffers_setup(struct dwc3 *dwc);
|
||||
void dwc3_usb3_phy_suspend(struct dwc3 *dwc, int suspend);
|
||||
|
||||
|
|
|
@ -1808,6 +1808,16 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned mA)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
|
||||
dwc->vbus_draw = mA;
|
||||
dev_dbg(dwc->dev, "Notify controller from %s. mA = %d\n", __func__, mA);
|
||||
dwc3_notify_event(dwc, DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
|
@ -1816,6 +1826,16 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
|||
|
||||
is_on = !!is_on;
|
||||
|
||||
dwc->softconnect = is_on;
|
||||
|
||||
if ((dwc->is_drd && !dwc->vbus_active) || !dwc->gadget_driver) {
|
||||
/*
|
||||
* Need to wait for vbus_session(on) from otg driver or to
|
||||
* the udc_start.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(dwc->dev);
|
||||
dbg_event(0xFF, "Pullup gsync",
|
||||
atomic_read(&dwc->dev->power.usage_count));
|
||||
|
@ -1875,37 +1895,57 @@ void dwc3_gadget_disable_irq(struct dwc3 *dwc)
|
|||
|
||||
static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
|
||||
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
|
||||
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc);
|
||||
|
||||
static int dwc3_gadget_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
struct dwc3_ep *dep;
|
||||
struct dwc3 *dwc = gadget_to_dwc(_gadget);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
int irq;
|
||||
u32 reg;
|
||||
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
dwc->irq = irq;
|
||||
ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, "dwc3", dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
|
||||
irq, ret);
|
||||
goto err0;
|
||||
}
|
||||
if (!dwc->is_drd)
|
||||
return -EPERM;
|
||||
|
||||
is_active = !!is_active;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
if (dwc->gadget_driver) {
|
||||
dev_err(dwc->dev, "%s is already bound to %s\n",
|
||||
dwc->gadget.name,
|
||||
dwc->gadget_driver->driver.name);
|
||||
ret = -EBUSY;
|
||||
goto err1;
|
||||
/* Mark that the vbus was powered */
|
||||
dwc->vbus_active = is_active;
|
||||
|
||||
/*
|
||||
* Check if upper level usb_gadget_driver was already registerd with
|
||||
* this udc controller driver (if dwc3_gadget_start was called)
|
||||
*/
|
||||
if (dwc->gadget_driver && dwc->softconnect) {
|
||||
if (dwc->vbus_active) {
|
||||
/*
|
||||
* Both vbus was activated by otg and pullup was
|
||||
* signaled by the gadget driver.
|
||||
*/
|
||||
dwc3_gadget_run_stop(dwc, 1, false);
|
||||
} else {
|
||||
dwc3_gadget_run_stop(dwc, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
dwc->gadget_driver = driver;
|
||||
/*
|
||||
* Clearing run/stop bit might occur before disconnect event is seen.
|
||||
* Make sure to let gadget driver know in that case.
|
||||
*/
|
||||
if (!dwc->vbus_active) {
|
||||
dev_dbg(dwc->dev, "calling disconnect from %s\n", __func__);
|
||||
dwc3_gadget_disconnect_interrupt(dwc);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_start(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_SPEED_MASK);
|
||||
|
@ -1959,20 +1999,27 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
/* Start with SuperSpeed Default */
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
dwc->delayed_status = false;
|
||||
/* reinitialize physical ep0-1 */
|
||||
dep = dwc->eps[0];
|
||||
dep->flags = 0;
|
||||
dep->endpoint.maxburst = 1;
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
dep->flags = 0;
|
||||
dep->endpoint.maxburst = 1;
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err3;
|
||||
__dwc3_gadget_ep_disable(dwc->eps[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* begin to receive SETUP packets */
|
||||
|
@ -1981,19 +2028,55 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
|
||||
dwc3_gadget_enable_irq(dwc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Required gadget re-initialization before switching to gadget in OTG mode */
|
||||
void dwc3_gadget_restart(struct dwc3 *dwc)
|
||||
{
|
||||
__dwc3_gadget_start(dwc);
|
||||
}
|
||||
|
||||
static int dwc3_gadget_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
dwc->irq = irq;
|
||||
ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, "dwc3", dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
|
||||
irq, ret);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
if (dwc->gadget_driver) {
|
||||
dev_err(dwc->dev, "%s is already bound to %s\n",
|
||||
dwc->gadget.name,
|
||||
dwc->gadget_driver->driver.name);
|
||||
ret = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
dwc->gadget_driver = driver;
|
||||
|
||||
/*
|
||||
* For DRD, this might get called by gadget driver during bootup
|
||||
* even though host mode might be active. Don't actually perform
|
||||
* device-specific initialization until device mode is activated.
|
||||
* In that case dwc3_gadget_restart() will handle it.
|
||||
*/
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
__dwc3_gadget_ep_disable(dwc->eps[0]);
|
||||
|
||||
err2:
|
||||
dwc->gadget_driver = NULL;
|
||||
|
||||
err1:
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
free_irq(irq, dwc);
|
||||
|
||||
err0:
|
||||
|
@ -2037,6 +2120,8 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
|
|||
.wakeup = dwc3_gadget_wakeup,
|
||||
.func_wakeup = dwc_gadget_func_wakeup,
|
||||
.set_selfpowered = dwc3_gadget_set_selfpowered,
|
||||
.vbus_session = dwc3_gadget_vbus_session,
|
||||
.vbus_draw = dwc3_gadget_vbus_draw,
|
||||
.pullup = dwc3_gadget_pullup,
|
||||
.udc_start = dwc3_gadget_start,
|
||||
.udc_stop = dwc3_gadget_stop,
|
||||
|
@ -2646,6 +2731,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT);
|
||||
|
||||
dwc3_usb3_phy_suspend(dwc, false);
|
||||
usb_gadget_vbus_draw(&dwc->gadget, 0);
|
||||
|
||||
dwc3_reset_gadget(dwc);
|
||||
dbg_event(0xFF, "BUS RST", 0);
|
||||
|
@ -3114,6 +3200,12 @@ static void dwc3_process_event_entry(struct dwc3 *dwc,
|
|||
{
|
||||
trace_dwc3_event(event->raw);
|
||||
|
||||
/* skip event processing in absence of vbus */
|
||||
if (!dwc->vbus_active) {
|
||||
dbg_print_reg("SKIP EVT", event->raw);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If run/stop is cleared don't process any more events */
|
||||
if (!dwc->pullups_connected) {
|
||||
dbg_print_reg("SKIP_EVT_PULLUP", event->raw);
|
||||
|
@ -3399,6 +3491,13 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
goto err4;
|
||||
}
|
||||
|
||||
if (!dwc->is_drd) {
|
||||
pm_runtime_no_callbacks(&dwc->gadget.dev);
|
||||
pm_runtime_set_active(&dwc->gadget.dev);
|
||||
pm_runtime_enable(&dwc->gadget.dev);
|
||||
pm_runtime_get(&dwc->gadget.dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
|
@ -3425,6 +3524,11 @@ err0:
|
|||
|
||||
void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->is_drd) {
|
||||
pm_runtime_put(&dwc->gadget.dev);
|
||||
pm_runtime_disable(&dwc->gadget.dev);
|
||||
}
|
||||
|
||||
usb_del_gadget_udc(&dwc->gadget);
|
||||
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
|
|
@ -62,18 +62,9 @@ int dwc3_host_init(struct dwc3 *dwc)
|
|||
phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
||||
dev_name(&xhci->dev));
|
||||
|
||||
ret = platform_device_add(xhci);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register xHCI device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
/* Platform device gets added as part of state machine */
|
||||
return 0;
|
||||
err2:
|
||||
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
||||
dev_name(&xhci->dev));
|
||||
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
||||
dev_name(&xhci->dev));
|
||||
|
||||
err1:
|
||||
platform_device_put(xhci);
|
||||
return ret;
|
||||
|
@ -85,5 +76,6 @@ void dwc3_host_exit(struct dwc3 *dwc)
|
|||
dev_name(&dwc->xhci->dev));
|
||||
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
||||
dev_name(&dwc->xhci->dev));
|
||||
if (!dwc->is_drd)
|
||||
platform_device_unregister(dwc->xhci);
|
||||
}
|
||||
|
|
|
@ -134,6 +134,15 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
|||
goto put_hcd;
|
||||
}
|
||||
|
||||
if (pdev->dev.parent)
|
||||
pm_runtime_resume(pdev->dev.parent);
|
||||
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"marvell,armada-375-xhci") ||
|
||||
of_device_is_compatible(pdev->dev.of_node,
|
||||
|
@ -182,6 +191,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto dealloc_usb2_hcd;
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
|
@ -210,6 +222,8 @@ static int xhci_plat_remove(struct platform_device *dev)
|
|||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct clk *clk = xhci->clk;
|
||||
|
||||
pm_runtime_disable(&dev->dev);
|
||||
|
||||
usb_remove_hcd(xhci->shared_hcd);
|
||||
usb_phy_shutdown(hcd->usb_phy);
|
||||
|
||||
|
@ -223,33 +237,57 @@ static int xhci_plat_remove(struct platform_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int xhci_plat_suspend(struct device *dev)
|
||||
#ifdef CONFIG_PM
|
||||
static int xhci_plat_runtime_idle(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
||||
/*
|
||||
* xhci_suspend() needs `do_wakeup` to know whether host is allowed
|
||||
* to do wakeup during suspend. Since xhci_plat_suspend is currently
|
||||
* only designed for system suspend, device_may_wakeup() is enough
|
||||
* to dertermine whether host is allowed to do wakeup. Need to
|
||||
* reconsider this when xhci_plat_suspend enlarges its scope, e.g.,
|
||||
* also applies to runtime suspend.
|
||||
* When pm_runtime_put_autosuspend() is called on this device,
|
||||
* after this idle callback returns the PM core will schedule the
|
||||
* autosuspend if there is any remaining time until expiry. However,
|
||||
* when reaching this point because the child_count becomes 0, the
|
||||
* core does not honor autosuspend in that case and results in
|
||||
* idle/suspend happening immediately. In order to have a delay
|
||||
* before suspend we have to call pm_runtime_autosuspend() manually.
|
||||
*/
|
||||
return xhci_suspend(xhci, device_may_wakeup(dev));
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_autosuspend(dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int xhci_plat_resume(struct device *dev)
|
||||
static int xhci_plat_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
||||
return xhci_resume(xhci, 0);
|
||||
if (!xhci)
|
||||
return 0;
|
||||
|
||||
dev_dbg(dev, "xhci-plat runtime suspend\n");
|
||||
|
||||
return xhci_suspend(xhci, true);
|
||||
}
|
||||
|
||||
static int xhci_plat_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
int ret;
|
||||
|
||||
if (!xhci)
|
||||
return 0;
|
||||
|
||||
dev_dbg(dev, "xhci-plat runtime resume\n");
|
||||
|
||||
ret = xhci_resume(xhci, false);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops xhci_plat_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(NULL, NULL)
|
||||
SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume,
|
||||
xhci_plat_runtime_idle)
|
||||
};
|
||||
#define DEV_PM_OPS (&xhci_plat_pm_ops)
|
||||
#else
|
||||
|
|
Loading…
Add table
Reference in a new issue