blob: 005ad5f11b69321377d906f17c1ab0086f97d290 [file] [log] [blame]
Richard Zhao15302802012-07-07 22:56:48 +08001/*
2 * Copyright 2012 Freescale Semiconductor, Inc.
3 * Copyright (C) 2012 Marek Vasut <marex@denx.de>
4 * on behalf of DENX Software Engineering GmbH
5 *
6 * The code contained herein is licensed under the GNU General Public
7 * License. You may obtain a copy of the GNU General Public License
8 * Version 2 or later at the following locations:
9 *
10 * http://www.opensource.org/licenses/gpl-license.html
11 * http://www.gnu.org/copyleft/gpl.html
12 */
13
14#include <linux/module.h>
15#include <linux/of_platform.h>
16#include <linux/of_gpio.h>
17#include <linux/platform_device.h>
18#include <linux/pm_runtime.h>
19#include <linux/dma-mapping.h>
20#include <linux/usb/chipidea.h>
21#include <linux/clk.h>
22#include <linux/regulator/consumer.h>
23
24#include "ci.h"
Alexander Shishkin8e229782013-06-24 14:46:36 +030025#include "ci_hdrc_imx.h"
Richard Zhao15302802012-07-07 22:56:48 +080026
Alexander Shishkin8e229782013-06-24 14:46:36 +030027struct ci_hdrc_imx_data {
Richard Zhao15302802012-07-07 22:56:48 +080028 struct usb_phy *phy;
29 struct platform_device *ci_pdev;
30 struct clk *clk;
31 struct regulator *reg_vbus;
32};
33
Richard Zhaod142d6b2012-09-12 14:58:05 +030034static const struct usbmisc_ops *usbmisc_ops;
35
36/* Common functions shared by usbmisc drivers */
37
38int usbmisc_set_ops(const struct usbmisc_ops *ops)
39{
40 if (usbmisc_ops)
41 return -EBUSY;
42
43 usbmisc_ops = ops;
44
45 return 0;
46}
47EXPORT_SYMBOL_GPL(usbmisc_set_ops);
48
49void usbmisc_unset_ops(const struct usbmisc_ops *ops)
50{
51 usbmisc_ops = NULL;
52}
53EXPORT_SYMBOL_GPL(usbmisc_unset_ops);
54
55int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev)
56{
57 struct device_node *np = dev->of_node;
58 struct of_phandle_args args;
59 int ret;
60
61 usbdev->dev = dev;
62
63 ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
64 0, &args);
65 if (ret) {
66 dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
67 ret);
68 memset(usbdev, 0, sizeof(*usbdev));
69 return ret;
70 }
71 usbdev->index = args.args[0];
72 of_node_put(args.np);
73
74 if (of_find_property(np, "disable-over-current", NULL))
75 usbdev->disable_oc = 1;
76
Michael Grzeschika0685332013-03-30 12:54:01 +020077 if (of_find_property(np, "external-vbus-divider", NULL))
78 usbdev->evdo = 1;
79
Richard Zhaod142d6b2012-09-12 14:58:05 +030080 return 0;
81}
82EXPORT_SYMBOL_GPL(usbmisc_get_init_data);
83
84/* End of common functions shared by usbmisc drivers*/
85
Alexander Shishkin8e229782013-06-24 14:46:36 +030086static int ci_hdrc_imx_probe(struct platform_device *pdev)
Richard Zhao15302802012-07-07 22:56:48 +080087{
Alexander Shishkin8e229782013-06-24 14:46:36 +030088 struct ci_hdrc_imx_data *data;
89 struct ci_hdrc_platform_data pdata = {
90 .name = "ci_hdrc_imx",
Michael Grzeschikf6a3b3a2013-06-13 17:59:58 +030091 .capoffset = DEF_CAPOFFSET,
Alexander Shishkin8e229782013-06-24 14:46:36 +030092 .flags = CI_HDRC_REQUIRE_TRANSCEIVER |
93 CI_HDRC_PULLUP_ON_VBUS |
94 CI_HDRC_DISABLE_STREAMING,
Michael Grzeschikf6a3b3a2013-06-13 17:59:58 +030095 };
Richard Zhao15302802012-07-07 22:56:48 +080096 int ret;
97
Richard Zhaod142d6b2012-09-12 14:58:05 +030098 if (of_find_property(pdev->dev.of_node, "fsl,usbmisc", NULL)
99 && !usbmisc_ops)
100 return -EPROBE_DEFER;
101
Richard Zhao15302802012-07-07 22:56:48 +0800102 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
103 if (!data) {
Alexander Shishkin8e229782013-06-24 14:46:36 +0300104 dev_err(&pdev->dev, "Failed to allocate ci_hdrc-imx data!\n");
Richard Zhao15302802012-07-07 22:56:48 +0800105 return -ENOMEM;
106 }
107
Richard Zhao15302802012-07-07 22:56:48 +0800108 data->clk = devm_clk_get(&pdev->dev, NULL);
109 if (IS_ERR(data->clk)) {
110 dev_err(&pdev->dev,
111 "Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
112 return PTR_ERR(data->clk);
113 }
114
115 ret = clk_prepare_enable(data->clk);
116 if (ret) {
117 dev_err(&pdev->dev,
118 "Failed to prepare or enable clock, err=%d\n", ret);
119 return ret;
120 }
121
Fabio Estevam046916d2013-06-25 12:58:05 +0300122 data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
123 if (!IS_ERR(data->phy)) {
124 ret = usb_phy_init(data->phy);
Sascha Hauerea1418b2013-06-13 18:00:00 +0300125 if (ret) {
126 dev_err(&pdev->dev, "unable to init phy: %d\n", ret);
127 goto err_clk;
Richard Zhao15302802012-07-07 22:56:48 +0800128 }
Fabio Estevam046916d2013-06-25 12:58:05 +0300129 } else if (PTR_ERR(data->phy) == -EPROBE_DEFER) {
Sascha Hauerea1418b2013-06-13 18:00:00 +0300130 ret = -EPROBE_DEFER;
131 goto err_clk;
Richard Zhao15302802012-07-07 22:56:48 +0800132 }
133
134 /* we only support host now, so enable vbus here */
Fabio Estevame56ae542013-06-13 17:59:49 +0300135 data->reg_vbus = devm_regulator_get(&pdev->dev, "vbus");
136 if (!IS_ERR(data->reg_vbus)) {
137 ret = regulator_enable(data->reg_vbus);
Richard Zhao15302802012-07-07 22:56:48 +0800138 if (ret) {
139 dev_err(&pdev->dev,
140 "Failed to enable vbus regulator, err=%d\n",
141 ret);
Sascha Hauerea1418b2013-06-13 18:00:00 +0300142 goto err_clk;
Richard Zhao15302802012-07-07 22:56:48 +0800143 }
Richard Zhao15302802012-07-07 22:56:48 +0800144 } else {
Fabio Estevame56ae542013-06-13 17:59:49 +0300145 data->reg_vbus = NULL;
Richard Zhao15302802012-07-07 22:56:48 +0800146 }
147
Michael Grzeschikf6a3b3a2013-06-13 17:59:58 +0300148 pdata.phy = data->phy;
Richard Zhao15302802012-07-07 22:56:48 +0800149
Stephen Warren3b9561e2013-05-07 16:53:52 -0600150 if (!pdev->dev.dma_mask)
151 pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
152 if (!pdev->dev.coherent_dma_mask)
153 pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300154
155 if (usbmisc_ops && usbmisc_ops->init) {
156 ret = usbmisc_ops->init(&pdev->dev);
157 if (ret) {
158 dev_err(&pdev->dev,
159 "usbmisc init failed, ret=%d\n", ret);
160 goto err;
161 }
162 }
163
Alexander Shishkin8e229782013-06-24 14:46:36 +0300164 data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
Richard Zhao15302802012-07-07 22:56:48 +0800165 pdev->resource, pdev->num_resources,
Michael Grzeschikf6a3b3a2013-06-13 17:59:58 +0300166 &pdata);
Fabio Estevam770719d2013-06-13 17:59:48 +0300167 if (IS_ERR(data->ci_pdev)) {
168 ret = PTR_ERR(data->ci_pdev);
Richard Zhao15302802012-07-07 22:56:48 +0800169 dev_err(&pdev->dev,
170 "Can't register ci_hdrc platform device, err=%d\n",
171 ret);
172 goto err;
173 }
174
Michael Grzeschika0685332013-03-30 12:54:01 +0200175 if (usbmisc_ops && usbmisc_ops->post) {
176 ret = usbmisc_ops->post(&pdev->dev);
177 if (ret) {
178 dev_err(&pdev->dev,
179 "usbmisc post failed, ret=%d\n", ret);
Fabio Estevam770719d2013-06-13 17:59:48 +0300180 goto disable_device;
Michael Grzeschika0685332013-03-30 12:54:01 +0200181 }
182 }
183
Richard Zhao15302802012-07-07 22:56:48 +0800184 platform_set_drvdata(pdev, data);
185
186 pm_runtime_no_callbacks(&pdev->dev);
187 pm_runtime_enable(&pdev->dev);
188
189 return 0;
190
Fabio Estevam770719d2013-06-13 17:59:48 +0300191disable_device:
Alexander Shishkin8e229782013-06-24 14:46:36 +0300192 ci_hdrc_remove_device(data->ci_pdev);
Richard Zhao15302802012-07-07 22:56:48 +0800193err:
Fabio Estevame56ae542013-06-13 17:59:49 +0300194 if (data->reg_vbus)
195 regulator_disable(data->reg_vbus);
Sascha Hauerea1418b2013-06-13 18:00:00 +0300196err_clk:
Richard Zhao15302802012-07-07 22:56:48 +0800197 clk_disable_unprepare(data->clk);
198 return ret;
199}
200
Alexander Shishkin8e229782013-06-24 14:46:36 +0300201static int ci_hdrc_imx_remove(struct platform_device *pdev)
Richard Zhao15302802012-07-07 22:56:48 +0800202{
Alexander Shishkin8e229782013-06-24 14:46:36 +0300203 struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
Richard Zhao15302802012-07-07 22:56:48 +0800204
205 pm_runtime_disable(&pdev->dev);
Alexander Shishkin8e229782013-06-24 14:46:36 +0300206 ci_hdrc_remove_device(data->ci_pdev);
Richard Zhao15302802012-07-07 22:56:48 +0800207
208 if (data->reg_vbus)
209 regulator_disable(data->reg_vbus);
210
Lothar Waßmann769d92c2013-08-14 12:43:59 +0300211 if (data->phy)
Richard Zhao15302802012-07-07 22:56:48 +0800212 usb_phy_shutdown(data->phy);
Richard Zhao15302802012-07-07 22:56:48 +0800213
Richard Zhao15302802012-07-07 22:56:48 +0800214 clk_disable_unprepare(data->clk);
215
Richard Zhao15302802012-07-07 22:56:48 +0800216 return 0;
217}
218
Alexander Shishkin8e229782013-06-24 14:46:36 +0300219static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
Richard Zhao15302802012-07-07 22:56:48 +0800220 { .compatible = "fsl,imx27-usb", },
221 { /* sentinel */ }
222};
Alexander Shishkin8e229782013-06-24 14:46:36 +0300223MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
Richard Zhao15302802012-07-07 22:56:48 +0800224
Alexander Shishkin8e229782013-06-24 14:46:36 +0300225static struct platform_driver ci_hdrc_imx_driver = {
226 .probe = ci_hdrc_imx_probe,
227 .remove = ci_hdrc_imx_remove,
Richard Zhao15302802012-07-07 22:56:48 +0800228 .driver = {
229 .name = "imx_usb",
230 .owner = THIS_MODULE,
Alexander Shishkin8e229782013-06-24 14:46:36 +0300231 .of_match_table = ci_hdrc_imx_dt_ids,
Richard Zhao15302802012-07-07 22:56:48 +0800232 },
233};
234
Alexander Shishkin8e229782013-06-24 14:46:36 +0300235module_platform_driver(ci_hdrc_imx_driver);
Richard Zhao15302802012-07-07 22:56:48 +0800236
237MODULE_ALIAS("platform:imx-usb");
238MODULE_LICENSE("GPL v2");
Alexander Shishkin8e229782013-06-24 14:46:36 +0300239MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
Richard Zhao15302802012-07-07 22:56:48 +0800240MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
241MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");