2 * dwc3-rockchip-inno.c - Rockchip DWC3 Specific Glue layer with INNO PHY
3 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
5 * Authors: William Wu <william.wu@rock-chips.com>
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.
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.
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>
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>
33 #include "../host/xhci.h"
35 struct dwc3_rockchip {
41 struct notifier_block u3phy_nb;
42 struct work_struct u3_work;
46 static int u3phy_disconnect_det_notifier(struct notifier_block *nb,
47 unsigned long event, void *p)
49 struct dwc3_rockchip *rockchip =
50 container_of(nb, struct dwc3_rockchip, u3phy_nb);
52 schedule_work(&rockchip->u3_work);
57 static void u3phy_disconnect_det_work(struct work_struct *work)
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);
66 mutex_lock(&rockchip->lock);
68 if (hcd->state != HC_STATE_HALT) {
69 usb_remove_hcd(shared_hcd);
74 usb_phy_shutdown(rockchip->phy);
76 while (hcd->state != HC_STATE_HALT) {
78 dev_err(rockchip->dev,
79 "wait for HCD remove 1s timeout!\n");
82 usleep_range(1000, 1100);
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);
92 usb_phy_init(rockchip->phy);
94 mutex_unlock(&rockchip->lock);
97 static int dwc3_rockchip_probe(struct platform_device *pdev)
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;
108 rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL);
112 child = of_get_child_by_name(np, "dwc3");
114 dev_err(dev, "failed to find dwc3 core node\n");
118 count = of_clk_get_parent_count(np);
122 rockchip->num_clocks = count;
124 rockchip->clks = devm_kcalloc(dev, rockchip->num_clocks,
125 sizeof(struct clk *), GFP_KERNEL);
129 platform_set_drvdata(pdev, rockchip);
132 for (i = 0; i < rockchip->num_clocks; i++) {
135 clk = of_clk_get(np, i);
141 ret = clk_prepare_enable(clk);
147 rockchip->clks[i] = clk;
150 pm_runtime_set_active(dev);
151 pm_runtime_enable(dev);
152 ret = pm_runtime_get_sync(dev);
154 dev_err(dev, "get_sync failed with err %d\n", ret);
158 ret = of_platform_populate(np, NULL, NULL, dev);
162 child_pdev = of_find_device_by_node(child);
164 dev_err(dev, "failed to find dwc3 core device\n");
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");
176 mutex_init(&rockchip->lock);
178 rockchip->phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
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);
189 of_platform_depopulate(dev);
191 pm_runtime_put_sync(dev);
192 pm_runtime_disable(dev);
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]);
204 static int dwc3_rockchip_remove(struct platform_device *pdev)
206 struct dwc3_rockchip *rockchip = platform_get_drvdata(pdev);
207 struct device *dev = &pdev->dev;
210 of_platform_depopulate(dev);
212 pm_runtime_put_sync(dev);
213 pm_runtime_disable(dev);
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]);
226 static int dwc3_rockchip_runtime_suspend(struct device *dev)
228 struct dwc3_rockchip *rockchip = dev_get_drvdata(dev);
231 for (i = 0; i < rockchip->num_clocks; i++)
232 clk_disable(rockchip->clks[i]);
237 static int dwc3_rockchip_runtime_resume(struct device *dev)
239 struct dwc3_rockchip *rockchip = dev_get_drvdata(dev);
243 for (i = 0; i < rockchip->num_clocks; i++) {
244 ret = clk_enable(rockchip->clks[i]);
247 clk_disable(rockchip->clks[i]);
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)
260 #define DEV_PM_OPS (&dwc3_rockchip_dev_pm_ops)
262 #define DEV_PM_OPS NULL
263 #endif /* CONFIG_PM */
265 static const struct of_device_id rockchip_dwc3_match[] = {
266 { .compatible = "rockchip,rk3328-dwc3" },
269 MODULE_DEVICE_TABLE(of, rockchip_dwc3_match);
271 static struct platform_driver dwc3_rockchip_driver = {
272 .probe = dwc3_rockchip_probe,
273 .remove = dwc3_rockchip_remove,
275 .name = "rockchip-inno-dwc3",
277 .of_match_table = rockchip_dwc3_match,
281 module_platform_driver(dwc3_rockchip_driver);
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");