]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/usb/chipidea/ci_hdrc_imx.c
xhci: Add missing unlocks on error paths
[mirror_ubuntu-bionic-kernel.git] / drivers / usb / chipidea / ci_hdrc_imx.c
CommitLineData
15302800
RZ
1/*
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"
8e22978c 25#include "ci_hdrc_imx.h"
15302800
RZ
26
27#define pdev_to_phy(pdev) \
28 ((struct usb_phy *)platform_get_drvdata(pdev))
29
8e22978c 30struct ci_hdrc_imx_data {
15302800
RZ
31 struct usb_phy *phy;
32 struct platform_device *ci_pdev;
33 struct clk *clk;
34 struct regulator *reg_vbus;
35};
36
d142d6be
RZ
37static const struct usbmisc_ops *usbmisc_ops;
38
39/* Common functions shared by usbmisc drivers */
40
41int usbmisc_set_ops(const struct usbmisc_ops *ops)
42{
43 if (usbmisc_ops)
44 return -EBUSY;
45
46 usbmisc_ops = ops;
47
48 return 0;
49}
50EXPORT_SYMBOL_GPL(usbmisc_set_ops);
51
52void usbmisc_unset_ops(const struct usbmisc_ops *ops)
53{
54 usbmisc_ops = NULL;
55}
56EXPORT_SYMBOL_GPL(usbmisc_unset_ops);
57
58int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev)
59{
60 struct device_node *np = dev->of_node;
61 struct of_phandle_args args;
62 int ret;
63
64 usbdev->dev = dev;
65
66 ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
67 0, &args);
68 if (ret) {
69 dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
70 ret);
71 memset(usbdev, 0, sizeof(*usbdev));
72 return ret;
73 }
74 usbdev->index = args.args[0];
75 of_node_put(args.np);
76
77 if (of_find_property(np, "disable-over-current", NULL))
78 usbdev->disable_oc = 1;
79
a0685330
MG
80 if (of_find_property(np, "external-vbus-divider", NULL))
81 usbdev->evdo = 1;
82
d142d6be
RZ
83 return 0;
84}
85EXPORT_SYMBOL_GPL(usbmisc_get_init_data);
86
87/* End of common functions shared by usbmisc drivers*/
88
8e22978c 89static int ci_hdrc_imx_probe(struct platform_device *pdev)
15302800 90{
8e22978c
AS
91 struct ci_hdrc_imx_data *data;
92 struct ci_hdrc_platform_data pdata = {
93 .name = "ci_hdrc_imx",
f6a3b3a3 94 .capoffset = DEF_CAPOFFSET,
8e22978c
AS
95 .flags = CI_HDRC_REQUIRE_TRANSCEIVER |
96 CI_HDRC_PULLUP_ON_VBUS |
97 CI_HDRC_DISABLE_STREAMING,
f6a3b3a3 98 };
15302800 99 struct resource *res;
15302800 100 int ret;
ea1418b5 101 struct usb_phy *phy;
15302800 102
d142d6be
RZ
103 if (of_find_property(pdev->dev.of_node, "fsl,usbmisc", NULL)
104 && !usbmisc_ops)
105 return -EPROBE_DEFER;
106
15302800
RZ
107 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
108 if (!data) {
8e22978c 109 dev_err(&pdev->dev, "Failed to allocate ci_hdrc-imx data!\n");
15302800
RZ
110 return -ENOMEM;
111 }
112
113 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
114 if (!res) {
115 dev_err(&pdev->dev, "Can't get device resources!\n");
116 return -ENOENT;
117 }
118
119 data->clk = devm_clk_get(&pdev->dev, NULL);
120 if (IS_ERR(data->clk)) {
121 dev_err(&pdev->dev,
122 "Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
123 return PTR_ERR(data->clk);
124 }
125
126 ret = clk_prepare_enable(data->clk);
127 if (ret) {
128 dev_err(&pdev->dev,
129 "Failed to prepare or enable clock, err=%d\n", ret);
130 return ret;
131 }
132
ea1418b5
SH
133 phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
134 if (!IS_ERR(phy)) {
135 ret = usb_phy_init(phy);
136 if (ret) {
137 dev_err(&pdev->dev, "unable to init phy: %d\n", ret);
138 goto err_clk;
15302800 139 }
ea1418b5
SH
140 } else if (PTR_ERR(phy) == -EPROBE_DEFER) {
141 ret = -EPROBE_DEFER;
142 goto err_clk;
15302800
RZ
143 }
144
145 /* we only support host now, so enable vbus here */
e56ae54f
FE
146 data->reg_vbus = devm_regulator_get(&pdev->dev, "vbus");
147 if (!IS_ERR(data->reg_vbus)) {
148 ret = regulator_enable(data->reg_vbus);
15302800
RZ
149 if (ret) {
150 dev_err(&pdev->dev,
151 "Failed to enable vbus regulator, err=%d\n",
152 ret);
ea1418b5 153 goto err_clk;
15302800 154 }
15302800 155 } else {
e56ae54f 156 data->reg_vbus = NULL;
15302800
RZ
157 }
158
f6a3b3a3 159 pdata.phy = data->phy;
15302800 160
3b9561e9
SW
161 if (!pdev->dev.dma_mask)
162 pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
163 if (!pdev->dev.coherent_dma_mask)
164 pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
d142d6be
RZ
165
166 if (usbmisc_ops && usbmisc_ops->init) {
167 ret = usbmisc_ops->init(&pdev->dev);
168 if (ret) {
169 dev_err(&pdev->dev,
170 "usbmisc init failed, ret=%d\n", ret);
171 goto err;
172 }
173 }
174
8e22978c 175 data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
15302800 176 pdev->resource, pdev->num_resources,
f6a3b3a3 177 &pdata);
770719df
FE
178 if (IS_ERR(data->ci_pdev)) {
179 ret = PTR_ERR(data->ci_pdev);
15302800
RZ
180 dev_err(&pdev->dev,
181 "Can't register ci_hdrc platform device, err=%d\n",
182 ret);
183 goto err;
184 }
185
a0685330
MG
186 if (usbmisc_ops && usbmisc_ops->post) {
187 ret = usbmisc_ops->post(&pdev->dev);
188 if (ret) {
189 dev_err(&pdev->dev,
190 "usbmisc post failed, ret=%d\n", ret);
770719df 191 goto disable_device;
a0685330
MG
192 }
193 }
194
15302800
RZ
195 platform_set_drvdata(pdev, data);
196
197 pm_runtime_no_callbacks(&pdev->dev);
198 pm_runtime_enable(&pdev->dev);
199
200 return 0;
201
770719df 202disable_device:
8e22978c 203 ci_hdrc_remove_device(data->ci_pdev);
15302800 204err:
e56ae54f
FE
205 if (data->reg_vbus)
206 regulator_disable(data->reg_vbus);
ea1418b5 207err_clk:
15302800
RZ
208 clk_disable_unprepare(data->clk);
209 return ret;
210}
211
8e22978c 212static int ci_hdrc_imx_remove(struct platform_device *pdev)
15302800 213{
8e22978c 214 struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
15302800
RZ
215
216 pm_runtime_disable(&pdev->dev);
8e22978c 217 ci_hdrc_remove_device(data->ci_pdev);
15302800
RZ
218
219 if (data->reg_vbus)
220 regulator_disable(data->reg_vbus);
221
222 if (data->phy) {
223 usb_phy_shutdown(data->phy);
224 module_put(data->phy->dev->driver->owner);
225 }
226
15302800
RZ
227 clk_disable_unprepare(data->clk);
228
15302800
RZ
229 return 0;
230}
231
8e22978c 232static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
15302800
RZ
233 { .compatible = "fsl,imx27-usb", },
234 { /* sentinel */ }
235};
8e22978c 236MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
15302800 237
8e22978c
AS
238static struct platform_driver ci_hdrc_imx_driver = {
239 .probe = ci_hdrc_imx_probe,
240 .remove = ci_hdrc_imx_remove,
15302800
RZ
241 .driver = {
242 .name = "imx_usb",
243 .owner = THIS_MODULE,
8e22978c 244 .of_match_table = ci_hdrc_imx_dt_ids,
15302800
RZ
245 },
246};
247
8e22978c 248module_platform_driver(ci_hdrc_imx_driver);
15302800
RZ
249
250MODULE_ALIAS("platform:imx-usb");
251MODULE_LICENSE("GPL v2");
8e22978c 252MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
15302800
RZ
253MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
254MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");