[PATCH 2/2] usb: chipidea: improve the OTG handler

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.chen@xxxxxxx>
cc: Sanchayan Maity <maitysanchayan@xxxxxxxxx>
cc: ivan.ivanov@xxxxxxxxxx
---
 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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux