]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/usb/chipidea/ci_hdrc_imx.c
Merge tag 'mfd-fixes-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
[karo-tx-linux.git] / drivers / usb / chipidea / ci_hdrc_imx.c
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
23 #include "ci.h"
24 #include "ci_hdrc_imx.h"
25
26 struct ci_hdrc_imx_platform_flag {
27         unsigned int flags;
28         bool runtime_pm;
29 };
30
31 static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
32                 CI_HDRC_DISABLE_STREAMING,
33 };
34
35 static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
36         .flags = CI_HDRC_IMX28_WRITE_FIX |
37                 CI_HDRC_TURN_VBUS_EARLY_ON |
38                 CI_HDRC_DISABLE_STREAMING,
39 };
40
41 static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
42         .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
43                 CI_HDRC_TURN_VBUS_EARLY_ON |
44                 CI_HDRC_DISABLE_STREAMING,
45 };
46
47 static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
48         .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
49                 CI_HDRC_TURN_VBUS_EARLY_ON |
50                 CI_HDRC_DISABLE_HOST_STREAMING,
51 };
52
53 static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
54         .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
55                 CI_HDRC_TURN_VBUS_EARLY_ON |
56                 CI_HDRC_DISABLE_HOST_STREAMING,
57 };
58
59 static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
60         { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
61         { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
62         { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
63         { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
64         { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
65         { /* sentinel */ }
66 };
67 MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
68
69 struct ci_hdrc_imx_data {
70         struct usb_phy *phy;
71         struct platform_device *ci_pdev;
72         struct clk *clk;
73         struct imx_usbmisc_data *usbmisc_data;
74         bool supports_runtime_pm;
75         bool in_lpm;
76 };
77
78 /* Common functions shared by usbmisc drivers */
79
80 static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
81 {
82         struct platform_device *misc_pdev;
83         struct device_node *np = dev->of_node;
84         struct of_phandle_args args;
85         struct imx_usbmisc_data *data;
86         int ret;
87
88         /*
89          * In case the fsl,usbmisc property is not present this device doesn't
90          * need usbmisc. Return NULL (which is no error here)
91          */
92         if (!of_get_property(np, "fsl,usbmisc", NULL))
93                 return NULL;
94
95         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
96         if (!data)
97                 return ERR_PTR(-ENOMEM);
98
99         ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
100                                         0, &args);
101         if (ret) {
102                 dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
103                         ret);
104                 return ERR_PTR(ret);
105         }
106
107         data->index = args.args[0];
108
109         misc_pdev = of_find_device_by_node(args.np);
110         of_node_put(args.np);
111
112         if (!misc_pdev || !platform_get_drvdata(misc_pdev))
113                 return ERR_PTR(-EPROBE_DEFER);
114
115         data->dev = &misc_pdev->dev;
116
117         if (of_find_property(np, "disable-over-current", NULL))
118                 data->disable_oc = 1;
119
120         if (of_find_property(np, "external-vbus-divider", NULL))
121                 data->evdo = 1;
122
123         return data;
124 }
125
126 /* End of common functions shared by usbmisc drivers*/
127
128 static int ci_hdrc_imx_probe(struct platform_device *pdev)
129 {
130         struct ci_hdrc_imx_data *data;
131         struct ci_hdrc_platform_data pdata = {
132                 .name           = dev_name(&pdev->dev),
133                 .capoffset      = DEF_CAPOFFSET,
134                 .flags          = CI_HDRC_SET_NON_ZERO_TTHA,
135         };
136         int ret;
137         const struct of_device_id *of_id =
138                         of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
139         const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data;
140
141         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
142         if (!data)
143                 return -ENOMEM;
144
145         data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
146         if (IS_ERR(data->usbmisc_data))
147                 return PTR_ERR(data->usbmisc_data);
148
149         data->clk = devm_clk_get(&pdev->dev, NULL);
150         if (IS_ERR(data->clk)) {
151                 dev_err(&pdev->dev,
152                         "Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
153                 return PTR_ERR(data->clk);
154         }
155
156         ret = clk_prepare_enable(data->clk);
157         if (ret) {
158                 dev_err(&pdev->dev,
159                         "Failed to prepare or enable clock, err=%d\n", ret);
160                 return ret;
161         }
162
163         data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
164         if (IS_ERR(data->phy)) {
165                 ret = PTR_ERR(data->phy);
166                 /* Return -EINVAL if no usbphy is available */
167                 if (ret == -ENODEV)
168                         ret = -EINVAL;
169                 goto err_clk;
170         }
171
172         pdata.usb_phy = data->phy;
173         pdata.flags |= imx_platform_flag->flags;
174         if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
175                 data->supports_runtime_pm = true;
176
177         ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
178         if (ret)
179                 goto err_clk;
180
181         ret = imx_usbmisc_init(data->usbmisc_data);
182         if (ret) {
183                 dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
184                 goto err_clk;
185         }
186
187         data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
188                                 pdev->resource, pdev->num_resources,
189                                 &pdata);
190         if (IS_ERR(data->ci_pdev)) {
191                 ret = PTR_ERR(data->ci_pdev);
192                 dev_err(&pdev->dev,
193                         "Can't register ci_hdrc platform device, err=%d\n",
194                         ret);
195                 goto err_clk;
196         }
197
198         ret = imx_usbmisc_init_post(data->usbmisc_data);
199         if (ret) {
200                 dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
201                 goto disable_device;
202         }
203
204         platform_set_drvdata(pdev, data);
205
206         if (data->supports_runtime_pm) {
207                 pm_runtime_set_active(&pdev->dev);
208                 pm_runtime_enable(&pdev->dev);
209         }
210
211         device_set_wakeup_capable(&pdev->dev, true);
212
213         return 0;
214
215 disable_device:
216         ci_hdrc_remove_device(data->ci_pdev);
217 err_clk:
218         clk_disable_unprepare(data->clk);
219         return ret;
220 }
221
222 static int ci_hdrc_imx_remove(struct platform_device *pdev)
223 {
224         struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
225
226         if (data->supports_runtime_pm) {
227                 pm_runtime_get_sync(&pdev->dev);
228                 pm_runtime_disable(&pdev->dev);
229                 pm_runtime_put_noidle(&pdev->dev);
230         }
231         ci_hdrc_remove_device(data->ci_pdev);
232         clk_disable_unprepare(data->clk);
233
234         return 0;
235 }
236
237 #ifdef CONFIG_PM
238 static int imx_controller_suspend(struct device *dev)
239 {
240         struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
241
242         dev_dbg(dev, "at %s\n", __func__);
243
244         clk_disable_unprepare(data->clk);
245         data->in_lpm = true;
246
247         return 0;
248 }
249
250 static int imx_controller_resume(struct device *dev)
251 {
252         struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
253         int ret = 0;
254
255         dev_dbg(dev, "at %s\n", __func__);
256
257         if (!data->in_lpm) {
258                 WARN_ON(1);
259                 return 0;
260         }
261
262         ret = clk_prepare_enable(data->clk);
263         if (ret)
264                 return ret;
265
266         data->in_lpm = false;
267
268         ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
269         if (ret) {
270                 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
271                 goto clk_disable;
272         }
273
274         return 0;
275
276 clk_disable:
277         clk_disable_unprepare(data->clk);
278         return ret;
279 }
280
281 #ifdef CONFIG_PM_SLEEP
282 static int ci_hdrc_imx_suspend(struct device *dev)
283 {
284         int ret;
285
286         struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
287
288         if (data->in_lpm)
289                 /* The core's suspend doesn't run */
290                 return 0;
291
292         if (device_may_wakeup(dev)) {
293                 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
294                 if (ret) {
295                         dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
296                                         ret);
297                         return ret;
298                 }
299         }
300
301         return imx_controller_suspend(dev);
302 }
303
304 static int ci_hdrc_imx_resume(struct device *dev)
305 {
306         struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
307         int ret;
308
309         ret = imx_controller_resume(dev);
310         if (!ret && data->supports_runtime_pm) {
311                 pm_runtime_disable(dev);
312                 pm_runtime_set_active(dev);
313                 pm_runtime_enable(dev);
314         }
315
316         return ret;
317 }
318 #endif /* CONFIG_PM_SLEEP */
319
320 static int ci_hdrc_imx_runtime_suspend(struct device *dev)
321 {
322         struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
323         int ret;
324
325         if (data->in_lpm) {
326                 WARN_ON(1);
327                 return 0;
328         }
329
330         ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
331         if (ret) {
332                 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
333                 return ret;
334         }
335
336         return imx_controller_suspend(dev);
337 }
338
339 static int ci_hdrc_imx_runtime_resume(struct device *dev)
340 {
341         return imx_controller_resume(dev);
342 }
343
344 #endif /* CONFIG_PM */
345
346 static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
347         SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
348         SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
349                         ci_hdrc_imx_runtime_resume, NULL)
350 };
351 static struct platform_driver ci_hdrc_imx_driver = {
352         .probe = ci_hdrc_imx_probe,
353         .remove = ci_hdrc_imx_remove,
354         .driver = {
355                 .name = "imx_usb",
356                 .of_match_table = ci_hdrc_imx_dt_ids,
357                 .pm = &ci_hdrc_imx_pm_ops,
358          },
359 };
360
361 module_platform_driver(ci_hdrc_imx_driver);
362
363 MODULE_ALIAS("platform:imx-usb");
364 MODULE_LICENSE("GPL v2");
365 MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
366 MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
367 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");