1*5fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 28e22978cSAlexander Shishkin /* 38e22978cSAlexander Shishkin * Copyright 2012 Freescale Semiconductor, Inc. 48e22978cSAlexander Shishkin * Copyright (C) 2012 Marek Vasut <marex@denx.de> 58e22978cSAlexander Shishkin * on behalf of DENX Software Engineering GmbH 68e22978cSAlexander Shishkin * 78e22978cSAlexander Shishkin * The code contained herein is licensed under the GNU General Public 88e22978cSAlexander Shishkin * License. You may obtain a copy of the GNU General Public License 98e22978cSAlexander Shishkin * Version 2 or later at the following locations: 108e22978cSAlexander Shishkin * 118e22978cSAlexander Shishkin * http://www.opensource.org/licenses/gpl-license.html 128e22978cSAlexander Shishkin * http://www.gnu.org/copyleft/gpl.html 138e22978cSAlexander Shishkin */ 148e22978cSAlexander Shishkin 158e22978cSAlexander Shishkin #include <linux/module.h> 168e22978cSAlexander Shishkin #include <linux/of_platform.h> 178e22978cSAlexander Shishkin #include <linux/of_gpio.h> 188e22978cSAlexander Shishkin #include <linux/platform_device.h> 198e22978cSAlexander Shishkin #include <linux/pm_runtime.h> 208e22978cSAlexander Shishkin #include <linux/dma-mapping.h> 218e22978cSAlexander Shishkin #include <linux/usb/chipidea.h> 22d13631bbSFabien Lahoudere #include <linux/usb/of.h> 238e22978cSAlexander Shishkin #include <linux/clk.h> 248e22978cSAlexander Shishkin 258e22978cSAlexander Shishkin #include "ci.h" 268e22978cSAlexander Shishkin #include "ci_hdrc_imx.h" 278e22978cSAlexander Shishkin 281071055eSPeter Chen struct ci_hdrc_imx_platform_flag { 291071055eSPeter Chen unsigned int flags; 30e14db48dSPeter Chen bool runtime_pm; 311071055eSPeter Chen }; 321071055eSPeter Chen 3381345722SStefan Wahren static const struct ci_hdrc_imx_platform_flag imx23_usb_data = { 3481345722SStefan Wahren .flags = CI_HDRC_TURN_VBUS_EARLY_ON | 3581345722SStefan Wahren CI_HDRC_DISABLE_STREAMING, 3681345722SStefan Wahren }; 3781345722SStefan Wahren 381071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { 390ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING, 401071055eSPeter Chen }; 411071055eSPeter Chen 421071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { 436adb9b7bSLi Jun .flags = CI_HDRC_IMX28_WRITE_FIX | 440ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 450ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING, 461071055eSPeter Chen }; 471071055eSPeter Chen 48e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { 496adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 500ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 510ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING, 52e14db48dSPeter Chen }; 53e14db48dSPeter Chen 54e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { 556adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 560ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 570ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING, 58e14db48dSPeter Chen }; 59e14db48dSPeter Chen 60e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { 616adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 620ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 630ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING, 64e14db48dSPeter Chen }; 65e14db48dSPeter Chen 6652fe568eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { 6752fe568eSPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 6852fe568eSPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON, 6952fe568eSPeter Chen }; 7052fe568eSPeter Chen 715cb377c5SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { 725cb377c5SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, 735cb377c5SPeter Chen }; 745cb377c5SPeter Chen 751071055eSPeter Chen static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 7681345722SStefan Wahren { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data}, 771071055eSPeter Chen { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 781071055eSPeter Chen { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 79e14db48dSPeter Chen { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, 80e14db48dSPeter Chen { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, 818315b77dSLi Jun { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, 8252fe568eSPeter Chen { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data}, 835cb377c5SPeter Chen { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, 841071055eSPeter Chen { /* sentinel */ } 851071055eSPeter Chen }; 861071055eSPeter Chen MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 871071055eSPeter Chen 888e22978cSAlexander Shishkin struct ci_hdrc_imx_data { 898e22978cSAlexander Shishkin struct usb_phy *phy; 908e22978cSAlexander Shishkin struct platform_device *ci_pdev; 918e22978cSAlexander Shishkin struct clk *clk; 9205986ba9SSascha Hauer struct imx_usbmisc_data *usbmisc_data; 93e14db48dSPeter Chen bool supports_runtime_pm; 94e14db48dSPeter Chen bool in_lpm; 95ae3e57aeSPeter Chen /* SoC before i.mx6 (except imx23/imx28) needs three clks */ 96ae3e57aeSPeter Chen bool need_three_clks; 97ae3e57aeSPeter Chen struct clk *clk_ipg; 98ae3e57aeSPeter Chen struct clk *clk_ahb; 99ae3e57aeSPeter Chen struct clk *clk_per; 100ae3e57aeSPeter Chen /* --------------------------------- */ 1018e22978cSAlexander Shishkin }; 1028e22978cSAlexander Shishkin 1038e22978cSAlexander Shishkin /* Common functions shared by usbmisc drivers */ 1048e22978cSAlexander Shishkin 10505986ba9SSascha Hauer static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) 1068e22978cSAlexander Shishkin { 107f40017e0SStefan Agner struct platform_device *misc_pdev; 1088e22978cSAlexander Shishkin struct device_node *np = dev->of_node; 1098e22978cSAlexander Shishkin struct of_phandle_args args; 11005986ba9SSascha Hauer struct imx_usbmisc_data *data; 1118e22978cSAlexander Shishkin int ret; 1128e22978cSAlexander Shishkin 11305986ba9SSascha Hauer /* 11405986ba9SSascha Hauer * In case the fsl,usbmisc property is not present this device doesn't 11505986ba9SSascha Hauer * need usbmisc. Return NULL (which is no error here) 11605986ba9SSascha Hauer */ 11705986ba9SSascha Hauer if (!of_get_property(np, "fsl,usbmisc", NULL)) 11805986ba9SSascha Hauer return NULL; 11905986ba9SSascha Hauer 12005986ba9SSascha Hauer data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 12105986ba9SSascha Hauer if (!data) 12205986ba9SSascha Hauer return ERR_PTR(-ENOMEM); 1238e22978cSAlexander Shishkin 1248e22978cSAlexander Shishkin ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 1258e22978cSAlexander Shishkin 0, &args); 1268e22978cSAlexander Shishkin if (ret) { 1278e22978cSAlexander Shishkin dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", 1288e22978cSAlexander Shishkin ret); 12905986ba9SSascha Hauer return ERR_PTR(ret); 1308e22978cSAlexander Shishkin } 13105986ba9SSascha Hauer 13205986ba9SSascha Hauer data->index = args.args[0]; 133f40017e0SStefan Agner 134f40017e0SStefan Agner misc_pdev = of_find_device_by_node(args.np); 1358e22978cSAlexander Shishkin of_node_put(args.np); 1368e22978cSAlexander Shishkin 137ef12da91STomeu Vizoso if (!misc_pdev || !platform_get_drvdata(misc_pdev)) 138f40017e0SStefan Agner return ERR_PTR(-EPROBE_DEFER); 139f40017e0SStefan Agner 140f40017e0SStefan Agner data->dev = &misc_pdev->dev; 141f40017e0SStefan Agner 1428e22978cSAlexander Shishkin if (of_find_property(np, "disable-over-current", NULL)) 14305986ba9SSascha Hauer data->disable_oc = 1; 1448e22978cSAlexander Shishkin 1459dba516eSLi Jun if (of_find_property(np, "over-current-active-high", NULL)) 1469dba516eSLi Jun data->oc_polarity = 1; 1479dba516eSLi Jun 1488e22978cSAlexander Shishkin if (of_find_property(np, "external-vbus-divider", NULL)) 14905986ba9SSascha Hauer data->evdo = 1; 1508e22978cSAlexander Shishkin 151d13631bbSFabien Lahoudere if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) 152d13631bbSFabien Lahoudere data->ulpi = 1; 153d13631bbSFabien Lahoudere 15405986ba9SSascha Hauer return data; 1558e22978cSAlexander Shishkin } 1568e22978cSAlexander Shishkin 1578e22978cSAlexander Shishkin /* End of common functions shared by usbmisc drivers*/ 158ae3e57aeSPeter Chen static int imx_get_clks(struct device *dev) 159ae3e57aeSPeter Chen { 160ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 161ae3e57aeSPeter Chen int ret = 0; 162ae3e57aeSPeter Chen 163ae3e57aeSPeter Chen data->clk_ipg = devm_clk_get(dev, "ipg"); 164ae3e57aeSPeter Chen if (IS_ERR(data->clk_ipg)) { 165ae3e57aeSPeter Chen /* If the platform only needs one clocks */ 166ae3e57aeSPeter Chen data->clk = devm_clk_get(dev, NULL); 167ae3e57aeSPeter Chen if (IS_ERR(data->clk)) { 168ae3e57aeSPeter Chen ret = PTR_ERR(data->clk); 169ae3e57aeSPeter Chen dev_err(dev, 170ae3e57aeSPeter Chen "Failed to get clks, err=%ld,%ld\n", 171ae3e57aeSPeter Chen PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); 172ae3e57aeSPeter Chen return ret; 173ae3e57aeSPeter Chen } 174ae3e57aeSPeter Chen return ret; 175ae3e57aeSPeter Chen } 176ae3e57aeSPeter Chen 177ae3e57aeSPeter Chen data->clk_ahb = devm_clk_get(dev, "ahb"); 178ae3e57aeSPeter Chen if (IS_ERR(data->clk_ahb)) { 179ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_ahb); 180ae3e57aeSPeter Chen dev_err(dev, 181ae3e57aeSPeter Chen "Failed to get ahb clock, err=%d\n", ret); 182ae3e57aeSPeter Chen return ret; 183ae3e57aeSPeter Chen } 184ae3e57aeSPeter Chen 185ae3e57aeSPeter Chen data->clk_per = devm_clk_get(dev, "per"); 186ae3e57aeSPeter Chen if (IS_ERR(data->clk_per)) { 187ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_per); 188ae3e57aeSPeter Chen dev_err(dev, 189ae3e57aeSPeter Chen "Failed to get per clock, err=%d\n", ret); 190ae3e57aeSPeter Chen return ret; 191ae3e57aeSPeter Chen } 192ae3e57aeSPeter Chen 193ae3e57aeSPeter Chen data->need_three_clks = true; 194ae3e57aeSPeter Chen return ret; 195ae3e57aeSPeter Chen } 196ae3e57aeSPeter Chen 197ae3e57aeSPeter Chen static int imx_prepare_enable_clks(struct device *dev) 198ae3e57aeSPeter Chen { 199ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 200ae3e57aeSPeter Chen int ret = 0; 201ae3e57aeSPeter Chen 202ae3e57aeSPeter Chen if (data->need_three_clks) { 203ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ipg); 204ae3e57aeSPeter Chen if (ret) { 205ae3e57aeSPeter Chen dev_err(dev, 206ae3e57aeSPeter Chen "Failed to prepare/enable ipg clk, err=%d\n", 207ae3e57aeSPeter Chen ret); 208ae3e57aeSPeter Chen return ret; 209ae3e57aeSPeter Chen } 210ae3e57aeSPeter Chen 211ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ahb); 212ae3e57aeSPeter Chen if (ret) { 213ae3e57aeSPeter Chen dev_err(dev, 214ae3e57aeSPeter Chen "Failed to prepare/enable ahb clk, err=%d\n", 215ae3e57aeSPeter Chen ret); 216ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 217ae3e57aeSPeter Chen return ret; 218ae3e57aeSPeter Chen } 219ae3e57aeSPeter Chen 220ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_per); 221ae3e57aeSPeter Chen if (ret) { 222ae3e57aeSPeter Chen dev_err(dev, 223ae3e57aeSPeter Chen "Failed to prepare/enable per clk, err=%d\n", 224ae3e57aeSPeter Chen ret); 225ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 226ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 227ae3e57aeSPeter Chen return ret; 228ae3e57aeSPeter Chen } 229ae3e57aeSPeter Chen } else { 230ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk); 231ae3e57aeSPeter Chen if (ret) { 232ae3e57aeSPeter Chen dev_err(dev, 233ae3e57aeSPeter Chen "Failed to prepare/enable clk, err=%d\n", 234ae3e57aeSPeter Chen ret); 235ae3e57aeSPeter Chen return ret; 236ae3e57aeSPeter Chen } 237ae3e57aeSPeter Chen } 238ae3e57aeSPeter Chen 239ae3e57aeSPeter Chen return ret; 240ae3e57aeSPeter Chen } 241ae3e57aeSPeter Chen 242ae3e57aeSPeter Chen static void imx_disable_unprepare_clks(struct device *dev) 243ae3e57aeSPeter Chen { 244ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 245ae3e57aeSPeter Chen 246ae3e57aeSPeter Chen if (data->need_three_clks) { 247ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_per); 248ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 249ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 250ae3e57aeSPeter Chen } else { 251ae3e57aeSPeter Chen clk_disable_unprepare(data->clk); 252ae3e57aeSPeter Chen } 253ae3e57aeSPeter Chen } 2548e22978cSAlexander Shishkin 2558e22978cSAlexander Shishkin static int ci_hdrc_imx_probe(struct platform_device *pdev) 2568e22978cSAlexander Shishkin { 2578e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data; 2588e22978cSAlexander Shishkin struct ci_hdrc_platform_data pdata = { 259c844d6c8SAlexander Shiyan .name = dev_name(&pdev->dev), 2608e22978cSAlexander Shishkin .capoffset = DEF_CAPOFFSET, 2618e22978cSAlexander Shishkin }; 2628e22978cSAlexander Shishkin int ret; 2636f51bc34SLABBE Corentin const struct of_device_id *of_id; 2646f51bc34SLABBE Corentin const struct ci_hdrc_imx_platform_flag *imx_platform_flag; 2656f51bc34SLABBE Corentin 2666f51bc34SLABBE Corentin of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); 2676f51bc34SLABBE Corentin if (!of_id) 2686f51bc34SLABBE Corentin return -ENODEV; 2696f51bc34SLABBE Corentin 2706f51bc34SLABBE Corentin imx_platform_flag = of_id->data; 2718e22978cSAlexander Shishkin 2728e22978cSAlexander Shishkin data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 27373529828SFabio Estevam if (!data) 2748e22978cSAlexander Shishkin return -ENOMEM; 2758e22978cSAlexander Shishkin 276ae3e57aeSPeter Chen platform_set_drvdata(pdev, data); 27705986ba9SSascha Hauer data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); 27805986ba9SSascha Hauer if (IS_ERR(data->usbmisc_data)) 27905986ba9SSascha Hauer return PTR_ERR(data->usbmisc_data); 28005986ba9SSascha Hauer 281ae3e57aeSPeter Chen ret = imx_get_clks(&pdev->dev); 282ae3e57aeSPeter Chen if (ret) 2838e22978cSAlexander Shishkin return ret; 284ae3e57aeSPeter Chen 285ae3e57aeSPeter Chen ret = imx_prepare_enable_clks(&pdev->dev); 286ae3e57aeSPeter Chen if (ret) 287ae3e57aeSPeter Chen return ret; 2888e22978cSAlexander Shishkin 289046916deSFabio Estevam data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); 290af59a8b1SPeter Chen if (IS_ERR(data->phy)) { 291af59a8b1SPeter Chen ret = PTR_ERR(data->phy); 29216853d7bSMarkus Pargmann /* Return -EINVAL if no usbphy is available */ 29316853d7bSMarkus Pargmann if (ret == -ENODEV) 29416853d7bSMarkus Pargmann ret = -EINVAL; 2958e22978cSAlexander Shishkin goto err_clk; 2968e22978cSAlexander Shishkin } 2978e22978cSAlexander Shishkin 298ef44cb42SAntoine Tenart pdata.usb_phy = data->phy; 29956040087SPeter Chen pdata.flags |= imx_platform_flag->flags; 300e14db48dSPeter Chen if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) 301e14db48dSPeter Chen data->supports_runtime_pm = true; 3021071055eSPeter Chen 30305986ba9SSascha Hauer ret = imx_usbmisc_init(data->usbmisc_data); 3048e22978cSAlexander Shishkin if (ret) { 305a4cf1b14SPeter Chen dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); 306af59a8b1SPeter Chen goto err_clk; 3078e22978cSAlexander Shishkin } 3088e22978cSAlexander Shishkin 3098e22978cSAlexander Shishkin data->ci_pdev = ci_hdrc_add_device(&pdev->dev, 3108e22978cSAlexander Shishkin pdev->resource, pdev->num_resources, 3118e22978cSAlexander Shishkin &pdata); 3128e22978cSAlexander Shishkin if (IS_ERR(data->ci_pdev)) { 3138e22978cSAlexander Shishkin ret = PTR_ERR(data->ci_pdev); 314d3d8425aSStefan Agner if (ret != -EPROBE_DEFER) 3158e22978cSAlexander Shishkin dev_err(&pdev->dev, 316d3d8425aSStefan Agner "ci_hdrc_add_device failed, err=%d\n", ret); 317af59a8b1SPeter Chen goto err_clk; 3188e22978cSAlexander Shishkin } 3198e22978cSAlexander Shishkin 32005986ba9SSascha Hauer ret = imx_usbmisc_init_post(data->usbmisc_data); 3218e22978cSAlexander Shishkin if (ret) { 322a4cf1b14SPeter Chen dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret); 3238e22978cSAlexander Shishkin goto disable_device; 3248e22978cSAlexander Shishkin } 3258e22978cSAlexander Shishkin 326e14db48dSPeter Chen if (data->supports_runtime_pm) { 327e14db48dSPeter Chen pm_runtime_set_active(&pdev->dev); 3288e22978cSAlexander Shishkin pm_runtime_enable(&pdev->dev); 329e14db48dSPeter Chen } 3308e22978cSAlexander Shishkin 3316d653110SPeter Chen device_set_wakeup_capable(&pdev->dev, true); 3326d653110SPeter Chen 3338e22978cSAlexander Shishkin return 0; 3348e22978cSAlexander Shishkin 3358e22978cSAlexander Shishkin disable_device: 3368e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 3378e22978cSAlexander Shishkin err_clk: 338ae3e57aeSPeter Chen imx_disable_unprepare_clks(&pdev->dev); 3398e22978cSAlexander Shishkin return ret; 3408e22978cSAlexander Shishkin } 3418e22978cSAlexander Shishkin 3428e22978cSAlexander Shishkin static int ci_hdrc_imx_remove(struct platform_device *pdev) 3438e22978cSAlexander Shishkin { 3448e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); 3458e22978cSAlexander Shishkin 346e14db48dSPeter Chen if (data->supports_runtime_pm) { 347e14db48dSPeter Chen pm_runtime_get_sync(&pdev->dev); 3488e22978cSAlexander Shishkin pm_runtime_disable(&pdev->dev); 349e14db48dSPeter Chen pm_runtime_put_noidle(&pdev->dev); 350e14db48dSPeter Chen } 3518e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 352ae3e57aeSPeter Chen imx_disable_unprepare_clks(&pdev->dev); 3538e22978cSAlexander Shishkin 3548e22978cSAlexander Shishkin return 0; 3558e22978cSAlexander Shishkin } 3568e22978cSAlexander Shishkin 357b09b5224SAndreas Fenkart static void ci_hdrc_imx_shutdown(struct platform_device *pdev) 358b09b5224SAndreas Fenkart { 359b09b5224SAndreas Fenkart ci_hdrc_imx_remove(pdev); 360b09b5224SAndreas Fenkart } 361b09b5224SAndreas Fenkart 362e14db48dSPeter Chen #ifdef CONFIG_PM 3632558c1f5SPeter Chen static int imx_controller_suspend(struct device *dev) 3642558c1f5SPeter Chen { 3652558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 3662558c1f5SPeter Chen 3672558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 3682558c1f5SPeter Chen 369ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 370e14db48dSPeter Chen data->in_lpm = true; 3712558c1f5SPeter Chen 3722558c1f5SPeter Chen return 0; 3732558c1f5SPeter Chen } 3742558c1f5SPeter Chen 3752558c1f5SPeter Chen static int imx_controller_resume(struct device *dev) 3762558c1f5SPeter Chen { 3772558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 378e14db48dSPeter Chen int ret = 0; 3792558c1f5SPeter Chen 3802558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 3812558c1f5SPeter Chen 382e14db48dSPeter Chen if (!data->in_lpm) { 383e14db48dSPeter Chen WARN_ON(1); 384e14db48dSPeter Chen return 0; 3852558c1f5SPeter Chen } 3862558c1f5SPeter Chen 387ae3e57aeSPeter Chen ret = imx_prepare_enable_clks(dev); 388e14db48dSPeter Chen if (ret) 389e14db48dSPeter Chen return ret; 390e14db48dSPeter Chen 391e14db48dSPeter Chen data->in_lpm = false; 392e14db48dSPeter Chen 393e14db48dSPeter Chen ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); 394e14db48dSPeter Chen if (ret) { 395e14db48dSPeter Chen dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 396e14db48dSPeter Chen goto clk_disable; 397e14db48dSPeter Chen } 398e14db48dSPeter Chen 399e14db48dSPeter Chen return 0; 400e14db48dSPeter Chen 401e14db48dSPeter Chen clk_disable: 402ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 403e14db48dSPeter Chen return ret; 404e14db48dSPeter Chen } 405e14db48dSPeter Chen 406e14db48dSPeter Chen #ifdef CONFIG_PM_SLEEP 4072558c1f5SPeter Chen static int ci_hdrc_imx_suspend(struct device *dev) 4082558c1f5SPeter Chen { 4096d653110SPeter Chen int ret; 4106d653110SPeter Chen 411e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 412e14db48dSPeter Chen 413e14db48dSPeter Chen if (data->in_lpm) 414e14db48dSPeter Chen /* The core's suspend doesn't run */ 415e14db48dSPeter Chen return 0; 416e14db48dSPeter Chen 4176d653110SPeter Chen if (device_may_wakeup(dev)) { 4186d653110SPeter Chen ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 4196d653110SPeter Chen if (ret) { 4206d653110SPeter Chen dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", 4216d653110SPeter Chen ret); 4226d653110SPeter Chen return ret; 4236d653110SPeter Chen } 4246d653110SPeter Chen } 4256d653110SPeter Chen 4262558c1f5SPeter Chen return imx_controller_suspend(dev); 4272558c1f5SPeter Chen } 4282558c1f5SPeter Chen 4292558c1f5SPeter Chen static int ci_hdrc_imx_resume(struct device *dev) 4302558c1f5SPeter Chen { 431e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 432e14db48dSPeter Chen int ret; 433e14db48dSPeter Chen 434e14db48dSPeter Chen ret = imx_controller_resume(dev); 435e14db48dSPeter Chen if (!ret && data->supports_runtime_pm) { 436e14db48dSPeter Chen pm_runtime_disable(dev); 437e14db48dSPeter Chen pm_runtime_set_active(dev); 438e14db48dSPeter Chen pm_runtime_enable(dev); 439e14db48dSPeter Chen } 440e14db48dSPeter Chen 441e14db48dSPeter Chen return ret; 4422558c1f5SPeter Chen } 4432558c1f5SPeter Chen #endif /* CONFIG_PM_SLEEP */ 4442558c1f5SPeter Chen 445e14db48dSPeter Chen static int ci_hdrc_imx_runtime_suspend(struct device *dev) 446e14db48dSPeter Chen { 447e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 448e14db48dSPeter Chen int ret; 449e14db48dSPeter Chen 450e14db48dSPeter Chen if (data->in_lpm) { 451e14db48dSPeter Chen WARN_ON(1); 452e14db48dSPeter Chen return 0; 453e14db48dSPeter Chen } 454e14db48dSPeter Chen 455e14db48dSPeter Chen ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 456e14db48dSPeter Chen if (ret) { 457e14db48dSPeter Chen dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 458e14db48dSPeter Chen return ret; 459e14db48dSPeter Chen } 460e14db48dSPeter Chen 461e14db48dSPeter Chen return imx_controller_suspend(dev); 462e14db48dSPeter Chen } 463e14db48dSPeter Chen 464e14db48dSPeter Chen static int ci_hdrc_imx_runtime_resume(struct device *dev) 465e14db48dSPeter Chen { 466e14db48dSPeter Chen return imx_controller_resume(dev); 467e14db48dSPeter Chen } 468e14db48dSPeter Chen 469e14db48dSPeter Chen #endif /* CONFIG_PM */ 470e14db48dSPeter Chen 4712558c1f5SPeter Chen static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { 4722558c1f5SPeter Chen SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) 473e14db48dSPeter Chen SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, 474e14db48dSPeter Chen ci_hdrc_imx_runtime_resume, NULL) 4752558c1f5SPeter Chen }; 4768e22978cSAlexander Shishkin static struct platform_driver ci_hdrc_imx_driver = { 4778e22978cSAlexander Shishkin .probe = ci_hdrc_imx_probe, 4788e22978cSAlexander Shishkin .remove = ci_hdrc_imx_remove, 479b09b5224SAndreas Fenkart .shutdown = ci_hdrc_imx_shutdown, 4808e22978cSAlexander Shishkin .driver = { 4818e22978cSAlexander Shishkin .name = "imx_usb", 4828e22978cSAlexander Shishkin .of_match_table = ci_hdrc_imx_dt_ids, 4832558c1f5SPeter Chen .pm = &ci_hdrc_imx_pm_ops, 4848e22978cSAlexander Shishkin }, 4858e22978cSAlexander Shishkin }; 4868e22978cSAlexander Shishkin 4878e22978cSAlexander Shishkin module_platform_driver(ci_hdrc_imx_driver); 4888e22978cSAlexander Shishkin 4898e22978cSAlexander Shishkin MODULE_ALIAS("platform:imx-usb"); 4908e22978cSAlexander Shishkin MODULE_LICENSE("GPL v2"); 4918e22978cSAlexander Shishkin MODULE_DESCRIPTION("CI HDRC i.MX USB binding"); 4928e22978cSAlexander Shishkin MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 4938e22978cSAlexander Shishkin MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 494