We have more use cases need to consider for vbus/id handler, below are
current use cases we know now:
- The vbus/id value can be read from external connectors, eg, gpio
- The vbus/id value can be read from register OTGSC
- The vbus is always on
- There is no ID pin, but need to switch role through debugfs

So, in order to cover all cases, we improve the OTG handler like below:
- ci->is_otg flag means it is OTG controller, and the register OTGSC can
be accessed, but vbus/id value from OTG registers are not mandatory.
- ci_otg workqueue is used to get id/vbus value, no matter the values are
from extcon or register OTGSC.
- When we try to get id/vbus value, we try to get them from extcon first,
then from register OTGSC.
- Introduce two new APIs ci_write_otgsc and ci_read_otgsc to get vbus and
id value. The hw_read_otgsc and hw_read_otgsc are only used to access
OTGSC register at OTG FSM mode only.

Signed-off-by: Peter Chen <peter.c...@nxp.com>
cc: Sanchayan Maity <maitysancha...@gmail.com>
cc: ivan.iva...@linaro.org
---
 drivers/usb/chipidea/ci.h    |   5 +-
 drivers/usb/chipidea/core.c  |  43 ++++++----------
 drivers/usb/chipidea/debug.c |   6 +--
 drivers/usb/chipidea/otg.c   | 116 ++++++++++++++++++++++++++++---------------
 drivers/usb/chipidea/otg.h   |   2 +
 drivers/usb/chipidea/udc.c   |  10 ++--
 6 files changed, 103 insertions(+), 79 deletions(-)

diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index f81dd3b..07141a6 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -162,7 +162,7 @@ struct hw_bank {
  * @irq: IRQ number
  * @roles: array of supported roles for this controller
  * @role: current role
- * @is_otg: if the device is otg-capable
+ * @is_otg: if the device is otg-capable (the register OTGSC can be accessed)
  * @fsm: otg finite state machine
  * @otg_fsm_hrtimer: hrtimer for otg fsm timers
  * @hr_timeouts: time out list for active otg fsm timers
@@ -200,6 +200,8 @@ struct hw_bank {
  * @wakeup_int: if wakeup interrupt occur
  * @rev: The revision number for controller
  * @dp_always_pullup: keep dp always pullup at device mode
+ * @fake_otgsc: record id/vbus status when these two values are not from
+ * registers
  */
 struct ci_hdrc {
        struct device                   *dev;
@@ -250,6 +252,7 @@ struct ci_hdrc {
        bool                            wakeup_int;
        enum ci_revision                rev;
        bool                            dp_always_pullup;
+       u32                             fake_otgsc;
 };
 
 static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 6f22c6c..b37bc52 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -562,23 +562,21 @@ static irqreturn_t ci_irq(int irq, void *data)
                return IRQ_HANDLED;
        }
 
-       if (ci->is_otg) {
-               otgsc = hw_read_otgsc(ci, ~0);
-               if (ci_otg_is_fsm_mode(ci)) {
-                       ret = ci_otg_fsm_irq(ci);
-                       if (ret == IRQ_HANDLED)
-                               return ret;
-               }
+       otgsc = ci_read_otgsc(ci, ~0);
+       if (ci_otg_is_fsm_mode(ci)) {
+               ret = ci_otg_fsm_irq(ci);
+               if (ret == IRQ_HANDLED)
+                       return ret;
        }
 
        /*
         * Handle id change interrupt, it indicates device/host function
         * switch.
         */
-       if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
+       if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
                ci->id_event = true;
                /* Clear ID change irq status */
-               hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
+               ci_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
                ci_otg_queue_work(ci);
                return IRQ_HANDLED;
        }
@@ -587,10 +585,10 @@ static irqreturn_t ci_irq(int irq, void *data)
         * Handle vbus change interrupt, it indicates device connection
         * and disconnection events.
         */
-       if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
+       if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
                ci->b_sess_valid_event = true;
                /* Clear BSV irq */
-               hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
+               ci_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
                ci_otg_queue_work(ci);
                return IRQ_HANDLED;
        }
@@ -909,12 +907,12 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
                ci->is_otg = (hw_read(ci, CAP_DCCPARAMS,
                                DCCPARAMS_DC | DCCPARAMS_HC)
                                        == (DCCPARAMS_DC | DCCPARAMS_HC));
-       if (ci->is_otg) {
-               dev_dbg(ci->dev, "It is OTG capable controller\n");
-               /* Disable and clear all OTG irq */
-               hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
-                                                       OTGSC_INT_STATUS_BITS);
-       }
+       /* Disable and clear all OTG irq */
+       ci_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
+                       OTGSC_INT_STATUS_BITS);
+
+       dev_dbg(ci->dev, "It is %sOTG capable controller\n",
+               ci->is_otg ? "" : "non-");
 }
 
 static int ci_hdrc_probe(struct platform_device *pdev)
