xref: /linux/drivers/usb/chipidea/ci_hdrc_imx.c (revision 560400875d56d6e5f589a25319febcb32238f004)
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