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>
24 #include <linux/debugfs.h>
26 #include <linux/of_platform.h>
27 #include <linux/pm_runtime.h>
28 #include <linux/usb.h>
29 #include <linux/usb/hcd.h>
30 #include <linux/notifier.h>
31 #include <linux/uaccess.h>
32 #include <linux/usb/phy.h>
33 #include <linux/usb/ch9.h>
36 #include "../host/xhci.h"
38 #define XHCI_TSTCTRL_MASK (0xf << 28)
39 struct dwc3_rockchip {
46 struct notifier_block u3phy_nb;
47 struct work_struct u3_work;
51 static int dwc3_rockchip_host_testmode_show(struct seq_file *s, void *unused)
53 struct dwc3_rockchip *rockchip = s->private;
54 struct dwc3 *dwc = rockchip->dwc;
55 struct usb_hcd *hcd = dev_get_drvdata(&dwc->xhci->dev);
56 struct xhci_hcd *xhci = hcd_to_xhci(hcd);
57 __le32 __iomem **port_array;
60 if (rockchip->dwc->dr_mode == USB_DR_MODE_PERIPHERAL) {
61 dev_warn(rockchip->dev, "USB HOST not support!\n");
65 if (hcd->state == HC_STATE_HALT) {
66 dev_warn(rockchip->dev, "HOST is halted!\n");
70 port_array = xhci->usb2_ports;
71 reg = readl(port_array[0] + 1);
72 reg &= XHCI_TSTCTRL_MASK;
77 seq_puts(s, "U2: no test\n");
80 seq_puts(s, "U2: test_j\n");
83 seq_puts(s, "U2: test_k\n");
86 seq_puts(s, "U2: test_se0_nak\n");
89 seq_puts(s, "U2: test_packet\n");
92 seq_puts(s, "U2: test_force_enable\n");
95 seq_printf(s, "U2: UNKNOWN %d\n", reg);
98 port_array = xhci->usb3_ports;
99 reg = readl(port_array[0]);
100 reg &= PORT_PLS_MASK;
101 if (reg == USB_SS_PORT_LS_COMP_MOD)
102 seq_puts(s, "U3: compliance mode\n");
104 seq_printf(s, "U3: UNKNOWN %d\n", reg >> 5);
109 static int dwc3_rockchip_host_testmode_open(struct inode *inode,
112 return single_open(file, dwc3_rockchip_host_testmode_show,
117 * dwc3_rockchip_set_test_mode - Enables USB2/USB3 HOST Test Modes
118 * @rockchip: pointer to our context structure
119 * @mode: the mode to set (U2: J, K SE0 NAK, Test_packet,
120 * Force Enable; U3: Compliance mode)
122 * This function will return 0 on success or -EINVAL if wrong Test
123 * Selector is passed.
125 static int dwc3_rockchip_set_test_mode(struct dwc3_rockchip *rockchip,
128 struct dwc3 *dwc = rockchip->dwc;
129 struct usb_hcd *hcd = dev_get_drvdata(&dwc->xhci->dev);
130 struct xhci_hcd *xhci = hcd_to_xhci(hcd);
131 __le32 __iomem **port_array;
135 if (hcd->state == HC_STATE_HALT) {
136 dev_err(rockchip->dev, "HOST is halted!\n");
146 port_array = xhci->usb2_ports;
147 reg = readl(port_array[0] + 1);
148 reg &= ~XHCI_TSTCTRL_MASK;
150 writel(reg, port_array[0] + 1);
152 case USB_SS_PORT_LS_COMP_MOD:
154 * Enable Inno U3 PHY to toggle CP test pattern
155 * before set XHCI controller enter compliance mode.
157 ret = phy_cp_test(dwc->usb3_generic_phy);
159 dev_err(rockchip->dev, "phy cp test fail!\n");
163 port_array = xhci->usb3_ports;
164 xhci_set_link_state(xhci, port_array, 0, mode);
170 dev_info(rockchip->dev, "set USB HOST test mode successfully!\n");
175 static ssize_t dwc3_rockchip_host_testmode_write(struct file *file,
176 const char __user *ubuf,
177 size_t count, loff_t *ppos)
179 struct seq_file *s = file->private_data;
180 struct dwc3_rockchip *rockchip = s->private;
184 if (rockchip->dwc->dr_mode == USB_DR_MODE_PERIPHERAL) {
185 dev_warn(rockchip->dev, "USB HOST not support!\n");
189 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
192 if (!strncmp(buf, "test_j", 6)) {
194 } else if (!strncmp(buf, "test_k", 6)) {
196 } else if (!strncmp(buf, "test_se0_nak", 12)) {
197 testmode = TEST_SE0_NAK;
198 } else if (!strncmp(buf, "test_packet", 11)) {
199 testmode = TEST_PACKET;
200 } else if (!strncmp(buf, "test_force_enable", 17)) {
201 testmode = TEST_FORCE_EN;
202 } else if (!strncmp(buf, "test_u3", 7)) {
203 testmode = USB_SS_PORT_LS_COMP_MOD;
205 dev_warn(rockchip->dev, "Test cmd not support!\n");
209 dwc3_rockchip_set_test_mode(rockchip, testmode);
214 static const struct file_operations dwc3_host_testmode_fops = {
215 .open = dwc3_rockchip_host_testmode_open,
216 .write = dwc3_rockchip_host_testmode_write,
219 .release = single_release,
222 static void dwc3_rockchip_debugfs_init(struct dwc3_rockchip *rockchip)
227 root = debugfs_create_dir(dev_name(rockchip->dev), NULL);
228 if (IS_ERR_OR_NULL(root)) {
230 dev_err(rockchip->dev, "Can't create debugfs root\n");
233 rockchip->root = root;
235 if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) ||
236 IS_ENABLED(CONFIG_USB_DWC3_HOST)) {
237 file = debugfs_create_file("host_testmode", S_IRUSR | S_IWUSR,
239 &dwc3_host_testmode_fops);
241 dev_dbg(rockchip->dev, "Can't create debugfs host_testmode\n");
245 static int u3phy_disconnect_det_notifier(struct notifier_block *nb,
246 unsigned long event, void *p)
248 struct dwc3_rockchip *rockchip =
249 container_of(nb, struct dwc3_rockchip, u3phy_nb);
251 schedule_work(&rockchip->u3_work);
256 static void u3phy_disconnect_det_work(struct work_struct *work)
258 struct dwc3_rockchip *rockchip =
259 container_of(work, struct dwc3_rockchip, u3_work);
260 struct usb_hcd *hcd = dev_get_drvdata(&rockchip->dwc->xhci->dev);
261 struct usb_hcd *shared_hcd = hcd->shared_hcd;
262 struct xhci_hcd *xhci = hcd_to_xhci(hcd);
265 mutex_lock(&rockchip->lock);
267 if (hcd->state != HC_STATE_HALT) {
268 usb_remove_hcd(shared_hcd);
273 usb_phy_shutdown(rockchip->phy);
275 while (hcd->state != HC_STATE_HALT) {
276 if (++count > 1000) {
277 dev_err(rockchip->dev,
278 "wait for HCD remove 1s timeout!\n");
281 usleep_range(1000, 1100);
284 if (hcd->state == HC_STATE_HALT) {
285 xhci->shared_hcd = shared_hcd;
286 usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
287 usb_add_hcd(shared_hcd, hcd->irq, IRQF_SHARED);
291 usb_phy_init(rockchip->phy);
293 mutex_unlock(&rockchip->lock);
296 static int dwc3_rockchip_probe(struct platform_device *pdev)
298 struct dwc3_rockchip *rockchip;
299 struct device *dev = &pdev->dev;
300 struct device_node *np = dev->of_node, *child;
301 struct platform_device *child_pdev;
307 rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL);
311 child = of_get_child_by_name(np, "dwc3");
313 dev_err(dev, "failed to find dwc3 core node\n");
317 count = of_clk_get_parent_count(np);
321 rockchip->num_clocks = count;
323 rockchip->clks = devm_kcalloc(dev, rockchip->num_clocks,
324 sizeof(struct clk *), GFP_KERNEL);
328 platform_set_drvdata(pdev, rockchip);
331 for (i = 0; i < rockchip->num_clocks; i++) {
334 clk = of_clk_get(np, i);
340 ret = clk_prepare_enable(clk);
346 rockchip->clks[i] = clk;
349 pm_runtime_set_active(dev);
350 pm_runtime_enable(dev);
351 ret = pm_runtime_get_sync(dev);
353 dev_err(dev, "get_sync failed with err %d\n", ret);
357 ret = of_platform_populate(np, NULL, NULL, dev);
361 child_pdev = of_find_device_by_node(child);
363 dev_err(dev, "failed to find dwc3 core device\n");
368 rockchip->dwc = platform_get_drvdata(child_pdev);
369 if (!rockchip->dwc || !rockchip->dwc->xhci) {
370 dev_dbg(dev, "failed to get drvdata dwc3\n");
375 mutex_init(&rockchip->lock);
377 rockchip->phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
379 INIT_WORK(&rockchip->u3_work, u3phy_disconnect_det_work);
380 rockchip->u3phy_nb.notifier_call =
381 u3phy_disconnect_det_notifier;
382 usb_register_notifier(rockchip->phy, &rockchip->u3phy_nb);
385 dwc3_rockchip_debugfs_init(rockchip);
390 of_platform_depopulate(dev);
392 pm_runtime_put_sync(dev);
393 pm_runtime_disable(dev);
395 for (i = 0; i < rockchip->num_clocks && rockchip->clks[i]; i++) {
396 if (!pm_runtime_status_suspended(dev))
397 clk_disable(rockchip->clks[i]);
398 clk_unprepare(rockchip->clks[i]);
399 clk_put(rockchip->clks[i]);
405 static int dwc3_rockchip_remove(struct platform_device *pdev)
407 struct dwc3_rockchip *rockchip = platform_get_drvdata(pdev);
408 struct device *dev = &pdev->dev;
411 of_platform_depopulate(dev);
413 debugfs_remove_recursive(rockchip->root);
415 pm_runtime_put_sync(dev);
416 pm_runtime_disable(dev);
418 for (i = 0; i < rockchip->num_clocks; i++) {
419 if (!pm_runtime_status_suspended(dev))
420 clk_disable(rockchip->clks[i]);
421 clk_unprepare(rockchip->clks[i]);
422 clk_put(rockchip->clks[i]);
429 static int dwc3_rockchip_runtime_suspend(struct device *dev)
431 struct dwc3_rockchip *rockchip = dev_get_drvdata(dev);
434 for (i = 0; i < rockchip->num_clocks; i++)
435 clk_disable(rockchip->clks[i]);
440 static int dwc3_rockchip_runtime_resume(struct device *dev)
442 struct dwc3_rockchip *rockchip = dev_get_drvdata(dev);
446 for (i = 0; i < rockchip->num_clocks; i++) {
447 ret = clk_enable(rockchip->clks[i]);
450 clk_disable(rockchip->clks[i]);
458 static const struct dev_pm_ops dwc3_rockchip_dev_pm_ops = {
459 SET_RUNTIME_PM_OPS(dwc3_rockchip_runtime_suspend,
460 dwc3_rockchip_runtime_resume, NULL)
463 #define DEV_PM_OPS (&dwc3_rockchip_dev_pm_ops)
465 #define DEV_PM_OPS NULL
466 #endif /* CONFIG_PM */
468 static const struct of_device_id rockchip_dwc3_match[] = {
469 { .compatible = "rockchip,rk3328-dwc3" },
472 MODULE_DEVICE_TABLE(of, rockchip_dwc3_match);
474 static struct platform_driver dwc3_rockchip_driver = {
475 .probe = dwc3_rockchip_probe,
476 .remove = dwc3_rockchip_remove,
478 .name = "rockchip-inno-dwc3",
480 .of_match_table = rockchip_dwc3_match,
484 module_platform_driver(dwc3_rockchip_driver);
486 MODULE_ALIAS("platform:rockchip-inno-dwc3");
487 MODULE_AUTHOR("William Wu <william.wu@rock-chips.com>");
488 MODULE_LICENSE("GPL v2");
489 MODULE_DESCRIPTION("DesignWare USB3 rockchip-inno Glue Layer");