imx6Q USB OTG Host/Device纯软件切换

From 87f295ccaf081b4ef06218fc8028d3c3d075ede7 Mon Sep 17 00:00:00 2001
From: hongjiujin <hongjiujin@xxx.com>
Date: Tue, 2 Jan 2018 10:39:04 +0800
Subject: [PATCH] imx6: chipidea

The hardware didn't use OTG_ID pin in design and use USB A for OTG.
Chipidea will initialize otg to HOST once boot completed, which read OTGSC_IDIE.
So we implement otg role switch in SW only(/sys/kernel/debug/ci_hdrc.0/otgswitch).
---
 debug.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)

diff --git a/debug.c b/debug.c
index a4f7db2..b215674 100644
--- a/debug.c
+++ b/debug.c
@@ -16,6 +16,7 @@
 #include "udc.h"
 #include "bits.h"
 #include "otg.h"
+#include "host.h"
 
 /**
  * ci_device_show: prints information about device capabilities and status
@@ -343,6 +344,110 @@ static const struct file_operations ci_role_fops = {
     .release    = single_release,
 };
 
+#define CI_VBUS_STABLE_TIMEOUT_MS 100
+
+void handle_otg_switch(struct ci_hdrc *ci, enum ci_role role)
+{
+    int ret = 0;
+
+    if (role != ci->role) {
+        dev_dbg(ci->dev, "switching from %s to %s\n",
+            ci_role(ci)->name, ci->roles[role]->name);
+
+        while (ci_hdrc_host_has_device(ci)) {
+            enable_irq(ci->irq);
+            usleep_range(10000, 15000);
+            disable_irq_nosync(ci->irq);
+        }
+
+        ci_role_stop(ci);
+
+        if (role == CI_ROLE_GADGET) {
+            /* wait vbus lower than OTGSC_BSV */
+            ret = hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
+                    CI_VBUS_STABLE_TIMEOUT_MS);
+        }
+        else if (ci->vbus_active)
+            /*
+             * If the role switch happens(e.g. during
+             * system sleep), and we lose vbus drop
+             * event, disconnect gadget for it before
+             * start host.
+             */
+               usb_gadget_vbus_disconnect(&ci->gadget);
+
+        ci_role_start(ci, role);
+        /*
+         * If the role switch happens(e.g. during system
+         * sleep) and vbus keeps on afterwards, we connect
+         * gadget as vbus connect event lost.
+         */
+        if (ret == -ETIMEDOUT)
+            usb_gadget_vbus_connect(&ci->gadget);
+    }
+}
+
+static int ci_otgswitch_show(struct seq_file *s, void *data)
+{
+    struct ci_hdrc *ci = s->private;
+
+    seq_printf(s, "%s\n", ci_role(ci)->name);
+
+    return 0;
+}
+
+static ssize_t ci_otgswitch_write(struct file *file, const char __user *ubuf,
+                 size_t count, loff_t *ppos)
+{
+    struct seq_file *s = file->private_data;
+    struct ci_hdrc *ci = s->private;
+    enum ci_role role;
+    char buf[8];
+    int ret;
+
+    if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+    return -EFAULT;
+
+    for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
+     if (ci->roles[role] &&
+        !strncmp(buf, ci->roles[role]->name,
+        strlen(ci->roles[role]->name)))
+        break;
+
+     if (role == CI_ROLE_END || role == ci->role)
+        return -EINVAL;
+
+    pm_runtime_get_sync(ci->dev);
+    disable_irq(ci->irq);
+
+    if (role == CI_ROLE_HOST) {
+        ci->fsm.id = 0;
+
+    } else if (role == CI_ROLE_GADGET) {
+        ci->fsm.id = 1;
+    }
+
+    handle_otg_switch(ci, role);
+    enable_irq(ci->irq);
+    pm_runtime_put_sync(ci->dev);
+
+    return ret ? ret : count;
+}
+
+
+static int ci_otgswitch_open(struct inode *inode, struct file *file)
+{
+    return single_open(file, ci_otgswitch_show, inode->i_private);
+}
+
+static const struct file_operations ci_otgswitch_fops = {
+    .open        = ci_otgswitch_open,
+    .write        = ci_otgswitch_write,
+    .read        = seq_read,
+    .llseek        = seq_lseek,
+    .release    = single_release,
+};
+
 static int ci_registers_show(struct seq_file *s, void *unused)
 {
     struct ci_hdrc *ci = s->private;
@@ -433,6 +538,11 @@ int dbg_create_files(struct ci_hdrc *ci)
     if (!dent)
         goto err;
 
+    dent = debugfs_create_file("otgswitch", S_IRUGO | S_IWUSR, ci->debugfs, ci,
+                   &ci_otgswitch_fops);
+    if (!dent)
+        goto err;
+
     dent = debugfs_create_file("registers", S_IRUGO, ci->debugfs, ci,
                 &ci_registers_fops);
 
--
2.7.4


posted on 2018-01-02 10:58  hongjiujing  阅读(2007)  评论(0编辑  收藏  举报

导航