This patch adds support for usb role swap via sysfs "role".

For example:
 1) Connect a usb cable using 2 Salvator-X boards.
  - For A-Device, the cable is connected to CN11 (USB3.0 ch0).
  - For B-Device, the cable is connected to CN9 (USB2.0 ch0).
 2) On A-Device, you input the following command:
  # echo peripheral > /sys/devices/platform/soc/ee020000.usb/role
 3) On B-Device, you input the following command:
  # echo host > /sys/devices/platform/soc/ee080200.usb-phy/role

Then, the A-Device acts as a peripheral and the B-Device acts as
a host. Please note that A-Device must input the following command
if you want the board to act as a host again.
 # echo host > /sys/devices/platform/soc/ee020000.usb/role

Signed-off-by: Yoshihiro Shimoda <[email protected]>
---
 .../ABI/testing/sysfs-platform-renesas_usb3        | 15 +++++
 drivers/usb/gadget/udc/renesas_usb3.c              | 77 ++++++++++++++++++++++
 2 files changed, 92 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-renesas_usb3

diff --git a/Documentation/ABI/testing/sysfs-platform-renesas_usb3 
b/Documentation/ABI/testing/sysfs-platform-renesas_usb3
new file mode 100644
index 0000000..1f63190
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-renesas_usb3
@@ -0,0 +1,15 @@
+What:          /sys/devices/platform/<udc-name>/role
+Date:          March 2017
+KernelVersion: 4.13
+Contact:       Yoshihiro Shimoda <[email protected]>
+Description:
+               This file can be read and write.
+               The file can show/change the drd mode of usb.
+
+               Write the following string to change the mode:
+                "host" - switching mode from peripheral to host.
+                "peripheral" - switching mode from host to peripheral.
+
+               Read the file, then it shows the following strings:
+                "host" - The mode is host now.
+                "peripheral" - The mode is peripheral now.
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c 
b/drivers/usb/gadget/udc/renesas_usb3.c
index 3f04e57..5a4cd85 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -372,6 +372,11 @@ static void usb3_disable_pipe_irq(struct renesas_usb3 
*usb3, int num)
        usb3_clear_bit(usb3, USB_INT_2_PIPE(num), USB3_USB_INT_ENA_2);
 }
 
+static bool usb3_is_host(struct renesas_usb3 *usb3)
+{
+       return !(usb3_read(usb3, USB3_DRD_CON) & DRD_CON_PERI_CON);
+}
+
 static void usb3_init_axi_bridge(struct renesas_usb3 *usb3)
 {
        /* Set AXI_INT */
@@ -568,12 +573,29 @@ static void usb3_mode_a_host(struct renesas_usb3 *usb3)
        usb3_set_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON);
 }
 
+static void usb3_mode_a_peri(struct renesas_usb3 *usb3)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&usb3->lock, flags);
+       usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
+       usb3_set_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON);
+       usb3_connect(usb3);
+       spin_unlock_irqrestore(&usb3->lock, flags);
+}
+
 static void usb3_mode_b_peri(struct renesas_usb3 *usb3)
 {
        usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
        usb3_clear_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON);
 }
 
+static void usb3_mode_b_host(struct renesas_usb3 *usb3)
+{
+       usb3_clear_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
+       usb3_clear_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON);
+}
+
 static bool usb3_is_a_device(struct renesas_usb3 *usb3)
 {
        return !(usb3_read(usb3, USB3_USB_OTG_STA) & USB_OTG_IDMON);
@@ -1831,11 +1853,59 @@ static int renesas_usb3_set_selfpowered(struct 
usb_gadget *gadget, int is_self)
        .set_selfpowered        = renesas_usb3_set_selfpowered,
 };
 
+static ssize_t role_store(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+       bool new_mode_is_host;
+
+       if (!usb3->driver)
+               return -ENODEV;
+
+       if (!strncmp(buf, "host", strlen("host")))
+               new_mode_is_host = true;
+       else if (!strncmp(buf, "peripheral", strlen("peripheral")))
+               new_mode_is_host = false;
+       else
+               return -EINVAL;
+
+       if (new_mode_is_host == usb3_is_host(usb3))
+               return -EINVAL;
+
+       if (new_mode_is_host) {
+               if (usb3_is_a_device(usb3))
+                       usb3_mode_a_host(usb3);
+               else
+                       usb3_mode_b_host(usb3);
+       } else {
+               if (usb3_is_a_device(usb3))
+                       usb3_mode_a_peri(usb3);
+               else
+                       usb3_mode_b_peri(usb3);
+       }
+
+       return count;
+}
+
+static ssize_t role_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+
+       if (!usb3->driver)
+               return -ENODEV;
+
+       return sprintf(buf, "%s\n", usb3_is_host(usb3) ? "host" : "peripheral");
+}
+static DEVICE_ATTR_RW(role);
+
 /*------- platform_driver ------------------------------------------------*/
 static int renesas_usb3_remove(struct platform_device *pdev)
 {
        struct renesas_usb3 *usb3 = platform_get_drvdata(pdev);
 
+       device_remove_file(&pdev->dev, &dev_attr_role);
+
        pm_runtime_put(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
@@ -2038,6 +2108,10 @@ static int renesas_usb3_probe(struct platform_device 
*pdev)
        if (ret < 0)
                goto err_add_udc;
 
+       ret = device_create_file(&pdev->dev, &dev_attr_role);
+       if (ret < 0)
+               goto err_dev_create;
+
        usb3->workaround_for_vbus = priv->workaround_for_vbus;
 
        pm_runtime_enable(&pdev->dev);
@@ -2047,6 +2121,9 @@ static int renesas_usb3_probe(struct platform_device 
*pdev)
 
        return 0;
 
+err_dev_create:
+       usb_del_gadget_udc(&usb3->gadget);
+
 err_add_udc:
        __renesas_usb3_ep_free_request(usb3->ep0_req);
 
-- 
1.9.1

Reply via email to