.hot_plug = false,
.remote_wakeup_supported = true,
.power_off_on_suspend = true,
+ .skip_resume = true,
},
};
.hot_plug = false,
.remote_wakeup_supported = true,
.power_off_on_suspend = true,
+ .skip_resume = true,
},
};
{
struct usb_device *udev = to_usb_device(dev);
+ if (udev->bus->skip_resume) {
+ if (udev->state == USB_STATE_SUSPENDED)
+ return 0;
+ }
unbind_no_pm_drivers_interfaces(udev);
/* From now on we are sure all drivers support suspend/resume
struct usb_device *udev = to_usb_device(dev);
int status;
+ if (udev->bus->skip_resume)
+ return 0;
+
/* For all calls, take the device back to full power and
* tell the PM core in case it was autosuspended previously.
* Unbind the interfaces that will need rebinding later,
struct delayed_work boost_cpu_freq_work;
struct pm_qos_request boost_cpu_freq_req;
#endif
+ bool is_skip_resume_enabled;
};
struct dma_align_buffer {
ehci_dbg(ehci, "pmc interrupt detected\n");
wake_lock_timeout(&tegra->ehci_wake_lock, HZ);
usb_hcd_resume_root_hub(hcd);
+ hcd_to_bus(hcd)->skip_resume = false;
spin_unlock(&ehci->lock);
return irq_status;
}
err = ehci_bus_suspend(hcd);
if (err)
tegra->bus_suspended_fail = true;
- else
+ else {
usb_phy_set_suspend(get_usb_phy(tegra->phy), 1);
+ hcd_to_bus(hcd)->skip_resume = true;
+ }
mutex_unlock(&tegra->sync_lock);
EHCI_DBG("%s() END\n", __func__);
tegra->ehci = hcd_to_ehci(hcd);
+ hcd_to_bus(hcd)->skip_resume = pdata->u_data.host.skip_resume;
+ tegra->is_skip_resume_enabled = pdata->u_data.host.skip_resume;
if (pdata->port_otg) {
tegra->transceiver =
devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
int err = 0;
struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev);
struct tegra_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci);
+
if (tegra->irq) {
err = disable_irq_wake(tegra->irq);
if (err < 0)
"Couldn't disable USB host mode wakeup, irq=%d, "
"error=%d\n", tegra->irq, err);
}
+
+ if (tegra->is_skip_resume_enabled) {
+ if (tegra_usb_phy_is_pmc_wake(tegra->phy))
+ hcd_to_bus(hcd)->skip_resume = false;
+ }
+
if (pdata->u_data.host.turn_off_vbus_on_lp0) {
tegra_usb_enable_vbus(tegra->phy, true);
tegra_ehci_notify_event(tegra, USB_EVENT_ID);
}
- return tegra_usb_phy_power_on(tegra->phy);
+ if (tegra->is_skip_resume_enabled)
+ return 0;
+ else
+ return tegra_usb_phy_power_on(tegra->phy);
+
}
static int tegra_ehci_suspend(struct platform_device *pdev, pm_message_t state)
if (tegra->bus_suspended_fail)
return -EBUSY;
else {
- err = tegra_usb_phy_power_off(tegra->phy);
- if (err < 0)
- return err;
- if (pdata->u_data.host.turn_off_vbus_on_lp0) {
- tegra_usb_enable_vbus(tegra->phy, false);
- tegra_usb_phy_pmc_disable(tegra->phy);
+ if (!tegra->is_skip_resume_enabled) {
+ err = tegra_usb_phy_power_off(tegra->phy);
+ if (err < 0)
+ return err;
+ if (pdata->u_data.host.turn_off_vbus_on_lp0) {
+ tegra_usb_enable_vbus(tegra->phy, false);
+ tegra_usb_phy_pmc_disable(tegra->phy);
+ }
}
if (tegra->irq) {
err = enable_irq_wake(tegra->irq);
phy->ops->pmc_disable(phy);
}
EXPORT_SYMBOL_GPL(tegra_usb_phy_pmc_disable);
+
+bool tegra_usb_phy_is_pmc_wake(struct tegra_usb_phy *phy)
+{
+ bool status = 0;
+ if (phy->ops && phy->ops->is_pmc_wakeup)
+ status = phy->ops->is_pmc_wakeup(phy);
+ return status;
+}
+EXPORT_SYMBOL_GPL(tegra_usb_phy_is_pmc_wake);
pmc->pmc_ops->powerdown_pmc_wake_detect(pmc);
}
}
+
+static int utmi_phy_is_pmc_wakeup(struct tegra_usb_phy *phy)
+{
+ u32 val;
+ int inst = phy->inst;
+ void __iomem *base = phy->regs;
+ val = tegra_usb_pmc_reg_read(UTMIP_STATUS);
+ if (UTMIP_WALK_PTR_VAL(inst) & val)
+ return 1;
+ else {
+ val = readl(base + UTMIP_PMC_WAKEUP0);
+ val |= EVENT_INT_ENB;
+ writel(val, base + UTMIP_PMC_WAKEUP0);
+ return 0;
+ }
+}
+
static bool utmi_phy_nv_charger_detect(struct tegra_usb_phy *phy)
{
int status1;
return 0;
}
+static int uhsic_phy_is_pmc_wakeup(struct tegra_usb_phy *phy)
+{
+ u32 val;
+ int inst = phy->inst;
+ void __iomem *base = phy->regs;
+
+ val = tegra_usb_pmc_reg_read(UHSIC_STATUS(inst));
+ /* check whether we wake up from the remote resume */
+ if (UHSIC_WALK_PTR_VAL(inst) & val)
+ return 1;
+ else {
+ val = readl(base + UHSIC_PMC_WAKEUP0);
+ val |= EVENT_INT_ENB;
+ writel(val, base + UHSIC_PMC_WAKEUP0);
+ return 0;
+ }
+}
+
static struct tegra_usb_phy_ops utmi_phy_ops = {
.init = _usb_phy_init,
.reset = usb_phy_reset,
.apple_charger_2000ma_detect = utmi_phy_apple_charger_2000ma_detect,
.apple_charger_500ma_detect = utmi_phy_apple_charger_500ma_detect,
.pmc_disable = utmi_phy_pmc_disable,
+ .is_pmc_wakeup = utmi_phy_is_pmc_wakeup,
};
static struct tegra_usb_phy_ops uhsic_phy_ops = {
.power_on = uhsic_phy_power_on,
.power_off = uhsic_phy_power_off,
.port_power = uhsic_phy_bus_port_power,
+ .is_pmc_wakeup = uhsic_phy_is_pmc_wakeup,
};
static struct tegra_usb_phy_ops *phy_ops[] = {
bool (*apple_charger_2000ma_detect)(struct tegra_usb_phy *phy);
bool (*apple_charger_500ma_detect)(struct tegra_usb_phy *phy);
void (*pmc_disable) (struct tegra_usb_phy *phy);
+ bool (*is_pmc_wakeup)(struct tegra_usb_phy *phy);
};
/**
bool power_off_on_suspend;
bool turn_off_vbus_on_lp0;
bool support_y_cable;
+ bool skip_resume;
};
/**
struct mon_bus *mon_bus; /* non-null when associated */
int monitored; /* non-zero when monitored */
#endif
+ bool skip_resume;
};
/* ----------------------------------------------------------------------- */
*/
bool tegra_usb_phy_pmc_wakeup(struct tegra_usb_phy *phy);
+bool tegra_usb_phy_is_pmc_wake(struct tegra_usb_phy *phy);
+
void tegra_usb_phy_memory_prefetch_on(struct tegra_usb_phy *phy);
void tegra_usb_phy_memory_prefetch_off(struct tegra_usb_phy *phy);