diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 4268fe0dcbc..66f875f4e3b 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,25 @@ static struct msm_otg *the_msm_otg; static bool debug_aca_enabled; +/* Prevent idle power collapse(pc) while operating in peripheral mode */ +static void otg_pm_qos_update_latency(struct msm_otg *dev, int vote) +{ + struct msm_otg_platform_data *pdata = dev->pdata; + u32 swfi_latency = 0; + + if (!pdata || !pdata->swfi_latency) + return; + + swfi_latency = pdata->swfi_latency + 1; + + if (vote) + pm_qos_update_request(&dev->pm_qos_req_dma, + swfi_latency); + else + pm_qos_update_request(&dev->pm_qos_req_dma, + PM_QOS_DEFAULT_VALUE); +} + static struct regulator *hsusb_3p3; static struct regulator *hsusb_1p8; static struct regulator *hsusb_vddcx; @@ -995,10 +1015,16 @@ static void msm_otg_start_peripheral(struct usb_phy *phy, int on) */ if (pdata->setup_gpio) pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); + /* + * vote for minimum dma_latency to prevent idle + * power collapse(pc) while running in peripheral mode. + */ + otg_pm_qos_update_latency(motg, 1); usb_gadget_vbus_connect(phy->otg->gadget); } else { dev_dbg(phy->dev, "gadget off\n"); usb_gadget_vbus_disconnect(phy->otg->gadget); + otg_pm_qos_update_latency(motg, 0); if (pdata->setup_gpio) pdata->setup_gpio(OTG_STATE_UNDEFINED); } @@ -2252,6 +2278,10 @@ static int __init msm_otg_probe(struct platform_device *pdev) } clk_set_rate(motg->clk, 60000000); + /* pm qos request to prevent apps idle power collapse */ + if (motg->pdata->swfi_latency) + pm_qos_add_request(&motg->pm_qos_req_dma, + PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); /* * If USB Core is running its protocol engine based on CORE CLK, * CORE CLK must be running at >55Mhz for correct HSUSB @@ -2475,6 +2505,8 @@ put_phy_reset_clk: if (!IS_ERR(motg->phy_reset_clk)) clk_put(motg->phy_reset_clk); free_motg: + if (motg->pdata->swfi_latency) + pm_qos_remove_request(&motg->pm_qos_req_dma); kfree(motg->phy.otg); kfree(motg); return ret; @@ -2551,9 +2583,10 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) if (!IS_ERR(motg->system_clk)) clk_put(motg->system_clk); + if (motg->pdata->swfi_latency) + pm_qos_remove_request(&motg->pm_qos_req_dma); kfree(motg->phy.otg); kfree(motg); - return 0; } diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index d0a174d0e1b..0b79c9538ea 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -164,6 +164,7 @@ enum usb_chg_type { * dfab_usb_hs_clk in case of 8660 and 8960. * @pmic_id_irq: IRQ number assigned for PMIC USB ID line. * @mhl_enable: indicates MHL connector or not. + * @swfi_latency: miminum latency to allow swfi. */ struct msm_otg_platform_data { int *phy_init_seq; @@ -177,6 +178,7 @@ struct msm_otg_platform_data { const char *pclk_src_name; int pmic_id_irq; bool mhl_enable; + u32 swfi_latency; }; /** @@ -207,6 +209,8 @@ struct msm_otg_platform_data { * connected. Useful only when ACA_A charger is * connected. * @mA_port: The amount of current drawn by the attached B-device. + * @pm_qos_req_dma: miminum DMA latency to vote against idle power + collapse when cable is connected. * @id_timer: The timer used for polling ID line to detect ACA states. * @xo_handle: TCXO buffer handle */ @@ -263,6 +267,7 @@ struct msm_otg { #define PHY_PWR_COLLAPSED BIT(0) #define PHY_RETENTIONED BIT(1) #define PHY_OTG_COMP_DISABLED BIT(2) + struct pm_qos_request_list pm_qos_req_dma; }; struct msm_hsic_host_platform_data {