@@ -1023,18 +1021,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        }
 
        if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
-               if (ci->is_otg) {
                        ci->role = ci_otg_role(ci);
                        /* Enable ID change irq */
-                       hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE);
-               } else {
-                       /*
-                        * If the controller is not OTG capable, but support
-                        * role switch, the defalt role is gadget, and the
-                        * user can switch it through debugfs.
-                        */
-                       ci->role = CI_ROLE_GADGET;
-               }
+                       ci_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE);
        } else {
                ci->role = ci->roles[CI_ROLE_HOST]
                        ? CI_ROLE_HOST
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index 6d23eed..91b8c26 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -367,10 +367,8 @@ static int ci_registers_show(struct seq_file *s, void 
*unused)
        tmp_reg = hw_read(ci, OP_PORTSC, ~0);
        seq_printf(s, "PORTSC reg: %08x\n", tmp_reg);
 
-       if (ci->is_otg) {
-               tmp_reg = hw_read_otgsc(ci, ~0);
-               seq_printf(s, "OTGSC reg: %08x\n", tmp_reg);
-       }
+       tmp_reg = ci_read_otgsc(ci, ~0);
+       seq_printf(s, "OTGSC(may be fake one) reg : %08x\n", tmp_reg);
 
        return 0;
 }
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 58be59e..b02a80e 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -25,53 +25,76 @@
 #include "otg_fsm.h"
 
 /**
- * hw_read_otgsc returns otgsc register bits value.
+ * hw_read_otgsc returns otgsc register bits value, for OTG FSM only.
  * @mask: bitfield mask
  */
 u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
 {
-       struct ci_hdrc_cable *cable;
-       u32 val = hw_read(ci, OP_OTGSC, mask);
-
-       /*
-        * If using extcon framework for VBUS and/or ID signal
-        * detection overwrite OTGSC register value
-        */
-       cable = &ci->platdata->vbus_extcon;
-       if (!IS_ERR(cable->edev)) {
-               if (cable->changed)
-                       val |= OTGSC_BSVIS;
-               else
-                       val &= ~OTGSC_BSVIS;
-
-               cable->changed = false;
-
-               if (cable->state)
-                       val |= OTGSC_BSV;
-               else
-                       val &= ~OTGSC_BSV;
-       }
-
-       cable = &ci->platdata->id_extcon;
-       if (!IS_ERR(cable->edev)) {
-               if (cable->changed)
-                       val |= OTGSC_IDIS;
-               else
-                       val &= ~OTGSC_IDIS;
-
-               cable->changed = false;
+       return hw_read(ci, OP_OTGSC, mask);
+}
 
-               if (cable->state)
-                       val |= OTGSC_ID;
-               else
-                       val &= ~OTGSC_ID;
+/**
+ * ci_read_otgsc returns otgsc bits value.
+ * The value may from:
+ *     - register OTGSC
+ *     - extcon status
+ *
+ * @ci: the controller struct
+ * @mask: bitfield mask
+ */
+u32 ci_read_otgsc(struct ci_hdrc *ci, u32 mask)
+{
+       struct ci_hdrc_cable *cable_vbus;
+       struct ci_hdrc_cable *cable_id;
+       u32 val = ci->fake_otgsc;
+
+       cable_vbus = &ci->platdata->vbus_extcon;
+       cable_id = &ci->platdata->vbus_extcon;
+       if (!IS_ERR(cable_vbus->edev) || !IS_ERR(cable_id->edev)) {
+               if (!IS_ERR(cable_vbus->edev)) {
+                       if (cable_vbus->changed)
+                               val |= OTGSC_BSVIS;
+                       else
+                               val &= ~OTGSC_BSVIS;
+
+                       cable_vbus->changed = false;
+
+                       if (cable_vbus->state)
+                               val |= OTGSC_BSV;
+                       else
+                               val &= ~OTGSC_BSV;
+               }
+
+               if (!IS_ERR(cable_id->edev)) {
+                       if (cable_id->changed)
+                               val |= OTGSC_IDIS;
+                       else
+                               val &= ~OTGSC_IDIS;
+
+                       cable_id->changed = false;
+
+                       if (cable_id->state)
+                               val |= OTGSC_ID;
+                       else
+                               val &= ~OTGSC_ID;
+               }
+               return val;
+       } else if (ci->is_otg) { /* read otgsc from register */
+               return hw_read_otgsc(ci, mask);
+       } else if (mask == OTGSC_ID) {
+               /*
+                * Can't decide role from external pin, but the controller
+                * is dual role, we take the default role as gadget,
+                * and the user can switch it through debugfs.
+                */
+               return OTGSC_ID;
+       } else {
+               return 0;
        }
-
-       return val;
 }
 
 /**
- * hw_write_otgsc updates target bits of OTGSC register.
+ * hw_write_otgsc updates target bits of OTGSC register, for OTG FSM only
  * @mask: bitfield mask
  * @data: to be written
  */
@@ -81,12 +104,23 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
 }
 
 /**
+ * ci_write_otgsc: only update register for OTG controller
+ * @mask: bitfield mask
+ * @data: to be written
+ */
+void ci_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
+{
+       if (ci->is_otg)
+               hw_write_otgsc(ci, mask, data);
+}
+
+/**
  * ci_otg_role - pick role based on ID pin state
  * @ci: the controller
  */
 enum ci_role ci_otg_role(struct ci_hdrc *ci)
 {
-       enum ci_role role = hw_read_otgsc(ci, OTGSC_ID)
+       enum ci_role role = ci_read_otgsc(ci, OTGSC_ID)
                ? CI_ROLE_GADGET
                : CI_ROLE_HOST;
 
@@ -100,7 +134,7 @@ void ci_handle_vbus_change(struct ci_hdrc *ci)
                return;
        }
 
