15fd54aceSGreg 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 88e22978cSAlexander Shishkin #include <linux/module.h> 98e22978cSAlexander Shishkin #include <linux/of_platform.h> 108e22978cSAlexander Shishkin #include <linux/of_gpio.h> 118e22978cSAlexander Shishkin #include <linux/platform_device.h> 128e22978cSAlexander Shishkin #include <linux/pm_runtime.h> 138e22978cSAlexander Shishkin #include <linux/dma-mapping.h> 148e22978cSAlexander Shishkin #include <linux/usb/chipidea.h> 15d13631bbSFabien Lahoudere #include <linux/usb/of.h> 168e22978cSAlexander Shishkin #include <linux/clk.h> 178e22978cSAlexander Shishkin 188e22978cSAlexander Shishkin #include "ci.h" 198e22978cSAlexander Shishkin #include "ci_hdrc_imx.h" 208e22978cSAlexander Shishkin 211071055eSPeter Chen struct ci_hdrc_imx_platform_flag { 221071055eSPeter Chen unsigned int flags; 231071055eSPeter Chen }; 241071055eSPeter Chen 2581345722SStefan Wahren static const struct ci_hdrc_imx_platform_flag imx23_usb_data = { 2681345722SStefan Wahren .flags = CI_HDRC_TURN_VBUS_EARLY_ON | 2781345722SStefan Wahren CI_HDRC_DISABLE_STREAMING, 2881345722SStefan Wahren }; 2981345722SStefan Wahren 301071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { 3104e16379SSebastian Reichel .flags = CI_HDRC_DISABLE_STREAMING, 321071055eSPeter Chen }; 331071055eSPeter Chen 341071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { 356adb9b7bSLi Jun .flags = CI_HDRC_IMX28_WRITE_FIX | 360ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 370ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING, 381071055eSPeter Chen }; 391071055eSPeter Chen 40e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { 416adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 420ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 430ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING, 44e14db48dSPeter Chen }; 45e14db48dSPeter Chen 46e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { 476adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 480ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 490ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING, 50e14db48dSPeter Chen }; 51e14db48dSPeter Chen 52e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { 536adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 540ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 550ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING, 56e14db48dSPeter Chen }; 57e14db48dSPeter Chen 5852fe568eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { 5952fe568eSPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 6052fe568eSPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON, 6152fe568eSPeter Chen }; 6252fe568eSPeter Chen 635cb377c5SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { 645cb377c5SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, 655cb377c5SPeter Chen }; 665cb377c5SPeter Chen 671071055eSPeter Chen static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 6881345722SStefan Wahren { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data}, 691071055eSPeter Chen { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 701071055eSPeter Chen { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 71e14db48dSPeter Chen { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, 72e14db48dSPeter Chen { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, 738315b77dSLi Jun { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, 7452fe568eSPeter Chen { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data}, 755cb377c5SPeter Chen { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, 761071055eSPeter Chen { /* sentinel */ } 771071055eSPeter Chen }; 781071055eSPeter Chen MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 791071055eSPeter Chen 808e22978cSAlexander Shishkin struct ci_hdrc_imx_data { 818e22978cSAlexander Shishkin struct usb_phy *phy; 828e22978cSAlexander Shishkin struct platform_device *ci_pdev; 838e22978cSAlexander Shishkin struct clk *clk; 8405986ba9SSascha Hauer struct imx_usbmisc_data *usbmisc_data; 85e14db48dSPeter Chen bool supports_runtime_pm; 86be9cae24SSebastian Reichel bool override_phy_control; 87e14db48dSPeter Chen bool in_lpm; 88ae3e57aeSPeter Chen /* SoC before i.mx6 (except imx23/imx28) needs three clks */ 89ae3e57aeSPeter Chen bool need_three_clks; 90ae3e57aeSPeter Chen struct clk *clk_ipg; 91ae3e57aeSPeter Chen struct clk *clk_ahb; 92ae3e57aeSPeter Chen struct clk *clk_per; 93ae3e57aeSPeter Chen /* --------------------------------- */ 948e22978cSAlexander Shishkin }; 958e22978cSAlexander Shishkin 968e22978cSAlexander Shishkin /* Common functions shared by usbmisc drivers */ 978e22978cSAlexander Shishkin 9805986ba9SSascha Hauer static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) 998e22978cSAlexander Shishkin { 100f40017e0SStefan Agner struct platform_device *misc_pdev; 1018e22978cSAlexander Shishkin struct device_node *np = dev->of_node; 1028e22978cSAlexander Shishkin struct of_phandle_args args; 10305986ba9SSascha Hauer struct imx_usbmisc_data *data; 1048e22978cSAlexander Shishkin int ret; 1058e22978cSAlexander Shishkin 10605986ba9SSascha Hauer /* 10705986ba9SSascha Hauer * In case the fsl,usbmisc property is not present this device doesn't 10805986ba9SSascha Hauer * need usbmisc. Return NULL (which is no error here) 10905986ba9SSascha Hauer */ 11005986ba9SSascha Hauer if (!of_get_property(np, "fsl,usbmisc", NULL)) 11105986ba9SSascha Hauer return NULL; 11205986ba9SSascha Hauer 11305986ba9SSascha Hauer data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 11405986ba9SSascha Hauer if (!data) 11505986ba9SSascha Hauer return ERR_PTR(-ENOMEM); 1168e22978cSAlexander Shishkin 1178e22978cSAlexander Shishkin ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 1188e22978cSAlexander Shishkin 0, &args); 1198e22978cSAlexander Shishkin if (ret) { 1208e22978cSAlexander Shishkin dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", 1218e22978cSAlexander Shishkin ret); 12205986ba9SSascha Hauer return ERR_PTR(ret); 1238e22978cSAlexander Shishkin } 12405986ba9SSascha Hauer 12505986ba9SSascha Hauer data->index = args.args[0]; 126f40017e0SStefan Agner 127f40017e0SStefan Agner misc_pdev = of_find_device_by_node(args.np); 1288e22978cSAlexander Shishkin of_node_put(args.np); 1298e22978cSAlexander Shishkin 130ef12da91STomeu Vizoso if (!misc_pdev || !platform_get_drvdata(misc_pdev)) 131f40017e0SStefan Agner return ERR_PTR(-EPROBE_DEFER); 132f40017e0SStefan Agner 133f40017e0SStefan Agner data->dev = &misc_pdev->dev; 134f40017e0SStefan Agner 1358e22978cSAlexander Shishkin if (of_find_property(np, "disable-over-current", NULL)) 13605986ba9SSascha Hauer data->disable_oc = 1; 1378e22978cSAlexander Shishkin 1389dba516eSLi Jun if (of_find_property(np, "over-current-active-high", NULL)) 1399dba516eSLi Jun data->oc_polarity = 1; 1409dba516eSLi Jun 1418e22978cSAlexander Shishkin if (of_find_property(np, "external-vbus-divider", NULL)) 14205986ba9SSascha Hauer data->evdo = 1; 1438e22978cSAlexander Shishkin 144d13631bbSFabien Lahoudere if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) 145d13631bbSFabien Lahoudere data->ulpi = 1; 146d13631bbSFabien Lahoudere 14705986ba9SSascha Hauer return data; 1488e22978cSAlexander Shishkin } 1498e22978cSAlexander Shishkin 1508e22978cSAlexander Shishkin /* End of common functions shared by usbmisc drivers*/ 151ae3e57aeSPeter Chen static int imx_get_clks(struct device *dev) 152ae3e57aeSPeter Chen { 153ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 154ae3e57aeSPeter Chen int ret = 0; 155ae3e57aeSPeter Chen 156ae3e57aeSPeter Chen data->clk_ipg = devm_clk_get(dev, "ipg"); 157ae3e57aeSPeter Chen if (IS_ERR(data->clk_ipg)) { 158ae3e57aeSPeter Chen /* If the platform only needs one clocks */ 159ae3e57aeSPeter Chen data->clk = devm_clk_get(dev, NULL); 160ae3e57aeSPeter Chen if (IS_ERR(data->clk)) { 161ae3e57aeSPeter Chen ret = PTR_ERR(data->clk); 162ae3e57aeSPeter Chen dev_err(dev, 163ae3e57aeSPeter Chen "Failed to get clks, err=%ld,%ld\n", 164ae3e57aeSPeter Chen PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); 165ae3e57aeSPeter Chen return ret; 166ae3e57aeSPeter Chen } 167ae3e57aeSPeter Chen return ret; 168ae3e57aeSPeter Chen } 169ae3e57aeSPeter Chen 170ae3e57aeSPeter Chen data->clk_ahb = devm_clk_get(dev, "ahb"); 171ae3e57aeSPeter Chen if (IS_ERR(data->clk_ahb)) { 172ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_ahb); 173ae3e57aeSPeter Chen dev_err(dev, 174ae3e57aeSPeter Chen "Failed to get ahb clock, err=%d\n", ret); 175ae3e57aeSPeter Chen return ret; 176ae3e57aeSPeter Chen } 177ae3e57aeSPeter Chen 178ae3e57aeSPeter Chen data->clk_per = devm_clk_get(dev, "per"); 179ae3e57aeSPeter Chen if (IS_ERR(data->clk_per)) { 180ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_per); 181ae3e57aeSPeter Chen dev_err(dev, 182ae3e57aeSPeter Chen "Failed to get per clock, err=%d\n", ret); 183ae3e57aeSPeter Chen return ret; 184ae3e57aeSPeter Chen } 185ae3e57aeSPeter Chen 186ae3e57aeSPeter Chen data->need_three_clks = true; 187ae3e57aeSPeter Chen return ret; 188ae3e57aeSPeter Chen } 189ae3e57aeSPeter Chen 190ae3e57aeSPeter Chen static int imx_prepare_enable_clks(struct device *dev) 191ae3e57aeSPeter Chen { 192ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 193ae3e57aeSPeter Chen int ret = 0; 194ae3e57aeSPeter Chen 195ae3e57aeSPeter Chen if (data->need_three_clks) { 196ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ipg); 197ae3e57aeSPeter Chen if (ret) { 198ae3e57aeSPeter Chen dev_err(dev, 199ae3e57aeSPeter Chen "Failed to prepare/enable ipg clk, err=%d\n", 200ae3e57aeSPeter Chen ret); 201ae3e57aeSPeter Chen return ret; 202ae3e57aeSPeter Chen } 203ae3e57aeSPeter Chen 204ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ahb); 205ae3e57aeSPeter Chen if (ret) { 206ae3e57aeSPeter Chen dev_err(dev, 207ae3e57aeSPeter Chen "Failed to prepare/enable ahb clk, err=%d\n", 208ae3e57aeSPeter Chen ret); 209ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 210ae3e57aeSPeter Chen return ret; 211ae3e57aeSPeter Chen } 212ae3e57aeSPeter Chen 213ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_per); 214ae3e57aeSPeter Chen if (ret) { 215ae3e57aeSPeter Chen dev_err(dev, 216ae3e57aeSPeter Chen "Failed to prepare/enable per clk, err=%d\n", 217ae3e57aeSPeter Chen ret); 218ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 219ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 220ae3e57aeSPeter Chen return ret; 221ae3e57aeSPeter Chen } 222ae3e57aeSPeter Chen } else { 223ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk); 224ae3e57aeSPeter Chen if (ret) { 225ae3e57aeSPeter Chen dev_err(dev, 226ae3e57aeSPeter Chen "Failed to prepare/enable clk, err=%d\n", 227ae3e57aeSPeter Chen ret); 228ae3e57aeSPeter Chen return ret; 229ae3e57aeSPeter Chen } 230ae3e57aeSPeter Chen } 231ae3e57aeSPeter Chen 232ae3e57aeSPeter Chen return ret; 233ae3e57aeSPeter Chen } 234ae3e57aeSPeter Chen 235ae3e57aeSPeter Chen static void imx_disable_unprepare_clks(struct device *dev) 236ae3e57aeSPeter Chen { 237ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 238ae3e57aeSPeter Chen 239ae3e57aeSPeter Chen if (data->need_three_clks) { 240ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_per); 241ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 242ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 243ae3e57aeSPeter Chen } else { 244ae3e57aeSPeter Chen clk_disable_unprepare(data->clk); 245ae3e57aeSPeter Chen } 246ae3e57aeSPeter Chen } 2478e22978cSAlexander Shishkin 2488e22978cSAlexander Shishkin static int ci_hdrc_imx_probe(struct platform_device *pdev) 2498e22978cSAlexander Shishkin { 2508e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data; 2518e22978cSAlexander Shishkin struct ci_hdrc_platform_data pdata = { 252c844d6c8SAlexander Shiyan .name = dev_name(&pdev->dev), 2538e22978cSAlexander Shishkin .capoffset = DEF_CAPOFFSET, 2548e22978cSAlexander Shishkin }; 2558e22978cSAlexander Shishkin int ret; 2566f51bc34SLABBE Corentin const struct of_device_id *of_id; 2576f51bc34SLABBE Corentin const struct ci_hdrc_imx_platform_flag *imx_platform_flag; 258be9cae24SSebastian Reichel struct device_node *np = pdev->dev.of_node; 2596f51bc34SLABBE Corentin 2606f51bc34SLABBE Corentin of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); 2616f51bc34SLABBE Corentin if (!of_id) 2626f51bc34SLABBE Corentin return -ENODEV; 2636f51bc34SLABBE Corentin 2646f51bc34SLABBE Corentin imx_platform_flag = of_id->data; 2658e22978cSAlexander Shishkin 2668e22978cSAlexander Shishkin data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 26773529828SFabio Estevam if (!data) 2688e22978cSAlexander Shishkin return -ENOMEM; 2698e22978cSAlexander Shishkin 270ae3e57aeSPeter Chen platform_set_drvdata(pdev, data); 27105986ba9SSascha Hauer data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); 27205986ba9SSascha Hauer if (IS_ERR(data->usbmisc_data)) 27305986ba9SSascha Hauer return PTR_ERR(data->usbmisc_data); 27405986ba9SSascha Hauer 275ae3e57aeSPeter Chen ret = imx_get_clks(&pdev->dev); 276ae3e57aeSPeter Chen if (ret) 2778e22978cSAlexander Shishkin return ret; 278ae3e57aeSPeter Chen 279ae3e57aeSPeter Chen ret = imx_prepare_enable_clks(&pdev->dev); 280ae3e57aeSPeter Chen if (ret) 281ae3e57aeSPeter Chen return ret; 2828e22978cSAlexander Shishkin 283046916deSFabio Estevam data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); 284af59a8b1SPeter Chen if (IS_ERR(data->phy)) { 285af59a8b1SPeter Chen ret = PTR_ERR(data->phy); 28616853d7bSMarkus Pargmann /* Return -EINVAL if no usbphy is available */ 28716853d7bSMarkus Pargmann if (ret == -ENODEV) 28816853d7bSMarkus Pargmann ret = -EINVAL; 2898e22978cSAlexander Shishkin goto err_clk; 2908e22978cSAlexander Shishkin } 2918e22978cSAlexander Shishkin 292ef44cb42SAntoine Tenart pdata.usb_phy = data->phy; 293be9cae24SSebastian Reichel 29403e6275aSAndrey Smirnov if ((of_device_is_compatible(np, "fsl,imx53-usb") || 29503e6275aSAndrey Smirnov of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy && 296be9cae24SSebastian Reichel of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) { 297be9cae24SSebastian Reichel pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL; 298be9cae24SSebastian Reichel data->override_phy_control = true; 299be9cae24SSebastian Reichel usb_phy_init(pdata.usb_phy); 300be9cae24SSebastian Reichel } 301be9cae24SSebastian Reichel 30256040087SPeter Chen pdata.flags |= imx_platform_flag->flags; 303e14db48dSPeter Chen if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) 304e14db48dSPeter Chen data->supports_runtime_pm = true; 3051071055eSPeter Chen 30605986ba9SSascha Hauer ret = imx_usbmisc_init(data->usbmisc_data); 3078e22978cSAlexander Shishkin if (ret) { 308a4cf1b14SPeter Chen dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); 309af59a8b1SPeter Chen goto err_clk; 3108e22978cSAlexander Shishkin } 3118e22978cSAlexander Shishkin 3128e22978cSAlexander Shishkin data->ci_pdev = ci_hdrc_add_device(&pdev->dev, 3138e22978cSAlexander Shishkin pdev->resource, pdev->num_resources, 3148e22978cSAlexander Shishkin &pdata); 3158e22978cSAlexander Shishkin if (IS_ERR(data->ci_pdev)) { 3168e22978cSAlexander Shishkin ret = PTR_ERR(data->ci_pdev); 317d3d8425aSStefan Agner if (ret != -EPROBE_DEFER) 3188e22978cSAlexander Shishkin dev_err(&pdev->dev, 319d3d8425aSStefan Agner "ci_hdrc_add_device failed, err=%d\n", ret); 320af59a8b1SPeter Chen goto err_clk; 3218e22978cSAlexander Shishkin } 3228e22978cSAlexander Shishkin 32305986ba9SSascha Hauer ret = imx_usbmisc_init_post(data->usbmisc_data); 3248e22978cSAlexander Shishkin if (ret) { 325a4cf1b14SPeter Chen dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret); 3268e22978cSAlexander Shishkin goto disable_device; 3278e22978cSAlexander Shishkin } 3288e22978cSAlexander Shishkin 329e14db48dSPeter Chen if (data->supports_runtime_pm) { 330e14db48dSPeter Chen pm_runtime_set_active(&pdev->dev); 3318e22978cSAlexander Shishkin pm_runtime_enable(&pdev->dev); 332e14db48dSPeter Chen } 3338e22978cSAlexander Shishkin 3346d653110SPeter Chen device_set_wakeup_capable(&pdev->dev, true); 3356d653110SPeter Chen 3368e22978cSAlexander Shishkin return 0; 3378e22978cSAlexander Shishkin 3388e22978cSAlexander Shishkin disable_device: 3398e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 3408e22978cSAlexander Shishkin err_clk: 341ae3e57aeSPeter Chen imx_disable_unprepare_clks(&pdev->dev); 3428e22978cSAlexander Shishkin return ret; 3438e22978cSAlexander Shishkin } 3448e22978cSAlexander Shishkin 3458e22978cSAlexander Shishkin static int ci_hdrc_imx_remove(struct platform_device *pdev) 3468e22978cSAlexander Shishkin { 3478e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); 3488e22978cSAlexander Shishkin 349e14db48dSPeter Chen if (data->supports_runtime_pm) { 350e14db48dSPeter Chen pm_runtime_get_sync(&pdev->dev); 3518e22978cSAlexander Shishkin pm_runtime_disable(&pdev->dev); 352e14db48dSPeter Chen pm_runtime_put_noidle(&pdev->dev); 353e14db48dSPeter Chen } 3548e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 355be9cae24SSebastian Reichel if (data->override_phy_control) 356be9cae24SSebastian Reichel usb_phy_shutdown(data->phy); 357ae3e57aeSPeter Chen imx_disable_unprepare_clks(&pdev->dev); 3588e22978cSAlexander Shishkin 3598e22978cSAlexander Shishkin return 0; 3608e22978cSAlexander Shishkin } 3618e22978cSAlexander Shishkin 362b09b5224SAndreas Fenkart static void ci_hdrc_imx_shutdown(struct platform_device *pdev) 363b09b5224SAndreas Fenkart { 364b09b5224SAndreas Fenkart ci_hdrc_imx_remove(pdev); 365b09b5224SAndreas Fenkart } 366b09b5224SAndreas Fenkart 367*9f644a64SMarcus Folkesson static int __maybe_unused imx_controller_suspend(struct device *dev) 3682558c1f5SPeter Chen { 3692558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 3702558c1f5SPeter Chen 3712558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 3722558c1f5SPeter Chen 373ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 374e14db48dSPeter Chen data->in_lpm = true; 3752558c1f5SPeter Chen 3762558c1f5SPeter Chen return 0; 3772558c1f5SPeter Chen } 3782558c1f5SPeter Chen 379*9f644a64SMarcus Folkesson static int __maybe_unused imx_controller_resume(struct device *dev) 3802558c1f5SPeter Chen { 3812558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 382e14db48dSPeter Chen int ret = 0; 3832558c1f5SPeter Chen 3842558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 3852558c1f5SPeter Chen 386e14db48dSPeter Chen if (!data->in_lpm) { 387e14db48dSPeter Chen WARN_ON(1); 388e14db48dSPeter Chen return 0; 3892558c1f5SPeter Chen } 3902558c1f5SPeter Chen 391ae3e57aeSPeter Chen ret = imx_prepare_enable_clks(dev); 392e14db48dSPeter Chen if (ret) 393e14db48dSPeter Chen return ret; 394e14db48dSPeter Chen 395e14db48dSPeter Chen data->in_lpm = false; 396e14db48dSPeter Chen 397e14db48dSPeter Chen ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); 398e14db48dSPeter Chen if (ret) { 399e14db48dSPeter Chen dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 400e14db48dSPeter Chen goto clk_disable; 401e14db48dSPeter Chen } 402e14db48dSPeter Chen 403e14db48dSPeter Chen return 0; 404e14db48dSPeter Chen 405e14db48dSPeter Chen clk_disable: 406ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 407e14db48dSPeter Chen return ret; 408e14db48dSPeter Chen } 409e14db48dSPeter Chen 410*9f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev) 4112558c1f5SPeter Chen { 4126d653110SPeter Chen int ret; 4136d653110SPeter Chen 414e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 415e14db48dSPeter Chen 416e14db48dSPeter Chen if (data->in_lpm) 417e14db48dSPeter Chen /* The core's suspend doesn't run */ 418e14db48dSPeter Chen return 0; 419e14db48dSPeter Chen 4206d653110SPeter Chen if (device_may_wakeup(dev)) { 4216d653110SPeter Chen ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 4226d653110SPeter Chen if (ret) { 4236d653110SPeter Chen dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", 4246d653110SPeter Chen ret); 4256d653110SPeter Chen return ret; 4266d653110SPeter Chen } 4276d653110SPeter Chen } 4286d653110SPeter Chen 4292558c1f5SPeter Chen return imx_controller_suspend(dev); 4302558c1f5SPeter Chen } 4312558c1f5SPeter Chen 432*9f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_resume(struct device *dev) 4332558c1f5SPeter Chen { 434e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 435e14db48dSPeter Chen int ret; 436e14db48dSPeter Chen 437e14db48dSPeter Chen ret = imx_controller_resume(dev); 438e14db48dSPeter Chen if (!ret && data->supports_runtime_pm) { 439e14db48dSPeter Chen pm_runtime_disable(dev); 440e14db48dSPeter Chen pm_runtime_set_active(dev); 441e14db48dSPeter Chen pm_runtime_enable(dev); 442e14db48dSPeter Chen } 443e14db48dSPeter Chen 444e14db48dSPeter Chen return ret; 4452558c1f5SPeter Chen } 4462558c1f5SPeter Chen 447*9f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev) 448e14db48dSPeter Chen { 449e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 450e14db48dSPeter Chen int ret; 451e14db48dSPeter Chen 452e14db48dSPeter Chen if (data->in_lpm) { 453e14db48dSPeter Chen WARN_ON(1); 454e14db48dSPeter Chen return 0; 455e14db48dSPeter Chen } 456e14db48dSPeter Chen 457e14db48dSPeter Chen ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 458e14db48dSPeter Chen if (ret) { 459e14db48dSPeter Chen dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 460e14db48dSPeter Chen return ret; 461e14db48dSPeter Chen } 462e14db48dSPeter Chen 463e14db48dSPeter Chen return imx_controller_suspend(dev); 464e14db48dSPeter Chen } 465e14db48dSPeter Chen 466*9f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev) 467e14db48dSPeter Chen { 468e14db48dSPeter Chen return imx_controller_resume(dev); 469e14db48dSPeter Chen } 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