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