From: Roger Quadros <rogerq@ti.com>
To: <stern@rowland.harvard.edu>, <balbi@ti.com>,
<gregkh@linuxfoundation.org>, <peter.chen@freescale.com>
Cc: <dan.j.williams@intel.com>, <jun.li@freescale.com>,
<mathias.nyman@linux.intel.com>, <tony@atomide.com>,
<Joao.Pinto@synopsys.com>, <linux-usb@vger.kernel.org>,
<linux-kernel@vger.kernel.org>, <linux-omap@vger.kernel.org>,
Roger Quadros <rogerq@ti.com>
Subject: [PATCH v3 10/11] usb: otg: Add dual-role device (DRD) support
Date: Wed, 8 Jul 2015 13:19:36 +0300 [thread overview]
Message-ID: <1436350777-28056-11-git-send-email-rogerq@ti.com> (raw)
In-Reply-To: <1436350777-28056-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/otg.h | 5 +-
3 files changed, 180 insertions(+), 12 deletions(-)
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
index 1f19001..9b89f4b 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)
@@ -272,20 +273,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, "otg: 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, "otg: 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: otg: 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: otg: 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, "otg: 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);
- otg_statemachine(&otgd->fsm);
+ /* 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
@@ -294,7 +447,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;
@@ -328,7 +482,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;
@@ -338,9 +500,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;
@@ -443,8 +602,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 22d8baa..ae9c30a 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -48,6 +48,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
@@ -122,7 +127,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/otg.h b/include/linux/usb/otg.h
index ce6f8d8..1086a0b 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -58,7 +58,7 @@ enum usb_dr_mode {
#if IS_ENABLED(CONFIG_USB_OTG)
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, struct otg_hcd_ops *ops);
@@ -73,7 +73,8 @@ struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
#else /* CONFIG_USB_OTG */
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.4
next prev parent reply other threads:[~2015-07-08 10:21 UTC|newest]
Thread overview: 55+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-07-08 10:19 [PATCH v3 00/11] USB: OTG/DRD Core functionality Roger Quadros
2015-07-08 10:19 ` [PATCH v3 01/11] usb: otg-fsm: Add documentation for struct otg_fsm Roger Quadros
2015-07-13 1:21 ` Peter Chen
2015-07-13 10:06 ` Roger Quadros
2015-07-08 10:19 ` [PATCH v3 02/11] usb: otg-fsm: support multiple instances Roger Quadros
2015-07-10 8:06 ` Li Jun
2015-07-13 1:39 ` Peter Chen
2015-07-17 11:17 ` Roger Quadros
2015-07-08 10:19 ` [PATCH v3 03/11] usb: otg-fsm: Prevent build warning "VDBG" redefined Roger Quadros
2015-07-13 1:52 ` Peter Chen
2015-07-08 10:19 ` [PATCH v3 04/11] otg-fsm: move usb_bus_start_enum into otg-fsm->ops Roger Quadros
2015-07-13 1:58 ` Peter Chen
2015-07-13 10:13 ` Roger Quadros
2015-07-14 0:34 ` Peter Chen
2015-07-15 13:30 ` Roger Quadros
2015-07-16 0:54 ` Peter Chen
2015-07-16 10:40 ` Roger Quadros
2015-07-17 0:28 ` Peter Chen
2015-07-08 10:19 ` [PATCH v3 05/11] usb: hcd.h: Add OTG to HCD interface Roger Quadros
2015-07-13 2:00 ` Peter Chen
2015-07-17 0:30 ` Peter Chen
2015-07-08 10:19 ` [PATCH v3 06/11] usb: gadget.h: Add OTG to gadget interface Roger Quadros
2015-07-17 0:31 ` Peter Chen
2015-07-08 10:19 ` [PATCH v3 07/11] usb: otg: add OTG core Roger Quadros
2015-07-13 2:14 ` Peter Chen
2015-07-13 10:20 ` Roger Quadros
2015-08-14 9:42 ` Roger Quadros
2015-08-14 8:43 ` Peter Chen
2015-07-17 7:48 ` Li Jun
2015-07-17 12:06 ` Roger Quadros
2015-07-20 1:45 ` Peter Chen
2015-07-21 10:52 ` Li Jun
2015-07-27 10:03 ` Roger Quadros
2015-07-08 10:19 ` [PATCH v3 08/11] usb: hcd: Adapt to " Roger Quadros
2015-07-08 10:19 ` [PATCH v3 09/11] usb: gadget: udc: adapt " Roger Quadros
2015-07-08 10:19 ` Roger Quadros [this message]
2015-07-17 8:14 ` [PATCH v3 10/11] usb: otg: Add dual-role device (DRD) support Li Jun
2015-07-17 10:41 ` Roger Quadros
2015-07-17 9:02 ` Li Jun
2015-07-17 10:47 ` Roger Quadros
2015-07-20 1:23 ` Peter Chen
2015-07-27 9:31 ` Roger Quadros
2015-07-08 10:19 ` [PATCH v3 11/11] usb: otg: hub: Notify OTG fsm when A device sets b_hnp_enable Roger Quadros
2015-07-08 10:23 ` [PATCH v3 12/12] usb: chipidea: move from CONFIG_USB_OTG_FSM to CONFIG_USB_OTG Roger Quadros
2015-07-10 7:58 ` [PATCH v3 00/11] USB: OTG/DRD Core functionality Li Jun
2015-07-13 10:24 ` Roger Quadros
2015-07-13 13:04 ` Li Jun
2015-07-15 13:07 ` Roger Quadros
2015-07-13 19:14 ` Andrew Bresticker
2015-07-14 0:59 ` Peter Chen
2015-07-14 18:18 ` Andrew Bresticker
2015-07-15 2:13 ` Peter Chen
2015-07-15 13:26 ` Roger Quadros
2015-07-16 18:29 ` Andrew Bresticker
2015-07-17 10:34 ` 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=1436350777-28056-11-git-send-email-rogerq@ti.com \
--to=rogerq@ti.com \
--cc=Joao.Pinto@synopsys.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
Powered by JetHome