usb: dwc3: fix logical errors and improve stability for rockchip platform
[firefly-linux-kernel-4.4.55.git] / drivers / usb / dwc3 / dwc3-rockchip.c
1 /**
2  * dwc3-rockchip.c - Rockchip Specific Glue layer
3  *
4  * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
5  *
6  * Authors: William Wu <william.wu@rock-chips.com>
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2  of
10  * the License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17
18 #include <linux/module.h>
19 #include <linux/kernel.h>
20 #include <linux/slab.h>
21 #include <linux/platform_device.h>
22 #include <linux/dma-mapping.h>
23 #include <linux/clk.h>
24 #include <linux/clk-provider.h>
25 #include <linux/of.h>
26 #include <linux/of_platform.h>
27 #include <linux/pm_runtime.h>
28 #include <linux/extcon.h>
29 #include <linux/reset.h>
30 #include <linux/usb.h>
31 #include <linux/usb/hcd.h>
32
33 #include "core.h"
34 #include "io.h"
35
36 #define DWC3_ROCKCHIP_AUTOSUSPEND_DELAY  500 /* ms */
37
38 struct dwc3_rockchip {
39         int                     num_clocks;
40         bool                    connected;
41         struct device           *dev;
42         struct clk              **clks;
43         struct dwc3             *dwc;
44         struct reset_control    *otg_rst;
45         struct extcon_dev       *edev;
46         struct notifier_block   device_nb;
47         struct notifier_block   host_nb;
48         struct work_struct      otg_work;
49 };
50
51 static int dwc3_rockchip_device_notifier(struct notifier_block *nb,
52                                          unsigned long event, void *ptr)
53 {
54         struct dwc3_rockchip *rockchip =
55                 container_of(nb, struct dwc3_rockchip, device_nb);
56
57         schedule_work(&rockchip->otg_work);
58
59         return NOTIFY_DONE;
60 }
61
62 static int dwc3_rockchip_host_notifier(struct notifier_block *nb,
63                                        unsigned long event, void *ptr)
64 {
65         struct dwc3_rockchip *rockchip =
66                 container_of(nb, struct dwc3_rockchip, host_nb);
67
68         schedule_work(&rockchip->otg_work);
69
70         return NOTIFY_DONE;
71 }
72
73 static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
74 {
75         struct dwc3_rockchip    *rockchip =
76                 container_of(work, struct dwc3_rockchip, otg_work);
77         struct dwc3             *dwc = rockchip->dwc;
78         struct extcon_dev       *edev = rockchip->edev;
79         struct usb_hcd          *hcd;
80         unsigned long           flags;
81         int                     ret;
82         u32                     reg;
83
84         if (!dwc)
85                 return;
86
87         if (extcon_get_cable_state_(edev, EXTCON_USB) > 0) {
88                 if (rockchip->connected)
89                         return;
90
91                 /*
92                  * If dr_mode is host only, never to set
93                  * the mode to the peripheral mode.
94                  */
95                 if (WARN_ON(dwc->dr_mode == USB_DR_MODE_HOST))
96                         return;
97
98                 pm_runtime_get_sync(dwc->dev);
99
100                 spin_lock_irqsave(&dwc->lock, flags);
101                 dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
102                 spin_unlock_irqrestore(&dwc->lock, flags);
103
104                 rockchip->connected = true;
105                 dev_info(rockchip->dev, "USB peripheral connected\n");
106         } else if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) > 0) {
107                 if (rockchip->connected)
108                         return;
109
110                 /*
111                  * If dr_mode is device only, never to
112                  * set the mode to the host mode.
113                  */
114                 if (WARN_ON(dwc->dr_mode == USB_DR_MODE_PERIPHERAL))
115                         return;
116
117                 reset_control_assert(rockchip->otg_rst);
118
119                 ret = phy_power_on(dwc->usb2_generic_phy);
120                 if (ret < 0) {
121                         reset_control_deassert(rockchip->otg_rst);
122                         return;
123                 }
124
125                 ret = phy_power_on(dwc->usb3_generic_phy);
126                 if (ret < 0) {
127                         phy_power_off(dwc->usb2_generic_phy);
128                         reset_control_deassert(rockchip->otg_rst);
129                         return;
130                 }
131
132                 reset_control_deassert(rockchip->otg_rst);
133
134                 pm_runtime_get_sync(dwc->dev);
135
136                 hcd = dev_get_drvdata(&dwc->xhci->dev);
137
138                 spin_lock_irqsave(&dwc->lock, flags);
139                 dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
140                 spin_unlock_irqrestore(&dwc->lock, flags);
141
142                 if (hcd->state == HC_STATE_HALT) {
143                         usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
144                         usb_add_hcd(hcd->shared_hcd, hcd->irq, IRQF_SHARED);
145                 }
146
147                 rockchip->connected = true;
148                 dev_info(rockchip->dev, "USB HOST connected\n");
149         } else {
150                 if (!rockchip->connected)
151                         return;
152
153                 reg = dwc3_readl(dwc->regs, DWC3_GCTL);
154
155                 if (DWC3_GCTL_PRTCAP(reg) == DWC3_GCTL_PRTCAP_HOST ||
156                     DWC3_GCTL_PRTCAP(reg) == DWC3_GCTL_PRTCAP_OTG) {
157                         hcd = dev_get_drvdata(&dwc->xhci->dev);
158
159                         if (hcd->state != HC_STATE_HALT) {
160                                 usb_remove_hcd(hcd->shared_hcd);
161                                 usb_remove_hcd(hcd);
162                         }
163
164                         phy_power_off(dwc->usb2_generic_phy);
165                         phy_power_off(dwc->usb3_generic_phy);
166
167                 }
168
169                 pm_runtime_put_sync(dwc->dev);
170
171                 rockchip->connected = false;
172                 dev_info(rockchip->dev, "USB unconnected\n");
173         }
174 }
175
176 static int dwc3_rockchip_extcon_register(struct dwc3_rockchip *rockchip)
177 {
178         int                     ret;
179         struct device           *dev = rockchip->dev;
180         struct extcon_dev       *edev;
181
182         if (device_property_read_bool(dev, "extcon")) {
183                 edev = extcon_get_edev_by_phandle(dev, 0);
184                 if (IS_ERR(edev)) {
185                         if (PTR_ERR(edev) != -EPROBE_DEFER)
186                                 dev_err(dev, "couldn't get extcon device\n");
187                         return PTR_ERR(edev);
188                 }
189
190                 INIT_WORK(&rockchip->otg_work,
191                           dwc3_rockchip_otg_extcon_evt_work);
192
193                 rockchip->device_nb.notifier_call =
194                                 dwc3_rockchip_device_notifier;
195                 ret = extcon_register_notifier(edev, EXTCON_USB,
196                                                &rockchip->device_nb);
197                 if (ret < 0) {
198                         dev_err(dev, "failed to register notifier for USB\n");
199                         return ret;
200                 }
201
202                 rockchip->host_nb.notifier_call =
203                                 dwc3_rockchip_host_notifier;
204                 ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
205                                                &rockchip->host_nb);
206                 if (ret < 0) {
207                         dev_err(dev, "failed to register notifier for USB HOST\n");
208                         extcon_unregister_notifier(edev, EXTCON_USB,
209                                                    &rockchip->device_nb);
210                         return ret;
211                 }
212
213                 rockchip->edev = edev;
214         }
215
216         return 0;
217 }
218
219 static void dwc3_rockchip_extcon_unregister(struct dwc3_rockchip *rockchip)
220 {
221         if (!rockchip->edev)
222                 return;
223
224         extcon_unregister_notifier(rockchip->edev, EXTCON_USB,
225                                    &rockchip->device_nb);
226         extcon_unregister_notifier(rockchip->edev, EXTCON_USB_HOST,
227                                    &rockchip->host_nb);
228 }
229
230 static int dwc3_rockchip_probe(struct platform_device *pdev)
231 {
232         struct dwc3_rockchip    *rockchip;
233         struct device           *dev = &pdev->dev;
234         struct device_node      *np = dev->of_node, *child;
235         struct platform_device  *child_pdev;
236
237         unsigned int            count;
238         int                     ret;
239         int                     i;
240
241         rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL);
242
243         if (!rockchip)
244                 return -ENOMEM;
245
246         count = of_clk_get_parent_count(np);
247         if (!count)
248                 return -ENOENT;
249
250         rockchip->num_clocks = count;
251
252         rockchip->clks = devm_kcalloc(dev, rockchip->num_clocks,
253                                       sizeof(struct clk *), GFP_KERNEL);
254         if (!rockchip->clks)
255                 return -ENOMEM;
256
257         platform_set_drvdata(pdev, rockchip);
258
259         rockchip->dev = dev;
260
261         for (i = 0; i < rockchip->num_clocks; i++) {
262                 struct clk      *clk;
263
264                 clk = of_clk_get(np, i);
265                 if (IS_ERR(clk)) {
266                         ret = PTR_ERR(clk);
267                         goto err0;
268                 }
269
270                 ret = clk_prepare_enable(clk);
271                 if (ret < 0) {
272                         clk_put(clk);
273                         goto err0;
274                 }
275
276                 rockchip->clks[i] = clk;
277         }
278
279         pm_runtime_set_active(dev);
280         pm_runtime_enable(dev);
281         ret = pm_runtime_get_sync(dev);
282         if (ret < 0) {
283                 dev_err(dev, "get_sync failed with err %d\n", ret);
284                 goto err1;
285         }
286
287         rockchip->otg_rst = devm_reset_control_get(dev, "usb3-otg");
288         if (IS_ERR(rockchip->otg_rst)) {
289                 dev_err(dev, "could not get reset controller\n");
290                 ret = PTR_ERR(rockchip->otg_rst);
291                 goto err1;
292         }
293
294         child = of_get_child_by_name(np, "dwc3");
295         if (!child) {
296                 dev_err(dev, "failed to find dwc3 core node\n");
297                 ret = -ENODEV;
298                 goto err1;
299         }
300
301         /* Allocate and initialize the core */
302         ret = of_platform_populate(np, NULL, NULL, dev);
303         if (ret) {
304                 dev_err(dev, "failed to create dwc3 core\n");
305                 goto err1;
306         }
307
308         child_pdev = of_find_device_by_node(child);
309         if (!child_pdev) {
310                 dev_err(dev, "failed to find dwc3 core device\n");
311                 ret = -ENODEV;
312                 goto err2;
313         }
314
315         rockchip->dwc = platform_get_drvdata(child_pdev);
316         if (!rockchip->dwc) {
317                 dev_err(dev, "failed to get drvdata dwc3\n");
318                 ret = -EPROBE_DEFER;
319                 goto err2;
320         }
321
322         ret = dwc3_rockchip_extcon_register(rockchip);
323         if (ret < 0)
324                 goto err2;
325
326         if (rockchip->edev) {
327                 pm_runtime_set_autosuspend_delay(&child_pdev->dev,
328                                                  DWC3_ROCKCHIP_AUTOSUSPEND_DELAY);
329                 pm_runtime_allow(&child_pdev->dev);
330
331                 if (rockchip->dwc->dr_mode == USB_DR_MODE_HOST ||
332                     rockchip->dwc->dr_mode == USB_DR_MODE_OTG) {
333                         struct usb_hcd *hcd =
334                                 dev_get_drvdata(&rockchip->dwc->xhci->dev);
335
336                         if (hcd->state != HC_STATE_HALT) {
337                                 usb_remove_hcd(hcd->shared_hcd);
338                                 usb_remove_hcd(hcd);
339                         }
340                 }
341
342                 pm_runtime_put_sync(dev);
343
344                 if ((extcon_get_cable_state_(rockchip->edev,
345                                              EXTCON_USB) > 0) ||
346                     (extcon_get_cable_state_(rockchip->edev,
347                                              EXTCON_USB_HOST) > 0))
348                         schedule_work(&rockchip->otg_work);
349         }
350
351         return ret;
352
353 err2:
354         of_platform_depopulate(dev);
355
356 err1:
357         pm_runtime_put_sync(dev);
358         pm_runtime_disable(dev);
359
360 err0:
361         for (i = 0; i < rockchip->num_clocks && rockchip->clks[i]; i++) {
362                 if (!pm_runtime_status_suspended(dev))
363                         clk_disable(rockchip->clks[i]);
364                 clk_unprepare(rockchip->clks[i]);
365                 clk_put(rockchip->clks[i]);
366         }
367
368         return ret;
369 }
370
371 static int dwc3_rockchip_remove(struct platform_device *pdev)
372 {
373         struct dwc3_rockchip    *rockchip = platform_get_drvdata(pdev);
374         struct device           *dev = &pdev->dev;
375         int                     i;
376
377         dwc3_rockchip_extcon_unregister(rockchip);
378
379         of_platform_depopulate(dev);
380
381         if (!rockchip->edev)
382                 pm_runtime_put_sync(dev);
383
384         pm_runtime_disable(dev);
385         pm_runtime_set_suspended(dev);
386
387         for (i = 0; i < rockchip->num_clocks; i++) {
388                 if (!pm_runtime_status_suspended(dev))
389                         clk_disable(rockchip->clks[i]);
390                 clk_unprepare(rockchip->clks[i]);
391                 clk_put(rockchip->clks[i]);
392         }
393
394         return 0;
395 }
396
397 #ifdef CONFIG_PM
398 static int dwc3_rockchip_runtime_suspend(struct device *dev)
399 {
400         struct dwc3_rockchip    *rockchip = dev_get_drvdata(dev);
401         int                     i;
402
403         for (i = 0; i < rockchip->num_clocks; i++)
404                 clk_disable(rockchip->clks[i]);
405
406         return 0;
407 }
408
409 static int dwc3_rockchip_runtime_resume(struct device *dev)
410 {
411         struct dwc3_rockchip    *rockchip = dev_get_drvdata(dev);
412         int                     i;
413
414         for (i = 0; i < rockchip->num_clocks; i++)
415                 clk_enable(rockchip->clks[i]);
416
417         return 0;
418 }
419
420 static const struct dev_pm_ops dwc3_rockchip_dev_pm_ops = {
421         SET_RUNTIME_PM_OPS(dwc3_rockchip_runtime_suspend,
422                            dwc3_rockchip_runtime_resume, NULL)
423 };
424
425 #define DEV_PM_OPS      (&dwc3_rockchip_dev_pm_ops)
426 #else
427 #define DEV_PM_OPS      NULL
428 #endif /* CONFIG_PM */
429
430 static const struct of_device_id rockchip_dwc3_match[] = {
431         { .compatible = "rockchip,rk3399-dwc3" },
432         { /* Sentinel */ }
433 };
434
435 MODULE_DEVICE_TABLE(of, rockchip_dwc3_match);
436
437 static struct platform_driver dwc3_rockchip_driver = {
438         .probe          = dwc3_rockchip_probe,
439         .remove         = dwc3_rockchip_remove,
440         .driver         = {
441                 .name   = "rockchip-dwc3",
442                 .of_match_table = rockchip_dwc3_match,
443                 .pm     = DEV_PM_OPS,
444         },
445 };
446
447 module_platform_driver(dwc3_rockchip_driver);
448
449 MODULE_ALIAS("platform:rockchip-dwc3");
450 MODULE_AUTHOR("William Wu <william.wu@rock-chips.com>");
451 MODULE_LICENSE("GPL v2");
452 MODULE_DESCRIPTION("DesignWare USB3 ROCKCHIP Glue Layer");