From: Roger Quadros <rogerq@ti.com>
To: <gregkh@linuxfoundation.org>, <balbi@ti.com>,
<stern@rowland.harvard.edu>
Cc: <dan.j.williams@intel.com>, <peter.chen@freescale.com>,
<jun.li@freescale.com>, <mathias.nyman@linux.intel.com>,
<tony@atomide.com>, <linux-usb@vger.kernel.org>,
<linux-kernel@vger.kernel.org>, <linux-omap@vger.kernel.org>,
Roger Quadros <rogerq@ti.com>
Subject: [RFC][PATCH v2 07/13] usb: otg: Add dual-role device (DRD) support
Date: Tue, 14 Apr 2015 13:41:54 +0300 [thread overview]
Message-ID: <1429008120-5395-8-git-send-email-rogerq@ti.com> (raw)
In-Reply-To: <1429008120-5395-1-git-send-email-rogerq@ti.com>
DRD mode is a reduced functionality OTG mode. In this mode
we don't support SRP, HNP and dynamic role-swap.
In DRD operation, the controller mode (Host or Peripheral)
is decided based on the ID pin status. Once a cable plug (Type-A
or Type-B) is attached the controller selects the state
and doesn't change till the cable in unplugged and a different
cable type is inserted.
As we don't need most of the complex OTG states and OTG timers
we implement a lean DRD state machine in usb-otg.c.
The DRD state machine is only interested in 2 hardware inputs
'id' and 'vbus; that are still passed via the origintal struct otg_fsm.
Most of the usb-otg.c functionality remains the same except
adding a new parameter to usb_otg_register() to indicate that
the OTG controller needs to operate in DRD mode.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/common/usb-otg.c | 179 ++++++++++++++++++++++++++++++++++++++++---
include/linux/usb/otg-fsm.h | 8 +-
include/linux/usb/usb-otg.h | 5 +-
3 files changed, 179 insertions(+), 13 deletions(-)
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 860e2e7..6d6da86 100644
--- a/drivers/usb/common/usb-otg.c
+++ b/drivers/usb/common/usb-otg.c
@@ -44,6 +44,7 @@ struct otg_hcd {
struct otg_data {
struct device *dev; /* HCD & GCD's parent device */
+ bool drd_only; /* Dual-role only, no OTG features */
struct otg_fsm fsm;
/* HCD, GCD and usb_otg_state are present in otg_fsm->otg
* HCD is bus_to_hcd(fsm->otg->host)
@@ -251,22 +252,172 @@ static int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
return 0;
}
+/* Change USB protocol when there is a protocol change */
+static int drd_set_protocol(struct otg_fsm *fsm, int protocol)
+{
+ struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
+ int ret = 0;
+
+ if (fsm->protocol != protocol) {
+ dev_dbg(otgd->dev, "drd: changing role fsm->protocol= %d; new protocol= %d\n",
+ fsm->protocol, protocol);
+ /* stop old protocol */
+ if (fsm->protocol == PROTO_HOST)
+ ret = otg_start_host(fsm, 0);
+ else if (fsm->protocol == PROTO_GADGET)
+ ret = otg_start_gadget(fsm, 0);
+ if (ret)
+ return ret;
+
+ /* start new protocol */
+ if (protocol == PROTO_HOST)
+ ret = otg_start_host(fsm, 1);
+ else if (protocol == PROTO_GADGET)
+ ret = otg_start_gadget(fsm, 1);
+ if (ret)
+ return ret;
+
+ fsm->protocol = protocol;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Called when entering a DRD state */
+static void drd_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+{
+ struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
+
+ if (fsm->otg->state == new_state)
+ return;
+
+ fsm->state_changed = 1;
+ dev_dbg(otgd->dev, "drd: set state: %s\n",
+ usb_otg_state_string(new_state));
+ switch (new_state) {
+ case OTG_STATE_B_IDLE:
+ drd_set_protocol(fsm, PROTO_UNDEF);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ drd_set_protocol(fsm, PROTO_GADGET);
+ break;
+ case OTG_STATE_A_HOST:
+ drd_set_protocol(fsm, PROTO_HOST);
+ break;
+ case OTG_STATE_UNDEFINED:
+ case OTG_STATE_B_SRP_INIT:
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+ case OTG_STATE_A_IDLE:
+ case OTG_STATE_A_WAIT_VRISE:
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_SUSPEND:
+ case OTG_STATE_A_PERIPHERAL:
+ case OTG_STATE_A_WAIT_VFALL:
+ case OTG_STATE_A_VBUS_ERR:
+ default:
+ dev_warn(otgd->dev, "%s: drd: invalid state: %s\n",
+ __func__, usb_otg_state_string(new_state));
+ break;
+ }
+
+ fsm->otg->state = new_state;
+}
+
/**
- * OTG FSM work function
+ * DRD state change judgement
+ *
+ * For DRD we're only interested in some of the OTG states
+ * i.e. OTG_STATE_B_IDLE: both peripheral and host are stopped
+ * OTG_STATE_B_PERIPHERAL: peripheral active
+ * OTG_STATE_A_HOST: host active
+ * we're only interested in the following inputs
+ * fsm->id, fsm->vbus
+ */
+static int drd_statemachine(struct otg_fsm *fsm)
+{
+ struct otg_data *otgd = container_of(fsm, struct otg_data, fsm);
+ enum usb_otg_state state;
+
+ mutex_lock(&fsm->lock);
+
+ state = fsm->otg->state;
+
+ switch (state) {
+ case OTG_STATE_UNDEFINED:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->id && fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_B_IDLE:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ if (!fsm->id)
+ drd_set_state(fsm, OTG_STATE_A_HOST);
+ else if (!fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_A_HOST:
+ if (fsm->id && fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else if (fsm->id && !fsm->vbus)
+ drd_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+
+ /* invalid states for DRD */
+ case OTG_STATE_B_SRP_INIT:
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+ case OTG_STATE_A_IDLE:
+ case OTG_STATE_A_WAIT_VRISE:
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_SUSPEND:
+ case OTG_STATE_A_PERIPHERAL:
+ case OTG_STATE_A_WAIT_VFALL:
+ case OTG_STATE_A_VBUS_ERR:
+ dev_err(otgd->dev, "%s: drd: invalid usb-drd state: %s\n",
+ __func__, usb_otg_state_string(state));
+ drd_set_state(fsm, OTG_STATE_UNDEFINED);
+ break;
+ }
+
+ mutex_unlock(&fsm->lock);
+ dev_dbg(otgd->dev, "drd: quit statemachine, changed %d\n",
+ fsm->state_changed);
+
+ return fsm->state_changed;
+}
+
+/**
+ * OTG FSM/DRD work function
*/
static void usb_otg_work(struct work_struct *work)
{
struct otg_data *otgd = container_of(work, struct otg_data, work);
- if (otg_statemachine(&otgd->fsm)) {
- /* state changed. any action ? */
+ /* OTG state machine */
+ if (!otgd->drd_only) {
+ otg_statemachine(&otgd->fsm);
+ return;
}
+
+ /* DRD state machine */
+ drd_statemachine(&otgd->fsm);
}
/**
* usb_otg_register() - Register the OTG device to OTG core
* @parent_device: parent device of Host & Gadget controllers.
* @otg_fsm_ops: otg state machine ops.
+ * @drd_only: dual-role only. no OTG features.
*
* Register parent device that contains both HCD and GCD into
* the USB OTG core. HCD and GCD will be prevented from starting
@@ -275,7 +426,8 @@ static void usb_otg_work(struct work_struct *work)
* Return: struct otg_fsm * if success, NULL if error.
*/
struct otg_fsm *usb_otg_register(struct device *parent_dev,
- struct otg_fsm_ops *fsm_ops)
+ struct otg_fsm_ops *fsm_ops,
+ bool drd_only)
{
struct otg_data *otgd;
int ret = 0;
@@ -309,7 +461,15 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
goto err_wq;
}
- usb_otg_init_timers(otgd);
+ otgd->drd_only = drd_only;
+ /* For DRD mode we don't need OTG timers */
+ if (!drd_only) {
+ usb_otg_init_timers(otgd);
+
+ /* FIXME: we ignore caller's timer ops */
+ otgd->fsm_ops.add_timer = usb_otg_add_timer;
+ otgd->fsm_ops.del_timer = usb_otg_del_timer;
+ }
/* save original start host/gadget ops */
otgd->start_host = fsm_ops->start_host;
@@ -319,9 +479,6 @@ struct otg_fsm *usb_otg_register(struct device *parent_dev,
/* override ops */
otgd->fsm_ops.start_host = usb_otg_start_host;
otgd->fsm_ops.start_gadget = usb_otg_start_gadget;
- /* FIXME: we ignore caller's timer ops */
- otgd->fsm_ops.add_timer = usb_otg_add_timer;
- otgd->fsm_ops.del_timer = usb_otg_del_timer;
/* set otg ops */
otgd->fsm.ops = &otgd->fsm_ops;
otgd->fsm.otg = &otgd->otg;
@@ -424,8 +581,10 @@ static void usb_otg_stop_fsm(struct otg_fsm *fsm)
otgd->fsm_running = false;
/* Stop state machine / timers */
- for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
- hrtimer_cancel(&otgd->timers[i].timer);
+ if (!otgd->drd_only) {
+ for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
+ hrtimer_cancel(&otgd->timers[i].timer);
+ }
flush_workqueue(otgd->wq);
fsm->otg->state = OTG_STATE_UNDEFINED;
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index 73136aa..44666aa 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -45,6 +45,11 @@ enum otg_fsm_timer {
/**
* struct otg_fsm - OTG state machine according to the OTG spec
*
+ * DRD mode hardware Inputs
+ *
+ * @id: TRUE for B-device, FALSE for A-device.
+ * @vbus: VBUS voltage in regulation.
+ *
* OTG hardware Inputs
*
* Common inputs for A and B device
@@ -110,7 +115,8 @@ enum otg_fsm_timer {
*/
struct otg_fsm {
/* Input */
- int id;
+ int id; /* DRD + OTG */
+ int vbus; /* DRD only */
int adp_change;
int power_up;
int a_srp_det;
diff --git a/include/linux/usb/usb-otg.h b/include/linux/usb/usb-otg.h
index 8987cd1..cc06a94 100644
--- a/include/linux/usb/usb-otg.h
+++ b/include/linux/usb/usb-otg.h
@@ -25,7 +25,7 @@
#if IS_ENABLED(CONFIG_USB_OTG_CORE)
struct otg_fsm *usb_otg_register(struct device *parent_dev,
- struct otg_fsm_ops *fsm_ops);
+ struct otg_fsm_ops *fsm_ops, bool drd_only);
int usb_otg_unregister(struct device *parent_dev);
int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
unsigned long irqflags);
@@ -40,7 +40,8 @@ struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
#else /* CONFIG_USB_OTG_CORE */
static inline struct otg_fsm *usb_otg_register(struct device *parent_dev,
- struct otg_fsm_ops *fsm_ops)
+ struct otg_fsm_ops *fsm_ops,
+ bool drd_only)
{
return ERR_PTR(-ENOSYS);
}
--
2.1.0
next prev parent reply other threads:[~2015-04-14 10:44 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-14 10:41 [RFC][PATCH v2 00/13] USB: OTG/DRD Core functionality Roger Quadros
2015-04-14 10:41 ` [RFC][PATCH v2 01/13] usb: otg-fsm: Add documentation for struct otg_fsm Roger Quadros
2015-04-16 11:32 ` Peter Chen
2015-04-17 8:45 ` Roger Quadros
2015-04-14 10:41 ` [RFC][PATCH v2 02/13] usb: otg-fsm: support multiple instances Roger Quadros
2015-04-16 11:36 ` Peter Chen
2015-04-16 11:58 ` Roger Quadros
2015-04-16 12:06 ` Peter Chen
2015-04-14 10:41 ` [RFC][PATCH v2 03/13] usb: otg-fsm: Prevent build warning "VDBG" redefined Roger Quadros
2015-04-16 11:41 ` Peter Chen
2015-04-16 11:59 ` Roger Quadros
2015-04-16 12:07 ` Peter Chen
2015-04-14 10:41 ` [RFC][PATCH v2 04/13] usb: gadget: add usb_gadget_start/stop() Roger Quadros
2015-04-16 11:48 ` Peter Chen
2015-04-16 12:07 ` Roger Quadros
2015-04-16 12:12 ` Peter Chen
2015-04-14 10:41 ` [RFC][PATCH v2 05/13] usb: otg: add OTG core Roger Quadros
2015-04-15 9:29 ` Paul Bolle
2015-04-15 13:24 ` Roger Quadros
2015-04-16 12:02 ` Peter Chen
2015-04-16 13:04 ` Roger Quadros
2015-04-14 10:41 ` [RFC][PATCH v2 06/13] usb: hcd: Add hcd add/remove functions for OTG use Roger Quadros
2015-04-17 2:18 ` Peter Chen
2015-04-17 7:21 ` Roger Quadros
2015-04-17 14:03 ` Alan Stern
2015-04-20 7:00 ` Roger Quadros
2015-04-20 13:56 ` Alan Stern
2015-04-21 7:02 ` Roger Quadros
2015-04-14 10:41 ` Roger Quadros [this message]
2015-04-14 10:41 ` [RFC][PATCH v2 08/13] usb: otg: hub: Notify OTG fsm when A device sets b_hnp_enable Roger Quadros
2015-04-17 2:28 ` Peter Chen
2015-04-17 7:32 ` Roger Quadros
2015-04-14 10:41 ` [RFC][PATCH v2 09/13] usb: gadget: udc: adapt to OTG Roger Quadros
2015-04-14 10:41 ` [RFC][PATCH v2 10/13] udc-core: fix lock circular dependency on udc_lock Roger Quadros
2015-04-14 10:41 ` [RFC][PATCH v2 11/13] usb: add "dual-role" mode to dr_mode device tree helper Roger Quadros
2015-04-14 10:41 ` [RFC][PATCH v2 12/13] usb: dwc3: add dual-role support Roger Quadros
2015-04-14 10:42 ` [RFC][PATCH v2 13/13] ARM: dts: dra7x-evm: Enable dual-role for usb1 Roger Quadros
2015-04-20 3:05 ` [RFC][PATCH v2 00/13] USB: OTG/DRD Core functionality Peter Chen
2015-04-20 7:28 ` Roger Quadros
2015-04-21 6:04 ` Peter Chen
2015-04-21 7:34 ` Roger Quadros
2015-04-22 2:17 ` Peter Chen
2015-04-22 7:33 ` Roger Quadros
2015-04-22 9:22 ` Peter Chen
2015-04-22 12:42 ` Roger Quadros
2015-04-23 1:52 ` Peter Chen
2015-04-23 6:35 ` Roger Quadros
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1429008120-5395-8-git-send-email-rogerq@ti.com \
--to=rogerq@ti.com \
--cc=balbi@ti.com \
--cc=dan.j.williams@intel.com \
--cc=gregkh@linuxfoundation.org \
--cc=jun.li@freescale.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-omap@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=mathias.nyman@linux.intel.com \
--cc=peter.chen@freescale.com \
--cc=stern@rowland.harvard.edu \
--cc=tony@atomide.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®