-       if (hw_read_otgsc(ci, OTGSC_BSV))
+       if (ci_read_otgsc(ci, OTGSC_BSV))
                usb_gadget_vbus_connect(&ci->gadget);
        else
                usb_gadget_vbus_disconnect(&ci->gadget);
@@ -183,7 +217,7 @@ void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
                destroy_workqueue(ci->wq);
        }
        /* Disable all OTG irq and clear status */
-       hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
+       ci_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
                                                OTGSC_INT_STATUS_BITS);
        if (ci_otg_is_fsm_mode(ci))
                ci_hdrc_otg_fsm_remove(ci);
diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h
index 9ecb598..26b6548 100644
--- a/drivers/usb/chipidea/otg.h
+++ b/drivers/usb/chipidea/otg.h
@@ -12,7 +12,9 @@
 #define __DRIVERS_USB_CHIPIDEA_OTG_H
 
 u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask);
+u32 ci_read_otgsc(struct ci_hdrc *ci, u32 mask);
 void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data);
+void ci_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data);
 int ci_hdrc_otg_init(struct ci_hdrc *ci);
 void ci_hdrc_otg_destroy(struct ci_hdrc *ci);
 enum ci_role ci_otg_role(struct ci_hdrc *ci);
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 065f5d9..6986a50 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1956,10 +1956,9 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
 
 static int udc_id_switch_for_device(struct ci_hdrc *ci)
 {
-       if (ci->is_otg)
-               /* Clear and enable BSV irq */
-               hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
-                                       OTGSC_BSVIS | OTGSC_BSVIE);
+       /* Clear and enable BSV irq */
+       ci_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
+                               OTGSC_BSVIS | OTGSC_BSVIE);
 
        return 0;
 }
@@ -1970,8 +1969,7 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
         * host doesn't care B_SESSION_VALID event
         * so clear and disbale BSV irq
         */
-       if (ci->is_otg)
-               hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
+       ci_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
 }
 
 /**
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to