Skip to content

Commit 0cbdc8c

Browse files
wuliangfengrkhuangtao
authored andcommitted
usb: dwc3: add a new glue layer for rockchip SoCs with INNO PHY
This patch add a rockchip specific glue layer to support USB 3.0 HOST only mode for rockchip USB 3.0 core wrapper consisting of USB 3.0 controller IP from Synopsys and USB 3.0 PHY IP from Innosilicon. With this patch, we can support for XHCI integrated in DWC3 IP on rockchip platforms. Because some INNO USB 3.0 PHY can't detect disconnection by PHY IP, and cause USB3 device unrecognized when replugged again. So we depend on the HUB core driver to detect the disconnection, and send notifier to DWC3 driver from USB PHY driver, then we can do phy reset and remove/add hcd to reinit HCD. Change-Id: I6972c6f9f8f7160dbd74ad531b843a65ccec5dc0 Signed-off-by: William Wu <wulf@rock-chips.com>
1 parent 99cf210 commit 0cbdc8c

File tree

3 files changed

+296
-0
lines changed

3 files changed

+296
-0
lines changed

drivers/usb/dwc3/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ config USB_DWC3_ROCKCHIP
105105
Support of USB2/3 functionality in Rockchip platforms.
106106
say 'Y' or 'M' if you have one such device.
107107

108+
config USB_DWC3_ROCKCHIP_INNO
109+
tristate "Rockchip Platforms with INNO PHY"
110+
depends on OF && COMMON_CLK && ARCH_ROCKCHIP
111+
default USB_DWC3
112+
help
113+
Support of USB2/3 functionality in Rockchip platforms
114+
with INNO USB 3.0 PHY IP inside.
115+
say 'Y' or 'M' if you have one such device.
116+
108117
config USB_DWC3_ST
109118
tristate "STMicroelectronics Platforms"
110119
depends on ARCH_STI && OF

