]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/usb/chipidea/ci_hdrc_imx.c
MLK-10088-4 usb: chipidea: imx: simplify the usbmisc callers
[karo-tx-linux.git] / drivers / usb / chipidea / ci_hdrc_imx.c
1 /*
2  * Copyright 2012-2015 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 };
29
30 static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
31 };
32
33 static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
34         .flags = CI_HDRC_IMX28_WRITE_FIX,
35 };
36
37 static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
38         { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
39         { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
40         { /* sentinel */ }
41 };
42 MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
43
44 struct ci_hdrc_imx_data {
45         struct usb_phy *phy;
46         struct platform_device *ci_pdev;
47         struct clk *clk;
48         struct imx_usbmisc_data *usbmisc_data;
49 };
50
51 /* Common functions shared by usbmisc drivers */
52
53 static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
54 {
55         struct platform_device *misc_pdev;
56         struct device_node *np = dev->of_node;
57         struct of_phandle_args args;
58         struct imx_usbmisc_data *data;
59         int ret;
60
61         /*
62          * In case the fsl,usbmisc property is not present this device doesn't
63          * need usbmisc. Return NULL (which is no error here)
64          */
65         if (!of_get_property(np, "fsl,usbmisc", NULL))
66                 return NULL;
67
68         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
69         if (!data)
70                 return ERR_PTR(-ENOMEM);
71
72         ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
73                                         0, &args);
74         if (ret) {
75                 dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
76                         ret);
77                 return ERR_PTR(ret);
78         }
79
80         data->index = args.args[0];
81
82         misc_pdev = of_find_device_by_node(args.np);
83         of_node_put(args.np);
84
85         if (!misc_pdev)
86                 return ERR_PTR(-EPROBE_DEFER);
87
88         data->dev = &misc_pdev->dev;
89
90         if (of_find_property(np, "disable-over-current", NULL))
91                 data->disable_oc = 1;
92
93         if (of_find_property(np, "external-vbus-divider", NULL))
94                 data->evdo = 1;
95
96         return data;
97 }
98
99 /* End of common functions shared by usbmisc drivers*/
100
101 static int ci_hdrc_imx_probe(struct platform_device *pdev)
102 {
103         struct ci_hdrc_imx_data *data;
104         struct ci_hdrc_platform_data pdata = {
105                 .name           = dev_name(&pdev->dev),
106                 .capoffset      = DEF_CAPOFFSET,
107                 .flags          = CI_HDRC_DISABLE_STREAMING,
108         };
109         int ret;
110         const struct of_device_id *of_id =
111                         of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
112         const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data;
113
114         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
115         if (!data)
116                 return -ENOMEM;
117
118         data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
119         if (IS_ERR(data->usbmisc_data))
120                 return PTR_ERR(data->usbmisc_data);
121
122         data->clk = devm_clk_get(&pdev->dev, NULL);
123         if (IS_ERR(data->clk)) {
124                 dev_err(&pdev->dev,
125                         "Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
126                 return PTR_ERR(data->clk);
127         }
128
129         ret = clk_prepare_enable(data->clk);
130         if (ret) {
131                 dev_err(&pdev->dev,
132                         "Failed to prepare or enable clock, err=%d\n", ret);
133                 return ret;
134         }
135
136         data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
137         if (IS_ERR(data->phy)) {
138                 ret = PTR_ERR(data->phy);
139                 /* Return -EINVAL if no usbphy is available */
140                 if (ret == -ENODEV)
141                         ret = -EINVAL;
142                 goto err_clk;
143         }
144
145         pdata.usb_phy = data->phy;
146         pdata.flags |= imx_platform_flag->flags;
147
148         ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
149         if (ret)
150                 goto err_clk;
151
152         ret = imx_usbmisc_init(data->usbmisc_data);
153         if (ret) {
154                 dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
155                 goto err_clk;
156         }
157
158         data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
159                                 pdev->resource, pdev->num_resources,
160                                 &pdata);
161         if (IS_ERR(data->ci_pdev)) {
162                 ret = PTR_ERR(data->ci_pdev);
163                 dev_err(&pdev->dev,
164                         "Can't register ci_hdrc platform device, err=%d\n",
165                         ret);
166                 goto err_clk;
167         }
168
169         ret = imx_usbmisc_init_post(data->usbmisc_data);
170         if (ret) {
171                 dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
172                 goto disable_device;
173         }
174
175         platform_set_drvdata(pdev, data);
176
177         pm_runtime_no_callbacks(&pdev->dev);
178         pm_runtime_enable(&pdev->dev);
179
180         return 0;
181
182 disable_device:
183         ci_hdrc_remove_device(data->ci_pdev);
184 err_clk:
185         clk_disable_unprepare(data->clk);
186         return ret;
187 }
188
189 static int ci_hdrc_imx_remove(struct platform_device *pdev)
190 {
191         struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
192
193         pm_runtime_disable(&pdev->dev);
194         ci_hdrc_remove_device(data->ci_pdev);
195         clk_disable_unprepare(data->clk);
196
197         return 0;
198 }
199
200 #ifdef CONFIG_PM_SLEEP
201 static int imx_controller_suspend(struct device *dev)
202 {
203         struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
204
205         dev_dbg(dev, "at %s\n", __func__);
206
207         clk_disable_unprepare(data->clk);
208
209         return 0;
210 }
211
212 static int imx_controller_resume(struct device *dev)
213 {
214         struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
215
216         dev_dbg(dev, "at %s\n", __func__);
217
218         return clk_prepare_enable(data->clk);
219 }
220
221 static int ci_hdrc_imx_suspend(struct device *dev)
222 {
223         return imx_controller_suspend(dev);
224 }
225
226 static int ci_hdrc_imx_resume(struct device *dev)
227 {
228         return imx_controller_resume(dev);
229 }
230 #endif /* CONFIG_PM_SLEEP */
231
232 static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
233         SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
234 };
235 static struct platform_driver ci_hdrc_imx_driver = {
236         .probe = ci_hdrc_imx_probe,
237         .remove = ci_hdrc_imx_remove,
238         .driver = {
239                 .name = "imx_usb",
240                 .owner = THIS_MODULE,
241                 .of_match_table = ci_hdrc_imx_dt_ids,
242                 .pm = &ci_hdrc_imx_pm_ops,
243          },
244 };
245
246 module_platform_driver(ci_hdrc_imx_driver);
247
248 MODULE_ALIAS("platform:imx-usb");
249 MODULE_LICENSE("GPL v2");
250 MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
251 MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
252 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");