18e22978cSAlexander Shishkin /* 28e22978cSAlexander Shishkin * Copyright 2012 Freescale Semiconductor, Inc. 38e22978cSAlexander Shishkin * Copyright (C) 2012 Marek Vasut <marex@denx.de> 48e22978cSAlexander Shishkin * on behalf of DENX Software Engineering GmbH 58e22978cSAlexander Shishkin * 68e22978cSAlexander Shishkin * The code contained herein is licensed under the GNU General Public 78e22978cSAlexander Shishkin * License. You may obtain a copy of the GNU General Public License 88e22978cSAlexander Shishkin * Version 2 or later at the following locations: 98e22978cSAlexander Shishkin * 108e22978cSAlexander Shishkin * http://www.opensource.org/licenses/gpl-license.html 118e22978cSAlexander Shishkin * http://www.gnu.org/copyleft/gpl.html 128e22978cSAlexander Shishkin */ 138e22978cSAlexander Shishkin 148e22978cSAlexander Shishkin #include <linux/module.h> 158e22978cSAlexander Shishkin #include <linux/of_platform.h> 168e22978cSAlexander Shishkin #include <linux/of_gpio.h> 178e22978cSAlexander Shishkin #include <linux/platform_device.h> 188e22978cSAlexander Shishkin #include <linux/pm_runtime.h> 198e22978cSAlexander Shishkin #include <linux/dma-mapping.h> 208e22978cSAlexander Shishkin #include <linux/usb/chipidea.h> 218e22978cSAlexander Shishkin #include <linux/clk.h> 228e22978cSAlexander Shishkin 238e22978cSAlexander Shishkin #include "ci.h" 248e22978cSAlexander Shishkin #include "ci_hdrc_imx.h" 258e22978cSAlexander Shishkin 261071055eSPeter Chen struct ci_hdrc_imx_platform_flag { 271071055eSPeter Chen unsigned int flags; 281071055eSPeter Chen }; 291071055eSPeter Chen 301071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { 311071055eSPeter Chen }; 321071055eSPeter Chen 331071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { 34*56040087SPeter Chen .flags = CI_HDRC_IMX28_WRITE_FIX, 351071055eSPeter Chen }; 361071055eSPeter Chen 371071055eSPeter Chen static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 381071055eSPeter Chen { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 391071055eSPeter Chen { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 401071055eSPeter Chen { /* sentinel */ } 411071055eSPeter Chen }; 421071055eSPeter Chen MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 431071055eSPeter Chen 448e22978cSAlexander Shishkin struct ci_hdrc_imx_data { 458e22978cSAlexander Shishkin struct usb_phy *phy; 468e22978cSAlexander Shishkin struct platform_device *ci_pdev; 478e22978cSAlexander Shishkin struct clk *clk; 4805986ba9SSascha Hauer struct imx_usbmisc_data *usbmisc_data; 498e22978cSAlexander Shishkin }; 508e22978cSAlexander Shishkin 518e22978cSAlexander Shishkin /* Common functions shared by usbmisc drivers */ 528e22978cSAlexander Shishkin 5305986ba9SSascha Hauer static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) 548e22978cSAlexander Shishkin { 55f40017e0SStefan Agner struct platform_device *misc_pdev; 568e22978cSAlexander Shishkin struct device_node *np = dev->of_node; 578e22978cSAlexander Shishkin struct of_phandle_args args; 5805986ba9SSascha Hauer struct imx_usbmisc_data *data; 598e22978cSAlexander Shishkin int ret; 608e22978cSAlexander Shishkin 6105986ba9SSascha Hauer /* 6205986ba9SSascha Hauer * In case the fsl,usbmisc property is not present this device doesn't 6305986ba9SSascha Hauer * need usbmisc. Return NULL (which is no error here) 6405986ba9SSascha Hauer */ 6505986ba9SSascha Hauer if (!of_get_property(np, "fsl,usbmisc", NULL)) 6605986ba9SSascha Hauer return NULL; 6705986ba9SSascha Hauer 6805986ba9SSascha Hauer data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 6905986ba9SSascha Hauer if (!data) 7005986ba9SSascha Hauer return ERR_PTR(-ENOMEM); 718e22978cSAlexander Shishkin 728e22978cSAlexander Shishkin ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 738e22978cSAlexander Shishkin 0, &args); 748e22978cSAlexander Shishkin if (ret) { 758e22978cSAlexander Shishkin dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", 768e22978cSAlexander Shishkin ret); 7705986ba9SSascha Hauer return ERR_PTR(ret); 788e22978cSAlexander Shishkin } 7905986ba9SSascha Hauer 8005986ba9SSascha Hauer data->index = args.args[0]; 81f40017e0SStefan Agner 82f40017e0SStefan Agner misc_pdev = of_find_device_by_node(args.np); 838e22978cSAlexander Shishkin of_node_put(args.np); 848e22978cSAlexander Shishkin 85f40017e0SStefan Agner if (!misc_pdev) 86f40017e0SStefan Agner return ERR_PTR(-EPROBE_DEFER); 87f40017e0SStefan Agner 88f40017e0SStefan Agner data->dev = &misc_pdev->dev; 89f40017e0SStefan Agner 908e22978cSAlexander Shishkin if (of_find_property(np, "disable-over-current", NULL)) 9105986ba9SSascha Hauer data->disable_oc = 1; 928e22978cSAlexander Shishkin 938e22978cSAlexander Shishkin if (of_find_property(np, "external-vbus-divider", NULL)) 9405986ba9SSascha Hauer data->evdo = 1; 958e22978cSAlexander Shishkin 9605986ba9SSascha Hauer return data; 978e22978cSAlexander Shishkin } 988e22978cSAlexander Shishkin 998e22978cSAlexander Shishkin /* End of common functions shared by usbmisc drivers*/ 1008e22978cSAlexander Shishkin 1018e22978cSAlexander Shishkin static int ci_hdrc_imx_probe(struct platform_device *pdev) 1028e22978cSAlexander Shishkin { 1038e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data; 1048e22978cSAlexander Shishkin struct ci_hdrc_platform_data pdata = { 105c844d6c8SAlexander Shiyan .name = dev_name(&pdev->dev), 1068e22978cSAlexander Shishkin .capoffset = DEF_CAPOFFSET, 107947c8859SPeter Chen .flags = CI_HDRC_DISABLE_STREAMING, 1088e22978cSAlexander Shishkin }; 1098e22978cSAlexander Shishkin int ret; 1101071055eSPeter Chen const struct of_device_id *of_id = 1111071055eSPeter Chen of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); 1121071055eSPeter Chen const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data; 1138e22978cSAlexander Shishkin 1148e22978cSAlexander Shishkin data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 11573529828SFabio Estevam if (!data) 1168e22978cSAlexander Shishkin return -ENOMEM; 1178e22978cSAlexander Shishkin 11805986ba9SSascha Hauer data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); 11905986ba9SSascha Hauer if (IS_ERR(data->usbmisc_data)) 12005986ba9SSascha Hauer return PTR_ERR(data->usbmisc_data); 12105986ba9SSascha Hauer 1228e22978cSAlexander Shishkin data->clk = devm_clk_get(&pdev->dev, NULL); 1238e22978cSAlexander Shishkin if (IS_ERR(data->clk)) { 1248e22978cSAlexander Shishkin dev_err(&pdev->dev, 1258e22978cSAlexander Shishkin "Failed to get clock, err=%ld\n", PTR_ERR(data->clk)); 1268e22978cSAlexander Shishkin return PTR_ERR(data->clk); 1278e22978cSAlexander Shishkin } 1288e22978cSAlexander Shishkin 1298e22978cSAlexander Shishkin ret = clk_prepare_enable(data->clk); 1308e22978cSAlexander Shishkin if (ret) { 1318e22978cSAlexander Shishkin dev_err(&pdev->dev, 1328e22978cSAlexander Shishkin "Failed to prepare or enable clock, err=%d\n", ret); 1338e22978cSAlexander Shishkin return ret; 1348e22978cSAlexander Shishkin } 1358e22978cSAlexander Shishkin 136046916deSFabio Estevam data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); 137af59a8b1SPeter Chen if (IS_ERR(data->phy)) { 138af59a8b1SPeter Chen ret = PTR_ERR(data->phy); 13916853d7bSMarkus Pargmann /* Return -EINVAL if no usbphy is available */ 14016853d7bSMarkus Pargmann if (ret == -ENODEV) 14116853d7bSMarkus Pargmann ret = -EINVAL; 1428e22978cSAlexander Shishkin goto err_clk; 1438e22978cSAlexander Shishkin } 1448e22978cSAlexander Shishkin 145ef44cb42SAntoine Tenart pdata.usb_phy = data->phy; 146*56040087SPeter Chen pdata.flags |= imx_platform_flag->flags; 1471071055eSPeter Chen 148e1fd7341SRussell King ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 14922d9d8e8SRussell King if (ret) 15022d9d8e8SRussell King goto err_clk; 1518e22978cSAlexander Shishkin 15205986ba9SSascha Hauer if (data->usbmisc_data) { 15305986ba9SSascha Hauer ret = imx_usbmisc_init(data->usbmisc_data); 1548e22978cSAlexander Shishkin if (ret) { 15505986ba9SSascha Hauer dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", 15605986ba9SSascha Hauer ret); 157af59a8b1SPeter Chen goto err_clk; 1588e22978cSAlexander Shishkin } 1598e22978cSAlexander Shishkin } 1608e22978cSAlexander Shishkin 1618e22978cSAlexander Shishkin data->ci_pdev = ci_hdrc_add_device(&pdev->dev, 1628e22978cSAlexander Shishkin pdev->resource, pdev->num_resources, 1638e22978cSAlexander Shishkin &pdata); 1648e22978cSAlexander Shishkin if (IS_ERR(data->ci_pdev)) { 1658e22978cSAlexander Shishkin ret = PTR_ERR(data->ci_pdev); 1668e22978cSAlexander Shishkin dev_err(&pdev->dev, 1678e22978cSAlexander Shishkin "Can't register ci_hdrc platform device, err=%d\n", 1688e22978cSAlexander Shishkin ret); 169af59a8b1SPeter Chen goto err_clk; 1708e22978cSAlexander Shishkin } 1718e22978cSAlexander Shishkin 17205986ba9SSascha Hauer if (data->usbmisc_data) { 17305986ba9SSascha Hauer ret = imx_usbmisc_init_post(data->usbmisc_data); 1748e22978cSAlexander Shishkin if (ret) { 17505986ba9SSascha Hauer dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", 17605986ba9SSascha Hauer ret); 1778e22978cSAlexander Shishkin goto disable_device; 1788e22978cSAlexander Shishkin } 1798e22978cSAlexander Shishkin } 1808e22978cSAlexander Shishkin 1818e22978cSAlexander Shishkin platform_set_drvdata(pdev, data); 1828e22978cSAlexander Shishkin 1838e22978cSAlexander Shishkin pm_runtime_no_callbacks(&pdev->dev); 1848e22978cSAlexander Shishkin pm_runtime_enable(&pdev->dev); 1858e22978cSAlexander Shishkin 1868e22978cSAlexander Shishkin return 0; 1878e22978cSAlexander Shishkin 1888e22978cSAlexander Shishkin disable_device: 1898e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 1908e22978cSAlexander Shishkin err_clk: 1918e22978cSAlexander Shishkin clk_disable_unprepare(data->clk); 1928e22978cSAlexander Shishkin return ret; 1938e22978cSAlexander Shishkin } 1948e22978cSAlexander Shishkin 1958e22978cSAlexander Shishkin static int ci_hdrc_imx_remove(struct platform_device *pdev) 1968e22978cSAlexander Shishkin { 1978e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); 1988e22978cSAlexander Shishkin 1998e22978cSAlexander Shishkin pm_runtime_disable(&pdev->dev); 2008e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 2018e22978cSAlexander Shishkin clk_disable_unprepare(data->clk); 2028e22978cSAlexander Shishkin 2038e22978cSAlexander Shishkin return 0; 2048e22978cSAlexander Shishkin } 2058e22978cSAlexander Shishkin 2062558c1f5SPeter Chen #ifdef CONFIG_PM_SLEEP 2072558c1f5SPeter Chen static int imx_controller_suspend(struct device *dev) 2082558c1f5SPeter Chen { 2092558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 2102558c1f5SPeter Chen 2112558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 2122558c1f5SPeter Chen 2132558c1f5SPeter Chen clk_disable_unprepare(data->clk); 2142558c1f5SPeter Chen 2152558c1f5SPeter Chen return 0; 2162558c1f5SPeter Chen } 2172558c1f5SPeter Chen 2182558c1f5SPeter Chen static int imx_controller_resume(struct device *dev) 2192558c1f5SPeter Chen { 2202558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 2212558c1f5SPeter Chen 2222558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 2232558c1f5SPeter Chen 2242558c1f5SPeter Chen return clk_prepare_enable(data->clk); 2252558c1f5SPeter Chen } 2262558c1f5SPeter Chen 2272558c1f5SPeter Chen static int ci_hdrc_imx_suspend(struct device *dev) 2282558c1f5SPeter Chen { 2292558c1f5SPeter Chen return imx_controller_suspend(dev); 2302558c1f5SPeter Chen } 2312558c1f5SPeter Chen 2322558c1f5SPeter Chen static int ci_hdrc_imx_resume(struct device *dev) 2332558c1f5SPeter Chen { 2342558c1f5SPeter Chen return imx_controller_resume(dev); 2352558c1f5SPeter Chen } 2362558c1f5SPeter Chen #endif /* CONFIG_PM_SLEEP */ 2372558c1f5SPeter Chen 2382558c1f5SPeter Chen static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { 2392558c1f5SPeter Chen SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) 2402558c1f5SPeter Chen }; 2418e22978cSAlexander Shishkin static struct platform_driver ci_hdrc_imx_driver = { 2428e22978cSAlexander Shishkin .probe = ci_hdrc_imx_probe, 2438e22978cSAlexander Shishkin .remove = ci_hdrc_imx_remove, 2448e22978cSAlexander Shishkin .driver = { 2458e22978cSAlexander Shishkin .name = "imx_usb", 2468e22978cSAlexander Shishkin .of_match_table = ci_hdrc_imx_dt_ids, 2472558c1f5SPeter Chen .pm = &ci_hdrc_imx_pm_ops, 2488e22978cSAlexander Shishkin }, 2498e22978cSAlexander Shishkin }; 2508e22978cSAlexander Shishkin 2518e22978cSAlexander Shishkin module_platform_driver(ci_hdrc_imx_driver); 2528e22978cSAlexander Shishkin 2538e22978cSAlexander Shishkin MODULE_ALIAS("platform:imx-usb"); 2548e22978cSAlexander Shishkin MODULE_LICENSE("GPL v2"); 2558e22978cSAlexander Shishkin MODULE_DESCRIPTION("CI HDRC i.MX USB binding"); 2568e22978cSAlexander Shishkin MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 2578e22978cSAlexander Shishkin MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 258