From 3e4bb69e4ab2cc90cc33996ac65298ea06a17278 Mon Sep 17 00:00:00 2001 From: Meng Dongyang Date: Wed, 11 Jan 2017 10:46:24 +0800 Subject: [PATCH] usb: dwc3: rockchip: add force dr_mode function In current code, the mode of usb is control by extcon or define in the dts. So in the case of none extcon, we can't change the usb mode. This patch add a node in debug file system for the application to change usb mode even if the extcon is not exist. (P.S. The extcon property must not be set and dr_mode must be otg if you want to use rk_usb_force_mode.) usage: config host dr_mode: echo host > sys/kernel/debug/usb@fe800000/rk_usb_force_mode config peripheral dr_mode: echo peripheral > sys/kernel/debug/usb@fe800000/rk_usb_force_mode config otg dr_mode: echo otg > sys/kernel/debug/usb@fe800000/rk_usb_force_mode Change-Id: I9cb3b14b7a365fe74e4aa253f0e3f680f45e0f9f Signed-off-by: Meng Dongyang --- drivers/usb/dwc3/dwc3-rockchip.c | 119 +++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-rockchip.c b/drivers/usb/dwc3/dwc3-rockchip.c index 00e47f744310..1c8829bd2f91 100644 --- a/drivers/usb/dwc3/dwc3-rockchip.c +++ b/drivers/usb/dwc3/dwc3-rockchip.c @@ -62,6 +62,98 @@ struct dwc3_rockchip { struct mutex lock; }; +static int dwc3_rockchip_force_mode_show(struct seq_file *s, void *unused) +{ + struct dwc3_rockchip *rockchip = s->private; + struct dwc3 *dwc = rockchip->dwc; + + switch (dwc->dr_mode) { + case USB_DR_MODE_HOST: + seq_puts(s, "host\n"); + break; + case USB_DR_MODE_PERIPHERAL: + seq_puts(s, "peripheral\n"); + break; + case USB_DR_MODE_OTG: + seq_puts(s, "otg\n"); + break; + default: + seq_puts(s, "UNKNOWN\n"); + } + + return 0; +} + +static int dwc3_rockchip_force_mode_open(struct inode *inode, struct file *file) +{ + 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 *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 (!strncmp(buf, "0", 1) || !strncmp(buf, "otg", 3)) { + new_dr_mode = USB_DR_MODE_OTG; + } else if (!strncmp(buf, "1", 1) || !strncmp(buf, "host", 4)) { + new_dr_mode = USB_DR_MODE_HOST; + } else if (!strncmp(buf, "2", 1) || !strncmp(buf, "peripheral", 10)) { + new_dr_mode = USB_DR_MODE_PERIPHERAL; + } else { + dev_info(rockchip->dev, "illegal dr_mode\n"); + return count; + } + + if (dwc->dr_mode == new_dr_mode) { + dev_info(rockchip->dev, "Same with current dr_mode\n"); + return count; + } + + /* + * 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 (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. + */ + dwc->dr_mode = new_dr_mode; + schedule_work(&rockchip->otg_work); + flush_work(&rockchip->otg_work); + if (dwc->dr_mode == USB_DR_MODE_PERIPHERAL) + phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); + + 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; @@ -266,6 +358,17 @@ static void dwc3_rockchip_debugfs_init(struct dwc3_rockchip *rockchip) 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 int dwc3_rockchip_device_notifier(struct notifier_block *nb, @@ -307,7 +410,8 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work) mutex_lock(&rockchip->lock); - if (extcon_get_cable_state_(edev, EXTCON_USB) > 0) { + if (rockchip->edev ? extcon_get_cable_state_(edev, EXTCON_USB) : + (dwc->dr_mode == USB_DR_MODE_PERIPHERAL)) { if (rockchip->connected) goto out; @@ -348,7 +452,9 @@ 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 (extcon_get_cable_state_(edev, EXTCON_USB_HOST) > 0) { + } else if (rockchip->edev ? + extcon_get_cable_state_(edev, EXTCON_USB_HOST) : + (dwc->dr_mode == USB_DR_MODE_HOST)) { if (rockchip->connected) goto out; @@ -535,9 +641,6 @@ static int dwc3_rockchip_extcon_register(struct dwc3_rockchip *rockchip) return PTR_ERR(edev); } - INIT_WORK(&rockchip->otg_work, - dwc3_rockchip_otg_extcon_evt_work); - rockchip->device_nb.notifier_call = dwc3_rockchip_device_notifier; ret = extcon_register_notifier(edev, EXTCON_USB, @@ -573,7 +676,6 @@ static void dwc3_rockchip_extcon_unregister(struct dwc3_rockchip *rockchip) &rockchip->device_nb); extcon_unregister_notifier(rockchip->edev, EXTCON_USB_HOST, &rockchip->host_nb); - cancel_work_sync(&rockchip->otg_work); } static int dwc3_rockchip_probe(struct platform_device *pdev) @@ -658,6 +760,8 @@ static int dwc3_rockchip_probe(struct platform_device *pdev) goto err1; } + INIT_WORK(&rockchip->otg_work, dwc3_rockchip_otg_extcon_evt_work); + child_pdev = of_find_device_by_node(child); if (!child_pdev) { dev_err(dev, "failed to find dwc3 core device\n"); @@ -676,7 +780,7 @@ static int dwc3_rockchip_probe(struct platform_device *pdev) if (ret < 0) goto err2; - if (rockchip->edev) { + if (rockchip->edev || (rockchip->dwc->dr_mode == USB_DR_MODE_OTG)) { if (rockchip->dwc->dr_mode == USB_DR_MODE_HOST || rockchip->dwc->dr_mode == USB_DR_MODE_OTG) { struct usb_hcd *hcd = @@ -715,6 +819,7 @@ err3: dwc3_rockchip_extcon_unregister(rockchip); err2: + cancel_work_sync(&rockchip->otg_work); of_platform_depopulate(dev); err1: -- 2.34.1