From e14db48dfcf3ab6ebea212e82dc56036a00b0d6b Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 11 Feb 2015 12:44:47 +0800 Subject: [PATCH] usb: chipidea: imx: add runtime power management support Add runtime pm support for imx, only imx6 series are supported and tested. Signed-off-by: Peter Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 106 +++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 353989e5675b..5ad85fe84411 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -25,6 +25,7 @@ struct ci_hdrc_imx_platform_flag { unsigned int flags; + bool runtime_pm; }; static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { @@ -34,9 +35,24 @@ static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { .flags = CI_HDRC_IMX28_WRITE_FIX, }; +static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, +}; + +static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, +}; + +static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, +}; + static const struct of_device_id ci_hdrc_imx_dt_ids[] = { { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, + { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, + { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, + { .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); @@ -46,6 +62,8 @@ struct ci_hdrc_imx_data { struct platform_device *ci_pdev; struct clk *clk; struct imx_usbmisc_data *usbmisc_data; + bool supports_runtime_pm; + bool in_lpm; }; /* Common functions shared by usbmisc drivers */ @@ -144,6 +162,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) pdata.usb_phy = data->phy; pdata.flags |= imx_platform_flag->flags; + if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) + data->supports_runtime_pm = true; ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) @@ -174,8 +194,10 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); - pm_runtime_no_callbacks(&pdev->dev); - pm_runtime_enable(&pdev->dev); + if (data->supports_runtime_pm) { + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + } return 0; @@ -190,14 +212,18 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) { struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); + if (data->supports_runtime_pm) { + pm_runtime_get_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + } ci_hdrc_remove_device(data->ci_pdev); clk_disable_unprepare(data->clk); return 0; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM static int imx_controller_suspend(struct device *dev) { struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); @@ -205,6 +231,7 @@ static int imx_controller_suspend(struct device *dev) dev_dbg(dev, "at %s\n", __func__); clk_disable_unprepare(data->clk); + data->in_lpm = true; return 0; } @@ -212,25 +239,92 @@ static int imx_controller_suspend(struct device *dev) static int imx_controller_resume(struct device *dev) { struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret = 0; dev_dbg(dev, "at %s\n", __func__); - return clk_prepare_enable(data->clk); + if (!data->in_lpm) { + WARN_ON(1); + return 0; + } + + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; + + data->in_lpm = false; + + ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); + if (ret) { + dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); + goto clk_disable; + } + + return 0; + +clk_disable: + clk_disable_unprepare(data->clk); + return ret; } +#ifdef CONFIG_PM_SLEEP static int ci_hdrc_imx_suspend(struct device *dev) { + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + + if (data->in_lpm) + /* The core's suspend doesn't run */ + return 0; + return imx_controller_suspend(dev); } static int ci_hdrc_imx_resume(struct device *dev) { - return imx_controller_resume(dev); + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret; + + ret = imx_controller_resume(dev); + if (!ret && data->supports_runtime_pm) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + return ret; } #endif /* CONFIG_PM_SLEEP */ +static int ci_hdrc_imx_runtime_suspend(struct device *dev) +{ + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret; + + if (data->in_lpm) { + WARN_ON(1); + return 0; + } + + ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); + if (ret) { + dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); + return ret; + } + + return imx_controller_suspend(dev); +} + +static int ci_hdrc_imx_runtime_resume(struct device *dev) +{ + return imx_controller_resume(dev); +} + +#endif /* CONFIG_PM */ + static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) + SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, + ci_hdrc_imx_runtime_resume, NULL) }; static struct platform_driver ci_hdrc_imx_driver = { .probe = ci_hdrc_imx_probe, -- 2.39.5