drivers/usb/dwc3/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
3939
obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
4040
obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
4141
obj-$(CONFIG_USB_DWC3_ROCKCHIP) += dwc3-rockchip.o
42+
obj-$(CONFIG_USB_DWC3_ROCKCHIP_INNO) += dwc3-rockchip-inno.o
4243
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/**
2+
* dwc3-rockchip-inno.c - Rockchip DWC3 Specific Glue layer with INNO PHY
3+
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
4+
*
5+
* Authors: William Wu <william.wu@rock-chips.com>
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License version 2 of
9+
* the License as published by the Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*/
16+
17+
#include <linux/module.h>
18+
#include <linux/kernel.h>
19+
#include <linux/slab.h>
20+
#include <linux/platform_device.h>
21+
#include <linux/dma-mapping.h>
22+
#include <linux/clk.h>
23+
#include <linux/clk-provider.h>
24+
#include <linux/of.h>
25+
#include <linux/of_platform.h>
26+
#include <linux/pm_runtime.h>
27+
#include <linux/usb.h>
28+
#include <linux/usb/hcd.h>
29+
#include <linux/notifier.h>
30+
#include <linux/usb/phy.h>
31+
32+
#include "core.h"
33+
#include "../host/xhci.h"
34+
35+
struct dwc3_rockchip {
36+
struct device *dev;
37+
struct clk **clks;
38+
int num_clocks;
39+
struct dwc3 *dwc;
40+
struct usb_phy *phy;
41+
struct notifier_block u3phy_nb;
42+
struct work_struct u3_work;
43+
struct mutex lock;
44+
};
45+
46+
static int u3phy_disconnect_det_notifier(struct notifier_block *nb,
47+
unsigned long event, void *p)
48+
{
49+
struct dwc3_rockchip *rockchip =
50+
container_of(nb, struct dwc3_rockchip, u3phy_nb);
51+
52+
schedule_work(&rockchip->u3_work);
53+
54+
return NOTIFY_DONE;
55+
}
56+
57+
static void u3phy_disconnect_det_work(struct work_struct *work)
58+
{
59+
struct dwc3_rockchip *rockchip =
60+
container_of(work, struct dwc3_rockchip, u3_work);
61+
struct usb_hcd *hcd = dev_get_drvdata(&rockchip->dwc->xhci->dev);
62+
struct usb_hcd *shared_hcd = hcd->shared_hcd;
63+
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
64+
u32 count = 0;
65+
66+
mutex_lock(&rockchip->lock);
67+
68+
if (hcd->state != HC_STATE_HALT) {
69+
usb_remove_hcd(shared_hcd);
70+
usb_remove_hcd(hcd);
71+
}
72+
73+
if (rockchip->phy)
74+
usb_phy_shutdown(rockchip->phy);
75+
76+
while (hcd->state != HC_STATE_HALT) {
77+
if (++count > 1000) {
78+
dev_err(rockchip->dev,
79+
"wait for HCD remove 1s timeout!\n");
80+
break;
81+
}
82+
usleep_range(1000, 1100);
83+
}
84+
85+
if (hcd->state == HC_STATE_HALT) {
86+
xhci->shared_hcd = shared_hcd;
87+
usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
88+
usb_add_hcd(shared_hcd, hcd->irq, IRQF_SHARED);
89+
}
90+
91+
if (rockchip->phy)
92+
usb_phy_init(rockchip->phy);
93+
94+
mutex_unlock(&rockchip->lock);
95+
}
96+
97+
static int dwc3_rockchip_probe(struct platform_device *pdev)
98+
{
99+
struct dwc3_rockchip *rockchip;
100+
struct device *dev = &pdev->dev;
101+
struct device_node *np = dev->of_node, *child;
102+
struct platform_device *child_pdev;
103+
104+
unsigned int count;
105+
int ret;
106+
int i;
107+
108+
rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL);
109+
if (!rockchip)
110+
return -ENOMEM;
111+
112+
child = of_get_child_by_name(np, "dwc3");
113+
if (!child) {
114+
dev_err(dev, "failed to find dwc3 core node\n");
115+
return -ENODEV;
116+
}
117+
118+
count = of_clk_get_parent_count(np);
119+
if (!count)
120+
return -ENOENT;
121+
122+
rockchip->num_clocks = count;
123+
124+
rockchip->clks = devm_kcalloc(dev, rockchip->num_clocks,
125+
sizeof(struct clk *), GFP_KERNEL);
126+
if (!rockchip->clks)
127+
return -ENOMEM;
128+
129+
platform_set_drvdata(pdev, rockchip);
130+
rockchip->dev = dev;
131+
132+
for (i = 0; i < rockchip->num_clocks; i++) {
133+
struct clk *clk;
134+
135+
clk = of_clk_get(np, i);
136+
if (IS_ERR(clk)) {
137+
ret = PTR_ERR(clk);
138+
goto err0;
139+
}
140+
141+
ret = clk_prepare_enable(clk);
142+
if (ret < 0) {
143+
clk_put(clk);
144+
goto err0;
145+
}
146+
147+
rockchip->clks[i] = clk;
148+
}
149+
150+
pm_runtime_set_active(dev);
151+
pm_runtime_enable(dev);
152+
ret = pm_runtime_get_sync(dev);
153+
if (ret < 0) {
154+
dev_err(dev, "get_sync failed with err %d\n", ret);
155+
goto err1;
156+
}
157+
158+
ret = of_platform_populate(np, NULL, NULL, dev);
159+
if (ret)
160+
goto err1;
161+
162+
child_pdev = of_find_device_by_node(child);
163+
if (!child_pdev) {
164+
dev_err(dev, "failed to find dwc3 core device\n");
165+
ret = -ENODEV;
166+
goto err2;
167+
}
168+
169+
rockchip->dwc = platform_get_drvdata(child_pdev);
170+
if (!rockchip->dwc || !rockchip->dwc->xhci) {
171+
dev_dbg(dev, "failed to get drvdata dwc3\n");
172+
ret = -EPROBE_DEFER;
173+
goto err2;
174+
}
175+
176+
mutex_init(&rockchip->lock);
177+
178+
rockchip->phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
179+
if (rockchip->phy) {
180+
INIT_WORK(&rockchip->u3_work, u3phy_disconnect_det_work);
181+
rockchip->u3phy_nb.notifier_call =
182+
u3phy_disconnect_det_notifier;
183+
usb_register_notifier(rockchip->phy, &rockchip->u3phy_nb);
184+
}
185+
186+
return 0;
187+
188+
err2:
189+
of_platform_depopulate(dev);
190+
err1:
191+
pm_runtime_put_sync(dev);
192+
pm_runtime_disable(dev);
193+
err0:
194+
for (i = 0; i < rockchip->num_clocks && rockchip->clks[i]; i++) {
195+
if (!pm_runtime_status_suspended(dev))
196+
clk_disable(rockchip->clks[i]);
197+
clk_unprepare(rockchip->clks[i]);
198+
clk_put(rockchip->clks[i]);
199+
}
200+
201+
return ret;
202+
}
203+
204+
static int dwc3_rockchip_remove(struct platform_device *pdev)
205+
{
206+
struct dwc3_rockchip *rockchip = platform_get_drvdata(pdev);
207+
struct device *dev = &pdev->dev;
208+
int i;
209+
210+
of_platform_depopulate(dev);
211+
212+
pm_runtime_put_sync(dev);
213+
pm_runtime_disable(dev);
214+
215+
for (i = 0; i < rockchip->num_clocks; i++) {
216+
if (!pm_runtime_status_suspended(dev))
217+
clk_disable(rockchip->clks[i]);
218+
clk_unprepare(rockchip->clks[i]);
219+
clk_put(rockchip->clks[i]);
220+
}
221+
222+
return 0;
223+
}
224+
225+
#ifdef CONFIG_PM
226+
static int dwc3_rockchip_runtime_suspend(struct device *dev)
227+
{
228+
struct dwc3_rockchip *rockchip = dev_get_drvdata(dev);
229+
int i;
230+
231+
for (i = 0; i < rockchip->num_clocks; i++)
232+
clk_disable(rockchip->clks[i]);
233+
234+
return 0;
235+
}
236+
237+
static int dwc3_rockchip_runtime_resume(struct device *dev)
238+
{
239+
struct dwc3_rockchip *rockchip = dev_get_drvdata(dev);
240+
int ret;
241+
int i;
242+
243+
for (i = 0; i < rockchip->num_clocks; i++) {
244+
ret = clk_enable(rockchip->clks[i]);
245+
if (ret < 0) {
246+
while (--i >= 0)
247+
clk_disable(rockchip->clks[i]);
248+
return ret;
249+
}
250+
}
251+
252+
return 0;
253+
}
254+
255+
static const struct dev_pm_ops dwc3_rockchip_dev_pm_ops = {
256+
SET_RUNTIME_PM_OPS(dwc3_rockchip_runtime_suspend,
257+
dwc3_rockchip_runtime_resume, NULL)
258+
};
259+
260+
#define DEV_PM_OPS (&dwc3_rockchip_dev_pm_ops)
261+
#else
262+
#define DEV_PM_OPS NULL
263+
#endif /* CONFIG_PM */
264+
265+
static const struct of_device_id rockchip_dwc3_match[] = {
266+
{ .compatible = "rockchip,rk3328-dwc3" },
267+
{ /* Sentinel */ }
268+
};
269+
MODULE_DEVICE_TABLE(of, rockchip_dwc3_match);
270+
271+
static struct platform_driver dwc3_rockchip_driver = {
272+
.probe = dwc3_rockchip_probe,
273+
.remove = dwc3_rockchip_remove,
274+
.driver = {
275+
.name = "rockchip-inno-dwc3",
276+
.pm = DEV_PM_OPS,
277+
.of_match_table = rockchip_dwc3_match,
278+
},
279+
};
280+
281+
module_platform_driver(dwc3_rockchip_driver);
282+
283+
MODULE_ALIAS("platform:rockchip-inno-dwc3");
284+
MODULE_AUTHOR("William Wu <william.wu@rock-chips.com>");
285+
MODULE_LICENSE("GPL v2");
286+
MODULE_DESCRIPTION("DesignWare USB3 rockchip-inno Glue Layer");

0 commit comments

Comments
 (0)