usb: gadget: Add support for USB going into LPM on bus suspend
This change adds the ability of the msm_otg to go to LPM when
USB cable is connected and bus is suspended.
This ability is enabled by a board file parameter.
The msm_otg is notified about SUSPEND/RESUME events via the
UDC layer. New event for RESUME was defined for that.
Change-Id: Ic508f1898cc3b57ab76eccd379bea38ed363570b
Signed-off-by: Amit Blay <ablay@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index 06affd4..67697d2 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -754,6 +754,7 @@
.phy_type = SNPS_28NM_INTEGRATED_PHY,
.vbus_power = msm_hsusb_vbus_power,
.disable_reset_on_disconnect = true,
+ .enable_lpm_on_dev_suspend = true,
};
static struct msm_hsic_peripheral_platform_data msm_hsic_peripheral_pdata = {
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index b612a0b..fd341a7 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -2005,6 +2005,48 @@
}
/**
+ * isr_resume_handler: USB PCI interrupt handler
+ * @udc: UDC device
+ *
+ */
+static void isr_resume_handler(struct ci13xxx *udc)
+{
+ udc->gadget.speed = hw_port_is_high_speed() ?
+ USB_SPEED_HIGH : USB_SPEED_FULL;
+ if (udc->suspended) {
+ spin_unlock(udc->lock);
+ if (udc->transceiver)
+ otg_set_suspend(udc->transceiver, 0);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(udc->lock);
+ udc->suspended = 0;
+ }
+}
+
+/**
+ * isr_resume_handler: USB SLI interrupt handler
+ * @udc: UDC device
+ *
+ */
+static void isr_suspend_handler(struct ci13xxx *udc)
+{
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
+ udc->vbus_active) {
+ if (udc->suspended == 0) {
+ spin_unlock(udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_SUSPEND_EVENT);
+ if (udc->transceiver)
+ otg_set_suspend(udc->transceiver, 1);
+ spin_lock(udc->lock);
+ udc->suspended = 1;
+ }
+ }
+}
+
+/**
* isr_get_status_complete: get_status request complete function
* @ep: endpoint
* @req: request handled
@@ -3174,14 +3216,7 @@
}
if (USBi_PCI & intr) {
isr_statistics.pci++;
- udc->gadget.speed = hw_port_is_high_speed() ?
- USB_SPEED_HIGH : USB_SPEED_FULL;
- if (udc->suspended) {
- spin_unlock(udc->lock);
- udc->driver->resume(&udc->gadget);
- spin_lock(udc->lock);
- udc->suspended = 0;
- }
+ isr_resume_handler(udc);
}
if (USBi_UEI & intr)
isr_statistics.uei++;
@@ -3190,15 +3225,7 @@
isr_tr_complete_handler(udc);
}
if (USBi_SLI & intr) {
- if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
- udc->suspended = 1;
- spin_unlock(udc->lock);
- udc->driver->suspend(&udc->gadget);
- if (udc->udc_driver->notify_event)
- udc->udc_driver->notify_event(udc,
- CI13XXX_CONTROLLER_SUSPEND_EVENT);
- spin_lock(udc->lock);
- }
+ isr_suspend_handler(udc);
isr_statistics.sli++;
}
retval = IRQ_HANDLED;
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 0ff0a48..5b05c5b 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -673,8 +673,7 @@
{
struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
- if (aca_enabled() || (test_bit(ID, &motg->inputs) &&
- !test_bit(ID_A, &motg->inputs)))
+ if (aca_enabled())
return 0;
if (suspend) {
@@ -688,6 +687,14 @@
clear_bit(A_BUS_REQ, &motg->inputs);
queue_work(system_nrt_wq, &motg->sm_work);
break;
+ case OTG_STATE_B_PERIPHERAL:
+ pr_debug("peripheral bus suspend\n");
+ if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
+ break;
+ set_bit(A_BUS_SUSPEND, &motg->inputs);
+ queue_work(system_nrt_wq, &motg->sm_work);
+ break;
+
default:
break;
}
@@ -701,6 +708,13 @@
/* ensure hardware is not in low power mode */
pm_runtime_resume(otg->dev);
break;
+ case OTG_STATE_B_PERIPHERAL:
+ pr_debug("peripheral bus resume\n");
+ if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
+ break;
+ clear_bit(A_BUS_SUSPEND, &motg->inputs);
+ queue_work(system_nrt_wq, &motg->sm_work);
+ break;
default:
break;
}
@@ -718,7 +732,7 @@
struct usb_bus *bus = otg->host;
struct msm_otg_platform_data *pdata = motg->pdata;
int cnt = 0;
- bool host_bus_suspend, dcp;
+ bool host_bus_suspend, device_bus_suspend, dcp;
u32 phy_ctrl_val = 0, cmd_val;
unsigned ret;
u32 portsc;
@@ -728,6 +742,9 @@
disable_irq(motg->irq);
host_bus_suspend = otg->host && !test_bit(ID, &motg->inputs);
+ device_bus_suspend = otg->gadget && test_bit(ID, &motg->inputs) &&
+ test_bit(A_BUS_SUSPEND, &motg->inputs) &&
+ motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
dcp = motg->chg_type == USB_DCP_CHARGER;
/*
* Chipidea 45-nm PHY suspend sequence:
@@ -791,8 +808,8 @@
* PMIC notifications are unavailable.
*/
cmd_val = readl_relaxed(USB_USBCMD);
- if (host_bus_suspend || (motg->pdata->otg_control == OTG_PHY_CONTROL &&
- dcp))
+ if (host_bus_suspend || device_bus_suspend ||
+ (motg->pdata->otg_control == OTG_PHY_CONTROL && dcp))
cmd_val |= ASYNC_INTR_CTRL | ULPI_STP_CTRL;
else
cmd_val |= ULPI_STP_CTRL;
@@ -802,7 +819,8 @@
* BC1.2 spec mandates PD to enable VDP_SRC when charging from DCP.
* PHY retention and collapse can not happen with VDP_SRC enabled.
*/
- if (motg->caps & ALLOW_PHY_RETENTION && !host_bus_suspend && !dcp) {
+ if (motg->caps & ALLOW_PHY_RETENTION && !host_bus_suspend &&
+ !device_bus_suspend && !dcp) {
phy_ctrl_val = readl_relaxed(USB_PHY_CTRL);
if (motg->pdata->otg_control == OTG_PHY_CONTROL)
/* Enable PHY HV interrupts to wake MPM/Link */
@@ -2133,6 +2151,13 @@
*/
otg->host->is_b_host = 1;
msm_otg_start_host(otg, 1);
+ } else if (test_bit(A_BUS_SUSPEND, &motg->inputs) &&
+ test_bit(B_SESS_VLD, &motg->inputs)) {
+ pr_debug("a_bus_suspend && b_sess_vld\n");
+ if (motg->caps & ALLOW_LPM_ON_DEV_SUSPEND) {
+ pm_runtime_put_noidle(otg->dev);
+ pm_runtime_suspend(otg->dev);
+ }
} else if (test_bit(ID_C, &motg->inputs)) {
msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX);
}
@@ -2569,6 +2594,8 @@
} else {
pr_debug("BSV clear\n");
clear_bit(B_SESS_VLD, &motg->inputs);
+ clear_bit(A_BUS_SUSPEND, &motg->inputs);
+
msm_chg_check_aca_intr(motg);
}
work = 1;
@@ -3389,6 +3416,9 @@
motg->caps = ALLOW_PHY_RETENTION;
}
+ if (motg->pdata->enable_lpm_on_dev_suspend)
+ motg->caps |= ALLOW_LPM_ON_DEV_SUSPEND;
+
wake_lock(&motg->wlock);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 6e96f85..c68457e 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -177,6 +177,9 @@
* @enable_dcd: Enable Data Contact Detection circuit. if not set
* wait for 600msec before proceeding to primary
* detection.
+ * @enable_lpm_on_suspend: Enable the USB core to go into Low
+ * Power Mode, when USB bus is suspended but cable
+ * is connected.
* @bus_scale_table: parameters for bus bandwidth requirements
*/
struct msm_otg_platform_data {
@@ -192,6 +195,7 @@
bool mhl_enable;
bool disable_reset_on_disconnect;
bool enable_dcd;
+ bool enable_lpm_on_dev_suspend;
struct msm_bus_scale_pdata *bus_scale_table;
};
@@ -322,6 +326,11 @@
* voltage regulator(VDDCX).
*/
#define ALLOW_PHY_RETENTION BIT(1)
+ /*
+ * Allow putting the core in Low Power mode, when
+ * USB bus is suspended but cable is connected.
+ */
+#define ALLOW_LPM_ON_DEV_SUSPEND BIT(2)
unsigned long lpm_flags;
#define PHY_PWR_COLLAPSED BIT(0)
#define PHY_RETENTIONED BIT(1)