From e1b26377c7422f90982a4ab2d8797947657002bb Mon Sep 17 00:00:00 2001 From: William Wu Date: Tue, 21 Feb 2017 20:16:33 +0800 Subject: [PATCH] usb: dwc3: rockchip-inno: support to set testmodes via debugfs This patch create host_testmode file in debugfs for USB HOST. It's useful for us to use a scope to verify signal integrity for USB2/USB3 HOST. For example, set testmodes for rk3328 board USB: 1. set test packet for the USB2 port of USB3 interface: echo test_packet > /sys/kernel/debug/usb.23/host_testmode 2. set compliance mode for the USB3 port of USB3 interface: echo test_u3 > /sys/kernel/debug/usb.23/host_testmode 3. check the testmode status: cat /sys/kernel/debug/usb.23/host_testmode The log maybe like this: U2: test_packet /* means that U2 in test mode */ U3: compliance mode /* means that U3 in test mode */ Change-Id: I6ddead14a7b78a011bbffcec6bf865df0632fe1b Signed-off-by: William Wu --- drivers/usb/dwc3/dwc3-rockchip-inno.c | 203 ++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-rockchip-inno.c b/drivers/usb/dwc3/dwc3-rockchip-inno.c index 5c907b97d6a6..79537cc69a33 100644 --- a/drivers/usb/dwc3/dwc3-rockchip-inno.c +++ b/drivers/usb/dwc3/dwc3-rockchip-inno.c @@ -21,28 +21,227 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include +#include #include "core.h" #include "../host/xhci.h" +#define XHCI_TSTCTRL_MASK (0xf << 28) struct dwc3_rockchip { struct device *dev; struct clk **clks; int num_clocks; struct dwc3 *dwc; + struct dentry *root; struct usb_phy *phy; struct notifier_block u3phy_nb; struct work_struct u3_work; struct mutex lock; }; +static int dwc3_rockchip_host_testmode_show(struct seq_file *s, void *unused) +{ + struct dwc3_rockchip *rockchip = s->private; + struct dwc3 *dwc = rockchip->dwc; + struct usb_hcd *hcd = dev_get_drvdata(&dwc->xhci->dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + __le32 __iomem **port_array; + u32 reg; + + if (rockchip->dwc->dr_mode == USB_DR_MODE_PERIPHERAL) { + dev_warn(rockchip->dev, "USB HOST not support!\n"); + return 0; + } + + if (hcd->state == HC_STATE_HALT) { + dev_warn(rockchip->dev, "HOST is halted!\n"); + return 0; + } + + port_array = xhci->usb2_ports; + reg = readl(port_array[0] + 1); + reg &= XHCI_TSTCTRL_MASK; + reg >>= 28; + + switch (reg) { + case 0: + seq_puts(s, "U2: no test\n"); + break; + case TEST_J: + seq_puts(s, "U2: test_j\n"); + break; + case TEST_K: + seq_puts(s, "U2: test_k\n"); + break; + case TEST_SE0_NAK: + seq_puts(s, "U2: test_se0_nak\n"); + break; + case TEST_PACKET: + seq_puts(s, "U2: test_packet\n"); + break; + case TEST_FORCE_EN: + seq_puts(s, "U2: test_force_enable\n"); + break; + default: + seq_printf(s, "U2: UNKNOWN %d\n", reg); + } + + port_array = xhci->usb3_ports; + reg = readl(port_array[0]); + reg &= PORT_PLS_MASK; + if (reg == USB_SS_PORT_LS_COMP_MOD) + seq_puts(s, "U3: compliance mode\n"); + else + seq_printf(s, "U3: UNKNOWN %d\n", reg >> 5); + + return 0; +} + +static int dwc3_rockchip_host_testmode_open(struct inode *inode, + struct file *file) +{ + return single_open(file, dwc3_rockchip_host_testmode_show, + inode->i_private); +} + +/** + * dwc3_rockchip_set_test_mode - Enables USB2/USB3 HOST Test Modes + * @rockchip: pointer to our context structure + * @mode: the mode to set (U2: J, K SE0 NAK, Test_packet, + * Force Enable; U3: Compliance mode) + * + * This function will return 0 on success or -EINVAL if wrong Test + * Selector is passed. + */ +static int dwc3_rockchip_set_test_mode(struct dwc3_rockchip *rockchip, + u32 mode) +{ + struct dwc3 *dwc = rockchip->dwc; + struct usb_hcd *hcd = dev_get_drvdata(&dwc->xhci->dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + __le32 __iomem **port_array; + int ret; + u32 reg; + + if (hcd->state == HC_STATE_HALT) { + dev_err(rockchip->dev, "HOST is halted!\n"); + return -EINVAL; + } + + switch (mode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + port_array = xhci->usb2_ports; + reg = readl(port_array[0] + 1); + reg &= ~XHCI_TSTCTRL_MASK; + reg |= mode << 28; + writel(reg, port_array[0] + 1); + break; + case USB_SS_PORT_LS_COMP_MOD: + /* + * Enable Inno U3 PHY to toggle CP test pattern + * before set XHCI controller enter compliance mode. + */ + ret = phy_cp_test(dwc->usb3_generic_phy); + if (ret) { + dev_err(rockchip->dev, "phy cp test fail!\n"); + return ret; + } + + port_array = xhci->usb3_ports; + xhci_set_link_state(xhci, port_array, 0, mode); + break; + default: + return -EINVAL; + } + + dev_info(rockchip->dev, "set USB HOST test mode successfully!\n"); + + return 0; +} + +static ssize_t dwc3_rockchip_host_testmode_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct dwc3_rockchip *rockchip = s->private; + u32 testmode = 0; + char buf[32]; + + if (rockchip->dwc->dr_mode == USB_DR_MODE_PERIPHERAL) { + dev_warn(rockchip->dev, "USB HOST not support!\n"); + return -EINVAL; + } + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "test_j", 6)) { + testmode = TEST_J; + } else if (!strncmp(buf, "test_k", 6)) { + testmode = TEST_K; + } else if (!strncmp(buf, "test_se0_nak", 12)) { + testmode = TEST_SE0_NAK; + } else if (!strncmp(buf, "test_packet", 11)) { + testmode = TEST_PACKET; + } else if (!strncmp(buf, "test_force_enable", 17)) { + testmode = TEST_FORCE_EN; + } else if (!strncmp(buf, "test_u3", 7)) { + testmode = USB_SS_PORT_LS_COMP_MOD; + } else { + dev_warn(rockchip->dev, "Test cmd not support!\n"); + return -EINVAL; + } + + dwc3_rockchip_set_test_mode(rockchip, testmode); + + return count; +} + +static const struct file_operations dwc3_host_testmode_fops = { + .open = dwc3_rockchip_host_testmode_open, + .write = dwc3_rockchip_host_testmode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void dwc3_rockchip_debugfs_init(struct dwc3_rockchip *rockchip) +{ + struct dentry *root; + struct dentry *file; + + root = debugfs_create_dir(dev_name(rockchip->dev), NULL); + if (IS_ERR_OR_NULL(root)) { + if (!root) + dev_err(rockchip->dev, "Can't create debugfs root\n"); + return; + } + rockchip->root = root; + + if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) || + IS_ENABLED(CONFIG_USB_DWC3_HOST)) { + file = debugfs_create_file("host_testmode", S_IRUSR | S_IWUSR, + root, rockchip, + &dwc3_host_testmode_fops); + if (!file) + dev_dbg(rockchip->dev, "Can't create debugfs host_testmode\n"); + } +} + static int u3phy_disconnect_det_notifier(struct notifier_block *nb, unsigned long event, void *p) { @@ -183,6 +382,8 @@ static int dwc3_rockchip_probe(struct platform_device *pdev) usb_register_notifier(rockchip->phy, &rockchip->u3phy_nb); } + dwc3_rockchip_debugfs_init(rockchip); + return 0; err2: @@ -209,6 +410,8 @@ static int dwc3_rockchip_remove(struct platform_device *pdev) of_platform_depopulate(dev); + debugfs_remove_recursive(rockchip->root); + pm_runtime_put_sync(dev); pm_runtime_disable(dev); -- 2.34.1