diff --git a/drivers/usb/dwc3/dwc3-rockchip.c b/drivers/usb/dwc3/dwc3-rockchip.c index 3d3b5774b9bb..d8aa49fb49ca 100644 --- a/drivers/usb/dwc3/dwc3-rockchip.c +++ b/drivers/usb/dwc3/dwc3-rockchip.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -31,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -50,10 +48,11 @@ struct dwc3_rockchip { bool connected; bool skip_suspend; bool suspended; + bool force_mode; + enum usb_dr_mode original_dr_mode; struct device *dev; struct clk **clks; struct dwc3 *dwc; - struct dentry *root; struct reset_control *otg_rst; struct extcon_dev *edev; struct notifier_block device_nb; @@ -62,46 +61,45 @@ struct dwc3_rockchip { struct mutex lock; }; -static int dwc3_rockchip_force_mode_show(struct seq_file *s, void *unused) +static ssize_t dwc3_mode_show(struct device *device, + struct device_attribute *attr, char *buf) { - struct dwc3_rockchip *rockchip = s->private; + struct dwc3_rockchip *rockchip = dev_get_drvdata(device); struct dwc3 *dwc = rockchip->dwc; + int ret; switch (dwc->dr_mode) { case USB_DR_MODE_HOST: - seq_puts(s, "host\n"); + ret = sprintf(buf, "host\n"); break; case USB_DR_MODE_PERIPHERAL: - seq_puts(s, "peripheral\n"); + ret = sprintf(buf, "peripheral\n"); break; case USB_DR_MODE_OTG: - seq_puts(s, "otg\n"); + ret = sprintf(buf, "otg\n"); break; default: - seq_puts(s, "UNKNOWN\n"); + ret = sprintf(buf, "UNKNOWN\n"); } - return 0; + return ret; } -static int dwc3_rockchip_force_mode_open(struct inode *inode, struct file *file) +static ssize_t dwc3_mode_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) { - return single_open(file, dwc3_rockchip_force_mode_show, - inode->i_private); -} - -static ssize_t dwc3_rockchip_force_mode_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; + struct dwc3_rockchip *rockchip = dev_get_drvdata(device); struct dwc3 *dwc = rockchip->dwc; enum usb_dr_mode new_dr_mode; - char buf[32]; - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; + if (!rockchip->original_dr_mode) + rockchip->original_dr_mode = dwc->dr_mode; + + if (rockchip->original_dr_mode != USB_DR_MODE_OTG) { + dev_err(rockchip->dev, "Not support set mode!\n"); + return -EINVAL; + } if (!strncmp(buf, "0", 1) || !strncmp(buf, "otg", 3)) { new_dr_mode = USB_DR_MODE_OTG; @@ -119,108 +117,37 @@ static ssize_t dwc3_rockchip_force_mode_write(struct file *file, return count; } + rockchip->force_mode = true; + /* - * Disconnect vbus to trigger gadget disconnect process by setting - * the mode of phy to invalid if the current dr_mode of controller - * is peripheral, than schedule otg work to change connect status - * and suspend the controller. + * If force to host mode from current peripheral mode, firstly, + * set the usb2 phy mode to PHY_MODE_INVALID to disable vbus + * detection function in usb2 phy, this can help to trigger + * the peripheral disconnect by software. */ if (dwc->dr_mode == USB_DR_MODE_PERIPHERAL) phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_INVALID); + dwc->dr_mode = USB_DR_MODE_OTG; schedule_work(&rockchip->otg_work); flush_work(&rockchip->otg_work); - /* - * Schedule otg work to change connect status and resume the - * controller, than connect vbus to trigger connect interrupt - * and connect gadget by setting the mode of phy to device if - * the dr_mode is peripheral. - */ + /* Schedule the otg work to set the otg to new mode. */ dwc->dr_mode = new_dr_mode; schedule_work(&rockchip->otg_work); flush_work(&rockchip->otg_work); + + /* Set phy mode */ if (dwc->dr_mode == USB_DR_MODE_PERIPHERAL) phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); else if (dwc->dr_mode == USB_DR_MODE_HOST) phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); + dev_info(rockchip->dev, "set new mode successfully\n"); return count; } -static const struct file_operations dwc3_rockchip_force_mode_fops = { - .open = dwc3_rockchip_force_mode_open, - .write = dwc3_rockchip_force_mode_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -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, set test mode first!\n"); - return 0; - } - - port_array = xhci->usb2_ports; - reg = readl(port_array[0] + PORTPMSC); - 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); -} - +#if defined(CONFIG_USB_DWC3_HOST) || defined(CONFIG_USB_DWC3_DUAL_ROLE) /** * dwc3_rockchip_set_test_mode - Enables USB2/USB3 HOST Test Modes * @rockchip: pointer to our context structure @@ -273,26 +200,81 @@ static int dwc3_rockchip_set_test_mode(struct dwc3_rockchip *rockchip, return 0; } -static ssize_t dwc3_rockchip_host_testmode_write(struct file *file, - const char __user *ubuf, - size_t count, loff_t *ppos) +static ssize_t host_testmode_show(struct device *device, + struct device_attribute *attr, char *buf) { - struct seq_file *s = file->private_data; - struct dwc3_rockchip *rockchip = s->private; + struct dwc3_rockchip *rockchip = dev_get_drvdata(device); + 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; + int ret; + + if (rockchip->dwc->dr_mode == USB_DR_MODE_PERIPHERAL) { + dev_warn(rockchip->dev, "USB peripheral not support!\n"); + return 0; + } + + if (hcd->state == HC_STATE_HALT) { + dev_warn(rockchip->dev, "HOST is halted, set test mode first!\n"); + return 0; + } + + port_array = xhci->usb2_ports; + reg = readl(port_array[0] + PORTPMSC); + reg &= XHCI_TSTCTRL_MASK; + reg >>= 28; + + switch (reg) { + case 0: + ret = sprintf(buf, "U2: no test\n"); + break; + case TEST_J: + ret = sprintf(buf, "U2: test_j\n"); + break; + case TEST_K: + ret = sprintf(buf, "U2: test_k\n"); + break; + case TEST_SE0_NAK: + ret = sprintf(buf, "U2: test_se0_nak\n"); + break; + case TEST_PACKET: + ret = sprintf(buf, "U2: test_packet\n"); + break; + case TEST_FORCE_EN: + ret = sprintf(buf, "U2: test_force_enable\n"); + break; + default: + ret = sprintf(buf, "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) + ret += sprintf(buf + ret, "U3: compliance mode\n"); + else + ret += sprintf(buf + ret, "U3: UNKNOWN %d\n", reg >> 5); + + return ret; +} + +static ssize_t host_testmode_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dwc3_rockchip *rockchip = dev_get_drvdata(device); struct extcon_dev *edev = rockchip->edev; u32 testmode = 0; - bool flip = 0; - char buf[32]; + bool flip = false; union extcon_property_value property; if (rockchip->dwc->dr_mode == USB_DR_MODE_PERIPHERAL) { - dev_warn(rockchip->dev, "USB HOST not support!\n"); - return -EINVAL; + dev_warn(rockchip->dev, "USB peripheral not support!\n"); + return count; } - 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)) { @@ -307,10 +289,10 @@ static ssize_t dwc3_rockchip_host_testmode_write(struct file *file, testmode = USB_SS_PORT_LS_COMP_MOD; } else if (!strncmp(buf, "test_flip_u3", 12)) { testmode = USB_SS_PORT_LS_COMP_MOD; - flip = 1; + flip = true; } else { - dev_warn(rockchip->dev, "Test cmd not support!\n"); - return -EINVAL; + dev_warn(rockchip->dev, "Cmd not support! Try test_u3 or test_packet\n"); + return count; } if (edev && !extcon_get_cable_state_(edev, EXTCON_USB_HOST)) { @@ -324,6 +306,8 @@ static ssize_t dwc3_rockchip_host_testmode_write(struct file *file, /* Add a delay 1s to wait for XHCI HCD init */ msleep(1000); + + rockchip->dwc->dr_mode = USB_DR_MODE_HOST; } dwc3_rockchip_set_test_mode(rockchip, testmode); @@ -331,47 +315,23 @@ static ssize_t dwc3_rockchip_host_testmode_write(struct file *file, 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 DEVICE_ATTR_RW(host_testmode); +#endif + +static DEVICE_ATTR_RW(dwc3_mode); + +static struct attribute *dwc3_rockchip_attrs[] = { + &dev_attr_dwc3_mode.attr, +#if defined(CONFIG_USB_DWC3_HOST) || defined(CONFIG_USB_DWC3_DUAL_ROLE) + &dev_attr_host_testmode.attr, +#endif + NULL, }; -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"); - } - - if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) && - !rockchip->edev && (rockchip->dwc->dr_mode == USB_DR_MODE_OTG)) { - file = debugfs_create_file("rk_usb_force_mode", - S_IRUSR | S_IWUSR, - root, rockchip, - &dwc3_rockchip_force_mode_fops); - if (!file) - dev_dbg(rockchip->dev, - "Can't create debugfs rk_usb_force_mode\n"); - } -} +static struct attribute_group dwc3_rockchip_attr_group = { + .name = NULL, /* we want them in the same directory */ + .attrs = dwc3_rockchip_attrs, +}; static int dwc3_rockchip_device_notifier(struct notifier_block *nb, unsigned long event, void *ptr) @@ -413,8 +373,8 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work) mutex_lock(&rockchip->lock); - if (rockchip->edev ? extcon_get_cable_state_(edev, EXTCON_USB) : - (dwc->dr_mode == USB_DR_MODE_PERIPHERAL)) { + if (rockchip->force_mode ? dwc->dr_mode == USB_DR_MODE_PERIPHERAL : + extcon_get_cable_state_(edev, EXTCON_USB)) { if (rockchip->connected) goto out; @@ -465,9 +425,8 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work) rockchip->connected = true; dev_info(rockchip->dev, "USB peripheral connected\n"); - } else if (rockchip->edev ? - extcon_get_cable_state_(edev, EXTCON_USB_HOST) : - (dwc->dr_mode == USB_DR_MODE_HOST)) { + } else if (rockchip->force_mode ? dwc->dr_mode == USB_DR_MODE_HOST : + extcon_get_cable_state_(edev, EXTCON_USB_HOST)) { if (rockchip->connected) { reg = dwc3_readl(dwc->regs, DWC3_GCTL); @@ -847,7 +806,9 @@ static int dwc3_rockchip_probe(struct platform_device *pdev) rockchip->connected = true; } - dwc3_rockchip_debugfs_init(rockchip); + ret = sysfs_create_group(&dev->kobj, &dwc3_rockchip_attr_group); + if (ret) + dev_err(dev, "failed to create sysfs group: %d\n", ret); mutex_unlock(&rockchip->lock); @@ -881,7 +842,7 @@ static int dwc3_rockchip_remove(struct platform_device *pdev) dwc3_rockchip_extcon_unregister(rockchip); - debugfs_remove_recursive(rockchip->root); + sysfs_remove_group(&dev->kobj, &dwc3_rockchip_attr_group); /* Restore hcd state before unregistering xhci */ if (rockchip->edev && !rockchip->connected) {